package main import ( "context" "errors" "log" "os" "os/signal" "syscall" "time" companion "browser.local/plugins/scum-server-plugin/companion" ) func main() { log.SetFlags(log.Ldate | log.Ltime | log.LUTC) config, err := loadConfig() if err != nil { log.Printf("SCUM companion configuration failed: %v", err) os.Exit(1) } client, err := companion.NewClient(config, companion.Options{}) if err != nil { log.Printf("SCUM companion client failed: %v", err) os.Exit(1) } ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) defer stop() sequenceStore, err := companion.NewFileSnapshotSequenceStore(companion.SnapshotSequenceFilename) if err != nil { log.Printf("SCUM companion sequence state failed: %v", err) os.Exit(1) } projection := companion.NewSCUMPlayerLogProjection(client, config.Component.ServerInstanceID) projection.SequenceStore = sequenceStore collector := companion.NewConsoleLogCollector(client, noopLogStore{}, config.Component.ServerInstanceID, os.Getenv(config.Proof.MaterialEnv)) collector.OnSemanticEvents = projection.Handle runtime := companion.Runtime{ Client: client, Dispatcher: companion.Dispatcher{Client: client, Registry: companion.NewHandlerRegistry(companion.HandlerAvailability{BoundServerID: config.Component.ServerInstanceID, Approved: true, Capabilities: map[string]bool{"companion.diagnostics": true}}, companion.RuntimeAdapter{BoundServerID: config.Component.ServerInstanceID}), PollLimit: 10, Backoff: 2 * time.Second}, HeartbeatEvery: time.Duration(config.Timing.HeartbeatIntervalSeconds) * time.Second, PollEvery: time.Duration(config.Timing.CommandPollIntervalSeconds) * time.Second, Backoff: 2 * time.Second, } errorsCh := make(chan error, 2) go func() { errorsCh <- runtime.Run(ctx) }() go func() { errorsCh <- collector.Run(ctx) }() if err := <-errorsCh; err != nil && !errors.Is(err, context.Canceled) { log.Printf("SCUM companion stopped: %v", err) os.Exit(1) } } func loadConfig() (companion.Config, error) { file, err := os.Open("config.yaml") if err != nil { return companion.Config{}, err } defer file.Close() return companion.LoadConfig(file) } type noopLogStore struct{} func (noopLogStore) EnsureSchema(context.Context) error { return nil } func (noopLogStore) StoreConsoleRecords(context.Context, []companion.ConsoleRecord) (int, error) { return 0, nil } func (noopLogStore) StoreSemanticEventBatch(_ context.Context, batch companion.SemanticEventBatch) (int, error) { return len(batch.Events), nil }