app.go

 1package app
 2
 3import (
 4	"context"
 5	"database/sql"
 6	"maps"
 7	"sync"
 8	"time"
 9
10	"github.com/kujtimiihoxha/termai/internal/db"
11	"github.com/kujtimiihoxha/termai/internal/history"
12	"github.com/kujtimiihoxha/termai/internal/llm/agent"
13	"github.com/kujtimiihoxha/termai/internal/logging"
14	"github.com/kujtimiihoxha/termai/internal/lsp"
15	"github.com/kujtimiihoxha/termai/internal/message"
16	"github.com/kujtimiihoxha/termai/internal/permission"
17	"github.com/kujtimiihoxha/termai/internal/session"
18)
19
20type App struct {
21	Sessions    session.Service
22	Messages    message.Service
23	Files       history.Service
24	Permissions permission.Service
25
26	CoderAgent agent.Service
27
28	LSPClients map[string]*lsp.Client
29
30	clientsMutex sync.RWMutex
31
32	watcherCancelFuncs []context.CancelFunc
33	cancelFuncsMutex   sync.Mutex
34	watcherWG          sync.WaitGroup
35}
36
37func New(ctx context.Context, conn *sql.DB) (*App, error) {
38	q := db.New(conn)
39	sessions := session.NewService(q)
40	messages := message.NewService(q)
41	files := history.NewService(q)
42
43	app := &App{
44		Sessions:    sessions,
45		Messages:    messages,
46		Files:       files,
47		Permissions: permission.NewPermissionService(),
48		LSPClients:  make(map[string]*lsp.Client),
49	}
50
51	var err error
52	app.CoderAgent, err = agent.NewCoderAgent(
53
54		app.Permissions,
55		app.Sessions,
56		app.Messages,
57		app.LSPClients,
58	)
59	if err != nil {
60		logging.Error("Failed to create coder agent", err)
61		return nil, err
62	}
63
64	app.initLSPClients(ctx)
65
66	return app, nil
67}
68
69// Shutdown performs a clean shutdown of the application
70func (app *App) Shutdown() {
71	// Cancel all watcher goroutines
72	app.cancelFuncsMutex.Lock()
73	for _, cancel := range app.watcherCancelFuncs {
74		cancel()
75	}
76	app.cancelFuncsMutex.Unlock()
77	app.watcherWG.Wait()
78
79	// Perform additional cleanup for LSP clients
80	app.clientsMutex.RLock()
81	clients := make(map[string]*lsp.Client, len(app.LSPClients))
82	maps.Copy(clients, app.LSPClients)
83	app.clientsMutex.RUnlock()
84
85	for name, client := range clients {
86		shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
87		if err := client.Shutdown(shutdownCtx); err != nil {
88			logging.Error("Failed to shutdown LSP client", "name", name, "error", err)
89		}
90		cancel()
91	}
92}