feat: add safe Javis startup script
This commit is contained in:
@@ -4,34 +4,46 @@ Javis ist ein langfristiges, lokal-first ausgerichtetes Projekt für einen pers
|
|||||||
|
|
||||||
## Aktueller Stand
|
## Aktueller Stand
|
||||||
|
|
||||||
Der erste lokale Textchat funktioniert auf Windows mit Ollama und `qwen3:8b`.
|
Der lokale Textchat funktioniert auf Windows mit Ollama und `qwen3:8b`.
|
||||||
Er bietet echte lokale Modellantworten ohne Cloud-Fallback, neue und fortsetzbare
|
Ein optionaler Gemini-Free-Provider ist auf einem Feature-Branch vorbereitet;
|
||||||
Sitzungen sowie SQLite-Persistenz außerhalb des Repositories.
|
Datenschutzrouter, Nullkostenmodus und lokales Fallback sind automatisiert
|
||||||
|
getestet, aber noch nicht von Pascal mit einem echten Key abgenommen.
|
||||||
|
|
||||||
Noch nicht implementiert sind Obsidian-Zugriff, Serverzugriff, Sprache, Mobile
|
Noch nicht implementiert sind Obsidian-Zugriff, Serverzugriff, Sprache, Mobile
|
||||||
Client und Living Mind.
|
Client und Living Mind.
|
||||||
|
|
||||||
## Lokaler Start
|
## Einfacher Start
|
||||||
|
|
||||||
Ollama läuft in einem eigenen Terminal ausschließlich auf localhost:
|
Aus dem Repository:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
$env:OLLAMA_MODELS = 'D:\Javis-Data\ollama-models'
|
powershell -ExecutionPolicy Bypass -File .\scripts\start-javis.ps1
|
||||||
$env:OLLAMA_HOST = '127.0.0.1:11434'
|
|
||||||
& 'D:\Javis-Tools\ollama\ollama.exe' serve
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Der Chat startet aus dem Repository in einem zweiten Terminal:
|
Das Skript findet die portablen Werkzeuge standardmäßig relativ zum Laufwerk des
|
||||||
|
Repositories, setzt Variablen nur für seinen Prozess, prüft Ollama ausschließlich
|
||||||
|
auf `127.0.0.1:11434`, startet es bei Bedarf versteckt und öffnet den Chat. Es
|
||||||
|
ändert weder PATH, Registry, Autostart, Windows-Dienste noch die systemweite
|
||||||
|
PowerShell-Ausführungsrichtlinie.
|
||||||
|
|
||||||
```powershell
|
Abweichende Pfade sind über die Skriptparameter `-UvPath`, `-OllamaPath`,
|
||||||
$env:UV_CACHE_DIR = 'D:\Javis-Tools\uv-cache'
|
`-OllamaModels`, `-DataDir`, `-UvCacheDir` und `-PythonInstallDir` oder die
|
||||||
$env:JAVIS_DATA_DIR = 'D:\Javis-Data\runtime'
|
entsprechenden `JAVIS_*`-Umgebungsvariablen möglich.
|
||||||
& 'D:\Javis-Tools\uv\uv.exe' run javis chat
|
|
||||||
```
|
|
||||||
|
|
||||||
Im Chat zeigt `/help` alle Befehle. Ohne `JAVIS_DATA_DIR` nutzt Javis einen
|
Im Chat zeigt `/help` alle Befehle. Ohne `JAVIS_DATA_DIR` nutzt Javis einen
|
||||||
sicheren benutzerspezifischen Plattformstandard.
|
sicheren benutzerspezifischen Plattformstandard.
|
||||||
|
|
||||||
|
Gemini bleibt standardmäßig aus. Die spätere lokale Einrichtung erfolgt ohne
|
||||||
|
Key in der Konfigurationsdatei:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
& 'D:\Javis-Tools\uv\uv.exe' run javis configure gemini
|
||||||
|
& 'D:\Javis-Tools\uv\uv.exe' run javis secrets set gemini
|
||||||
|
```
|
||||||
|
|
||||||
|
Der Key wird bei der zweiten Zeile verdeckt eingegeben und im
|
||||||
|
Betriebssystem-Keyring gespeichert, niemals in Git oder TOML.
|
||||||
|
|
||||||
## Grundregeln
|
## Grundregeln
|
||||||
|
|
||||||
- Plattformunabhängiger modularer Monolith, voraussichtlich in Python.
|
- Plattformunabhängiger modularer Monolith, voraussichtlich in Python.
|
||||||
|
|||||||
+11
-5
@@ -95,6 +95,9 @@ Rootserver:
|
|||||||
- Der echte CLI-Chat verwendet nun `hybrid/auto`; der Key wird erst vor einem
|
- Der echte CLI-Chat verwendet nun `hybrid/auto`; der Key wird erst vor einem
|
||||||
zulässigen Cloudaufruf gelesen. `/provider`, `/privacy` und `/status` zeigen
|
zulässigen Cloudaufruf gelesen. `/provider`, `/privacy` und `/status` zeigen
|
||||||
ausschließlich bereinigte Modi, Entscheidungen und Zähler.
|
ausschließlich bereinigte Modi, Entscheidungen und Zähler.
|
||||||
|
- `scripts/start-javis.ps1` verifiziert das Repository, setzt nur
|
||||||
|
Prozessvariablen, prüft Ollama auf Loopback, startet es bei Bedarf versteckt
|
||||||
|
und wartet begrenzt. Pfade sind per Parameter oder `JAVIS_*` überschreibbar.
|
||||||
|
|
||||||
## Aktuelle Architektur
|
## Aktuelle Architektur
|
||||||
|
|
||||||
@@ -151,6 +154,8 @@ Rootserver:
|
|||||||
- Hybridprovider in die CLI verdrahtet, sicheren Aktivierungsdialog und
|
- Hybridprovider in die CLI verdrahtet, sicheren Aktivierungsdialog und
|
||||||
bereinigte Provider-/Datenschutz-/Statusbefehle ergänzt.
|
bereinigte Provider-/Datenschutz-/Statusbefehle ergänzt.
|
||||||
- Lesender Ollama-Status prüft nur Loopback-Erreichbarkeit und Modellliste.
|
- Lesender Ollama-Status prüft nur Loopback-Erreichbarkeit und Modellliste.
|
||||||
|
- Sicheres PowerShell-Startskript und kompakte Startdokumentation ergänzt; keine
|
||||||
|
PATH-, Registry-, Autostart-, Dienst- oder Richtlinienänderung.
|
||||||
|
|
||||||
## Aktuelle Tests
|
## Aktuelle Tests
|
||||||
|
|
||||||
@@ -165,8 +170,8 @@ Letzter bestätigter Projektstand:
|
|||||||
- uv-Lock und `uv sync --dev`: bestanden
|
- uv-Lock und `uv sync --dev`: bestanden
|
||||||
- Python in `.venv`: 3.12.13
|
- Python in `.venv`: 3.12.13
|
||||||
- Ruff in `.venv`: 0.16.0
|
- Ruff in `.venv`: 0.16.0
|
||||||
- Unit-Tests: 59 bestanden; CLI-Befehle, doppelte Bestätigung, verzögertes
|
- Unit-Tests: 60 bestanden; Startskript-Sicherheitsregeln zusätzlich abgedeckt
|
||||||
Key-Laden und Ollama-Status sind abgedeckt
|
- PowerShell-Syntax des Startskripts: erfolgreich geparst
|
||||||
- Ruff Lint: bestanden
|
- Ruff Lint: bestanden
|
||||||
- Ruff Formatprüfung: bestanden
|
- Ruff Formatprüfung: bestanden
|
||||||
|
|
||||||
@@ -252,9 +257,10 @@ Abnahmestatus:
|
|||||||
|
|
||||||
Auf dem bestehenden Feature-Branch als nächstes einzelnes Arbeitspaket:
|
Auf dem bestehenden Feature-Branch als nächstes einzelnes Arbeitspaket:
|
||||||
|
|
||||||
1. sicheres `scripts/start-javis.ps1` ohne Systemänderungen ergänzen
|
1. geforderte Resttests ergänzen, insbesondere Timeout und bereinigte Fehler
|
||||||
2. Start- und Einrichtungsablauf kompakt dokumentieren und automatisiert prüfen
|
2. vollständige Qualitäts-, Gitignore-, Secret- und Außenbereichsprüfung ausführen
|
||||||
3. noch keine echte Cloudanfrage ausführen und noch nicht nach `main` mergen
|
3. Abschlussdokumentation aktualisieren, Feature-Branch pushen und vor Pascals
|
||||||
|
manuellem Live-Test stoppen; nicht nach `main` mergen
|
||||||
|
|
||||||
Keinen API-Key in Chat, Git, Obsidian, Logs oder Dokumentation übernehmen.
|
Keinen API-Key in Chat, Git, Obsidian, Logs oder Dokumentation übernehmen.
|
||||||
|
|
||||||
|
|||||||
+18
-10
@@ -35,19 +35,27 @@ $env:UV_CACHE_DIR = 'D:\Javis-Tools\uv-cache'
|
|||||||
& 'D:\Javis-Tools\uv\uv.exe' run ruff format --check .
|
& 'D:\Javis-Tools\uv\uv.exe' run ruff format --check .
|
||||||
```
|
```
|
||||||
|
|
||||||
Vor dem Chat wird Ollama in einem eigenen Terminal gestartet:
|
## Komfortabler Start
|
||||||
|
|
||||||
|
Der normale Start aus dem Repository benötigt nur:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
$env:OLLAMA_MODELS = 'D:\Javis-Data\ollama-models'
|
powershell -ExecutionPolicy Bypass -File .\scripts\start-javis.ps1
|
||||||
$env:OLLAMA_HOST = '127.0.0.1:11434'
|
|
||||||
& 'D:\Javis-Tools\ollama\ollama.exe' serve
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Danach:
|
`-ExecutionPolicy Bypass` gilt nur für diesen Prozess; das Skript ändert keine
|
||||||
|
systemweite Richtlinie. Es prüft Ollama auf Loopback, startet bei Bedarf die
|
||||||
|
portable Laufzeit versteckt und wartet höchstens 15 Sekunden. Werkzeug- und
|
||||||
|
Datenpfade können mit Parametern oder folgenden Umgebungsvariablen überschrieben
|
||||||
|
werden:
|
||||||
|
|
||||||
```powershell
|
- `JAVIS_UV_PATH`
|
||||||
$env:JAVIS_DATA_DIR = 'D:\Javis-Data\runtime'
|
- `JAVIS_OLLAMA_PATH`
|
||||||
& 'D:\Javis-Tools\uv\uv.exe' run javis chat
|
- `JAVIS_OLLAMA_MODELS`
|
||||||
```
|
- `JAVIS_DATA_DIR`
|
||||||
|
- `JAVIS_UV_CACHE_DIR`
|
||||||
|
- `JAVIS_PYTHON_INSTALL_DIR`
|
||||||
|
|
||||||
Die absoluten Pfade sind ausschließlich lokale Entwicklungsdokumentation. Der Anwendungscode darf keine festen Windows-Pfade enthalten.
|
Die Standardpfade werden vom Laufwerk des Repositorys abgeleitet; persönliche
|
||||||
|
Windows-Benutzerpfade sind nicht eingebrannt. PATH, Registry, Autostart und
|
||||||
|
Windows-Dienste bleiben unverändert.
|
||||||
|
|||||||
@@ -58,7 +58,9 @@ Stand: lokaler Textchat auf `main`, 30.07.2026.
|
|||||||
- `javis configure gemini` mit doppelter ausdrücklicher Bestätigung
|
- `javis configure gemini` mit doppelter ausdrücklicher Bestätigung
|
||||||
- echter Chat auf Hybridprovider verdrahtet; Key wird verzögert geladen
|
- echter Chat auf Hybridprovider verdrahtet; Key wird verzögert geladen
|
||||||
- `/provider`, `/privacy` und `/status` geben keine Inhalte oder Keywerte aus
|
- `/provider`, `/privacy` und `/status` geben keine Inhalte oder Keywerte aus
|
||||||
- 59 Unit-Tests aktuell erfolgreich; Ruff-Format und Lint sauber
|
- sicheres `scripts/start-javis.ps1` mit relativer Repositoryprüfung,
|
||||||
|
Loopback-Ollama und überschreibbaren externen Pfaden
|
||||||
|
- 60 Unit-Tests aktuell erfolgreich; PowerShell-Syntax, Ruff-Format und Lint sauber
|
||||||
|
|
||||||
## Nicht implementiert
|
## Nicht implementiert
|
||||||
|
|
||||||
@@ -83,6 +85,6 @@ Living-Mind-Frontend und automatische Ollama-Prozessverwaltung.
|
|||||||
Ollama wird bewusst manuell in einem eigenen Terminal gestartet. Antworten werden
|
Ollama wird bewusst manuell in einem eigenen Terminal gestartet. Antworten werden
|
||||||
noch nicht gestreamt; Sitzungen haben noch keine Titel, Suche oder Löschfunktion.
|
noch nicht gestreamt; Sitzungen haben noch keine Titel, Suche oder Löschfunktion.
|
||||||
`qwen3:8b` ist nicht automatisch für die GTX 1050 des späteren Zielhosts geeignet.
|
`qwen3:8b` ist nicht automatisch für die GTX 1050 des späteren Zielhosts geeignet.
|
||||||
Das sichere Startskript und die abschließende Bedienungsdokumentation fehlen noch.
|
Abschließende Resttests, Qualitätsprüfung und Feature-Branch-Push fehlen noch.
|
||||||
Es wurde kein echter API-Key angefordert, angezeigt oder konfiguriert und keine
|
Es wurde kein echter API-Key angefordert, angezeigt oder konfiguriert und keine
|
||||||
Cloudanfrage ausgeführt.
|
Cloudanfrage ausgeführt.
|
||||||
|
|||||||
@@ -0,0 +1,121 @@
|
|||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[string]$UvPath,
|
||||||
|
[string]$OllamaPath,
|
||||||
|
[string]$OllamaModels,
|
||||||
|
[string]$DataDir,
|
||||||
|
[string]$UvCacheDir,
|
||||||
|
[string]$PythonInstallDir
|
||||||
|
)
|
||||||
|
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
|
||||||
|
function Get-ConfiguredPath {
|
||||||
|
param(
|
||||||
|
[string]$ParameterValue,
|
||||||
|
[string]$EnvironmentName,
|
||||||
|
[string]$DefaultValue
|
||||||
|
)
|
||||||
|
|
||||||
|
if (-not [string]::IsNullOrWhiteSpace($ParameterValue)) {
|
||||||
|
return [System.IO.Path]::GetFullPath($ParameterValue)
|
||||||
|
}
|
||||||
|
$environmentValue = [System.Environment]::GetEnvironmentVariable($EnvironmentName)
|
||||||
|
if (-not [string]::IsNullOrWhiteSpace($environmentValue)) {
|
||||||
|
return [System.IO.Path]::GetFullPath($environmentValue)
|
||||||
|
}
|
||||||
|
return [System.IO.Path]::GetFullPath($DefaultValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Test-OllamaReady {
|
||||||
|
try {
|
||||||
|
$null = Invoke-RestMethod `
|
||||||
|
-Uri 'http://127.0.0.1:11434/api/version' `
|
||||||
|
-Method Get `
|
||||||
|
-TimeoutSec 2
|
||||||
|
return $true
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
return $false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$repositoryRoot = [System.IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..'))
|
||||||
|
if (-not (Test-Path -LiteralPath (Join-Path $repositoryRoot 'pyproject.toml') -PathType Leaf)) {
|
||||||
|
throw "Das Repository konnte relativ zum Startskript nicht verifiziert werden."
|
||||||
|
}
|
||||||
|
|
||||||
|
$driveRoot = [System.IO.Path]::GetPathRoot($repositoryRoot)
|
||||||
|
$resolvedUv = Get-ConfiguredPath `
|
||||||
|
-ParameterValue $UvPath `
|
||||||
|
-EnvironmentName 'JAVIS_UV_PATH' `
|
||||||
|
-DefaultValue (Join-Path $driveRoot 'Javis-Tools\uv\uv.exe')
|
||||||
|
$resolvedOllama = Get-ConfiguredPath `
|
||||||
|
-ParameterValue $OllamaPath `
|
||||||
|
-EnvironmentName 'JAVIS_OLLAMA_PATH' `
|
||||||
|
-DefaultValue (Join-Path $driveRoot 'Javis-Tools\ollama\ollama.exe')
|
||||||
|
$resolvedModels = Get-ConfiguredPath `
|
||||||
|
-ParameterValue $OllamaModels `
|
||||||
|
-EnvironmentName 'JAVIS_OLLAMA_MODELS' `
|
||||||
|
-DefaultValue (Join-Path $driveRoot 'Javis-Data\ollama-models')
|
||||||
|
$resolvedData = Get-ConfiguredPath `
|
||||||
|
-ParameterValue $DataDir `
|
||||||
|
-EnvironmentName 'JAVIS_DATA_DIR' `
|
||||||
|
-DefaultValue (Join-Path $driveRoot 'Javis-Data\runtime')
|
||||||
|
$resolvedCache = Get-ConfiguredPath `
|
||||||
|
-ParameterValue $UvCacheDir `
|
||||||
|
-EnvironmentName 'JAVIS_UV_CACHE_DIR' `
|
||||||
|
-DefaultValue (Join-Path $driveRoot 'Javis-Tools\uv-cache')
|
||||||
|
$resolvedPython = Get-ConfiguredPath `
|
||||||
|
-ParameterValue $PythonInstallDir `
|
||||||
|
-EnvironmentName 'JAVIS_PYTHON_INSTALL_DIR' `
|
||||||
|
-DefaultValue (Join-Path $driveRoot 'Javis-Tools\python')
|
||||||
|
|
||||||
|
if (-not (Test-Path -LiteralPath $resolvedUv -PathType Leaf)) {
|
||||||
|
throw "uv.exe wurde nicht gefunden: $resolvedUv"
|
||||||
|
}
|
||||||
|
if (-not (Test-Path -LiteralPath $resolvedModels -PathType Container)) {
|
||||||
|
throw "Der externe Ollama-Modellpfad wurde nicht gefunden: $resolvedModels"
|
||||||
|
}
|
||||||
|
|
||||||
|
$env:JAVIS_DATA_DIR = $resolvedData
|
||||||
|
$env:OLLAMA_MODELS = $resolvedModels
|
||||||
|
$env:OLLAMA_HOST = '127.0.0.1:11434'
|
||||||
|
$env:UV_CACHE_DIR = $resolvedCache
|
||||||
|
$env:UV_PYTHON_INSTALL_DIR = $resolvedPython
|
||||||
|
|
||||||
|
if (-not (Test-OllamaReady)) {
|
||||||
|
if (-not (Test-Path -LiteralPath $resolvedOllama -PathType Leaf)) {
|
||||||
|
throw "Ollama ist nicht erreichbar und ollama.exe wurde nicht gefunden: $resolvedOllama"
|
||||||
|
}
|
||||||
|
Write-Host "Ollama wird lokal auf 127.0.0.1:11434 gestartet ..."
|
||||||
|
$ollamaProcess = Start-Process `
|
||||||
|
-FilePath $resolvedOllama `
|
||||||
|
-ArgumentList 'serve' `
|
||||||
|
-WindowStyle Hidden `
|
||||||
|
-PassThru
|
||||||
|
|
||||||
|
$ready = $false
|
||||||
|
for ($attempt = 1; $attempt -le 30; $attempt++) {
|
||||||
|
if ($ollamaProcess.HasExited) {
|
||||||
|
throw "Ollama wurde beendet, bevor die lokale API bereit war."
|
||||||
|
}
|
||||||
|
Start-Sleep -Milliseconds 500
|
||||||
|
if (Test-OllamaReady) {
|
||||||
|
$ready = $true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (-not $ready) {
|
||||||
|
throw "Ollama war nach 15 Sekunden nicht unter 127.0.0.1:11434 erreichbar."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Push-Location $repositoryRoot
|
||||||
|
try {
|
||||||
|
& $resolvedUv run javis chat
|
||||||
|
exit $LASTEXITCODE
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
Pop-Location
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import unittest
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
class StartScriptTests(unittest.TestCase):
|
||||||
|
def test_script_is_local_overridable_and_avoids_system_changes(self) -> None:
|
||||||
|
repository_root = Path(__file__).resolve().parents[2]
|
||||||
|
script = (repository_root / "scripts" / "start-javis.ps1").read_text(encoding="utf-8")
|
||||||
|
normalized = script.lower()
|
||||||
|
|
||||||
|
self.assertIn("$psscriptroot", normalized)
|
||||||
|
self.assertIn("http://127.0.0.1:11434/api/version", normalized)
|
||||||
|
self.assertIn("-windowstyle hidden", normalized)
|
||||||
|
self.assertIn("javis_uv_path", normalized)
|
||||||
|
self.assertIn("javis_ollama_path", normalized)
|
||||||
|
self.assertIn("javis_data_dir", normalized)
|
||||||
|
self.assertIn("run javis chat", normalized)
|
||||||
|
self.assertNotIn("c:\\users\\", normalized)
|
||||||
|
for forbidden in (
|
||||||
|
"set-executionpolicy",
|
||||||
|
"setx ",
|
||||||
|
"new-service",
|
||||||
|
"schtasks",
|
||||||
|
"hkey_",
|
||||||
|
):
|
||||||
|
self.assertNotIn(forbidden, normalized)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
Reference in New Issue
Block a user