Skip to content

Commit cb5dc8c

Browse files
committed
Cleanup, focus on systray
Signed-off-by: Ettore Di Giacinto <[email protected]>
1 parent 6400c05 commit cb5dc8c

File tree

6 files changed

+341
-155
lines changed

6 files changed

+341
-155
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ build-launcher: ## Build the launcher application
9999
$(info ${GREEN}I GO_TAGS: ${YELLOW}$(GO_TAGS)${RESET})
100100
$(info ${GREEN}I LD_FLAGS: ${YELLOW}$(LD_FLAGS)${RESET})
101101
rm -rf $(LAUNCHER_BINARY_NAME) || true
102-
CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GOCMD) build -ldflags "$(LD_FLAGS)" -tags "$(GO_TAGS)" -o $(LAUNCHER_BINARY_NAME) ./
102+
CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GOCMD) build -ldflags "$(LD_FLAGS)" -tags "$(GO_TAGS)" -o $(LAUNCHER_BINARY_NAME) ./cli/launcher
103103

104104
build-all: build build-launcher ## Build both server and launcher
105105

cli/launcher/launcher.go

Lines changed: 101 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@ import (
55
"context"
66
"fmt"
77
"io"
8+
"log"
89
"os"
910
"os/exec"
1011
"path/filepath"
1112
"strings"
1213
"sync"
14+
"syscall"
1315
"time"
1416

1517
"fyne.io/fyne/v2"
@@ -21,6 +23,7 @@ type Config struct {
2123
BackendsPath string `json:"backends_path"`
2224
Address string `json:"address"`
2325
AutoStart bool `json:"auto_start"`
26+
StartOnBoot bool `json:"start_on_boot"`
2427
LogLevel string `json:"log_level"`
2528
EnvironmentVars map[string]string `json:"environment_vars"`
2629
}
@@ -42,6 +45,10 @@ type Launcher struct {
4245
logMutex sync.RWMutex
4346
statusChannel chan string
4447

48+
// Logging
49+
logFile *os.File
50+
logPath string
51+
4552
// UI state
4653
lastUpdateCheck time.Time
4754
}
@@ -57,8 +64,35 @@ func NewLauncher() *Launcher {
5764
}
5865
}
5966

67+
// setupLogging sets up log file for LocalAI process output
68+
func (l *Launcher) setupLogging() error {
69+
// Create logs directory in data folder
70+
dataPath := l.GetDataPath()
71+
logsDir := filepath.Join(dataPath, "logs")
72+
if err := os.MkdirAll(logsDir, 0755); err != nil {
73+
return fmt.Errorf("failed to create logs directory: %w", err)
74+
}
75+
76+
// Create log file with timestamp
77+
timestamp := time.Now().Format("2006-01-02_15-04-05")
78+
l.logPath = filepath.Join(logsDir, fmt.Sprintf("localai_%s.log", timestamp))
79+
80+
logFile, err := os.Create(l.logPath)
81+
if err != nil {
82+
return fmt.Errorf("failed to create log file: %w", err)
83+
}
84+
85+
l.logFile = logFile
86+
return nil
87+
}
88+
6089
// Initialize sets up the launcher
6190
func (l *Launcher) Initialize() error {
91+
// Setup logging
92+
if err := l.setupLogging(); err != nil {
93+
return fmt.Errorf("failed to setup logging: %w", err)
94+
}
95+
6296
// Load configuration
6397
if err := l.loadConfig(); err != nil {
6498
return fmt.Errorf("failed to load config: %w", err)
@@ -98,7 +132,12 @@ func (l *Launcher) Initialize() error {
98132
time.Sleep(1 * time.Second) // Wait for UI to be ready
99133
available, version, err := l.CheckForUpdates()
100134
if err == nil && available {
101-
l.ui.NotifyUpdateAvailable(version)
135+
if l.systray != nil {
136+
l.systray.NotifyUpdateAvailable(version)
137+
}
138+
if l.ui != nil {
139+
l.ui.NotifyUpdateAvailable(version)
140+
}
102141
}
103142
}()
104143
}
@@ -165,8 +204,9 @@ func (l *Launcher) StartLocalAI() error {
165204
go l.monitorLogs(stdout, "STDOUT")
166205
go l.monitorLogs(stderr, "STDERR")
167206

168-
// Monitor process
207+
// Monitor process with startup timeout
169208
go func() {
209+
// Wait for process to start or fail
170210
err := l.localaiCmd.Wait()
171211
l.isRunning = false
172212
l.updateRunningState(false)
@@ -177,6 +217,22 @@ func (l *Launcher) StartLocalAI() error {
177217
}
178218
}()
179219

220+
// Add startup timeout detection
221+
go func() {
222+
time.Sleep(10 * time.Second) // Wait 10 seconds for startup
223+
if l.isRunning {
224+
// Check if process is still alive
225+
if l.localaiCmd.Process != nil {
226+
if err := l.localaiCmd.Process.Signal(syscall.Signal(0)); err != nil {
227+
// Process is dead, mark as not running
228+
l.isRunning = false
229+
l.updateRunningState(false)
230+
l.updateStatus("LocalAI failed to start properly")
231+
}
232+
}
233+
}
234+
}()
235+
180236
return nil
181237
}
182238

@@ -212,6 +268,22 @@ func (l *Launcher) GetLogs() string {
212268
return l.logBuffer.String()
213269
}
214270

271+
// GetRecentLogs returns the most recent logs (last 50 lines) for better error display
272+
func (l *Launcher) GetRecentLogs() string {
273+
l.logMutex.RLock()
274+
defer l.logMutex.RUnlock()
275+
276+
content := l.logBuffer.String()
277+
lines := strings.Split(content, "\n")
278+
279+
// Get last 50 lines
280+
if len(lines) > 50 {
281+
lines = lines[len(lines)-50:]
282+
}
283+
284+
return strings.Join(lines, "\n")
285+
}
286+
215287
// GetConfig returns the current configuration
216288
func (l *Launcher) GetConfig() *Config {
217289
return l.config
@@ -235,6 +307,23 @@ func (l *Launcher) GetWebUIURL() string {
235307
return address
236308
}
237309

310+
// GetDataPath returns the path where LocalAI data and logs are stored
311+
func (l *Launcher) GetDataPath() string {
312+
// LocalAI typically stores data in the current working directory or a models directory
313+
// First check if models path is configured
314+
if l.config != nil && l.config.ModelsPath != "" {
315+
// Return the parent directory of models path
316+
return filepath.Dir(l.config.ModelsPath)
317+
}
318+
319+
// Fallback to home directory LocalAI folder
320+
homeDir, err := os.UserHomeDir()
321+
if err != nil {
322+
return "."
323+
}
324+
return filepath.Join(homeDir, ".localai")
325+
}
326+
238327
// CheckForUpdates checks if there are any available updates
239328
func (l *Launcher) CheckForUpdates() (bool, string, error) {
240329
available, version, err := l.releaseManager.IsUpdateAvailable()
@@ -276,6 +365,13 @@ func (l *Launcher) monitorLogs(reader io.Reader, prefix string) {
276365
}
277366
l.logMutex.Unlock()
278367

368+
// Write to log file if available
369+
if l.logFile != nil {
370+
if _, err := l.logFile.WriteString(logLine); err != nil {
371+
log.Printf("Failed to write to log file: %v", err)
372+
}
373+
}
374+
279375
// Notify UI of new log content
280376
if l.ui != nil {
281377
l.ui.OnLogUpdate(logLine)
@@ -323,6 +419,9 @@ func (l *Launcher) periodicUpdateCheck() {
323419
available, version, err := l.CheckForUpdates()
324420
if err == nil && available {
325421
l.updateStatus(fmt.Sprintf("Update available: %s", version))
422+
if l.systray != nil {
423+
l.systray.NotifyUpdateAvailable(version)
424+
}
326425
if l.ui != nil {
327426
l.ui.NotifyUpdateAvailable(version)
328427
}

cli/launcher/main.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ import (
88
"fyne.io/fyne/v2/driver/desktop"
99
)
1010

11-
func Run() {
12-
// Create the application
13-
myApp := app.New()
11+
func main() {
12+
// Create the application with unique ID
13+
myApp := app.NewWithID("com.localai.launcher")
1414
myApp.SetIcon(resourceIconPng)
1515
myWindow := myApp.NewWindow("LocalAI Launcher")
1616
myWindow.Resize(fyne.NewSize(800, 600))
@@ -54,6 +54,6 @@ func Run() {
5454
}
5555
}()
5656

57-
// Show and run the application
58-
myWindow.ShowAndRun()
57+
// Run the application in background (window only shown when "Settings" is clicked)
58+
myApp.Run()
5959
}

0 commit comments

Comments
 (0)