app.go

  1package app
  2
  3import (
  4	"context"
  5	"database/sql"
  6	"maps"
  7	"sync"
  8	"time"
  9
 10	"github.com/opencode-ai/opencode/internal/config"
 11	"github.com/opencode-ai/opencode/internal/db"
 12	"github.com/opencode-ai/opencode/internal/history"
 13	"github.com/opencode-ai/opencode/internal/llm/agent"
 14	"github.com/opencode-ai/opencode/internal/logging"
 15	"github.com/opencode-ai/opencode/internal/lsp"
 16	"github.com/opencode-ai/opencode/internal/message"
 17	"github.com/opencode-ai/opencode/internal/permission"
 18	"github.com/opencode-ai/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	// Initialize LSP clients in the background
 53	go app.initLSPClients(ctx)
 54
 55	var err error
 56	app.CoderAgent, err = agent.NewAgent(
 57		config.AgentCoder,
 58		app.Sessions,
 59		app.Messages,
 60		agent.CoderAgentTools(
 61			app.Permissions,
 62			app.Sessions,
 63			app.Messages,
 64			app.History,
 65			app.LSPClients,
 66		),
 67	)
 68	if err != nil {
 69		logging.Error("Failed to create coder agent", err)
 70		return nil, err
 71	}
 72
 73	return app, nil
 74}
 75
 76
 77// Shutdown performs a clean shutdown of the application
 78func (app *App) Shutdown() {
 79	// Cancel all watcher goroutines
 80	app.cancelFuncsMutex.Lock()
 81	for _, cancel := range app.watcherCancelFuncs {
 82		cancel()
 83	}
 84	app.cancelFuncsMutex.Unlock()
 85	app.watcherWG.Wait()
 86
 87	// Perform additional cleanup for LSP clients
 88	app.clientsMutex.RLock()
 89	clients := make(map[string]*lsp.Client, len(app.LSPClients))
 90	maps.Copy(clients, app.LSPClients)
 91	app.clientsMutex.RUnlock()
 92
 93	for name, client := range clients {
 94		shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
 95		if err := client.Shutdown(shutdownCtx); err != nil {
 96			logging.Error("Failed to shutdown LSP client", "name", name, "error", err)
 97		}
 98		cancel()
 99	}
100}