using ClaudeDo.Data; using ClaudeDo.Data.Git; using ClaudeDo.Data.Repositories; using ClaudeDo.Worker.Config; using ClaudeDo.Worker.Hub; using ClaudeDo.Worker.Runner; using ClaudeDo.Worker.Services; using Microsoft.EntityFrameworkCore; var cfg = WorkerConfig.Load(); var builder = WebApplication.CreateBuilder(args); // When launched by the Windows SCM, speak the Service Control Protocol so SCM // doesn't think we crashed (~30s timeout). No-op when running interactively. builder.Host.UseWindowsService(o => o.ServiceName = "ClaudeDoWorker"); builder.Services.AddDbContextFactory(opt => opt.UseSqlite($"Data Source={cfg.DbPath}")); builder.Services.AddSingleton(cfg); builder.Services.AddHostedService(); builder.Services.AddSignalR(); // Runner stack. builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); // Agent file management. var agentsDir = Path.Combine(ClaudeDo.Data.Paths.AppDataRoot(), "agents"); Directory.CreateDirectory(agentsDir); builder.Services.AddSingleton(new AgentFileService(agentsDir)); // QueueService: singleton + hosted service (same instance). builder.Services.AddSingleton(); builder.Services.AddHostedService(sp => sp.GetRequiredService()); // Loopback-only bind. Firewall is irrelevant for 127.0.0.1. builder.WebHost.UseUrls($"http://127.0.0.1:{cfg.SignalRPort}"); var app = builder.Build(); using (var scope = app.Services.CreateScope()) { ClaudeDoDbContext.MigrateAndConfigure( scope.ServiceProvider.GetRequiredService()); } app.MapHub("/hub"); app.Logger.LogInformation("ClaudeDo.Worker listening on http://127.0.0.1:{Port} (db: {Db})", cfg.SignalRPort, cfg.DbPath); app.Run();