app.go

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