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}