cmd/llm_aggregator.go (view raw)
1package main
2
3import (
4 "context"
5 "fmt"
6 "os"
7
8 tea "github.com/charmbracelet/bubbletea"
9 "llm_aggregator/internal/cli"
10 "llm_aggregator/internal/config"
11 "llm_aggregator/internal/progress"
12 "llm_aggregator/internal/runtime"
13 "llm_aggregator/internal/tui"
14)
15
16var (
17 version string
18 buildDate string
19)
20
21func main() {
22 cli.BuildDate = buildDate
23 cli.Version = version
24
25 // Parse command line arguments
26 args, err := cli.ParseArgs()
27 if err != nil {
28 fmt.Fprintf(os.Stderr, "Error parsing arguments: %v\n", err)
29 os.Exit(1)
30 }
31
32 // Get viper instance with config file + environment variables + defaults
33 v := config.GetViper()
34
35 // Bind CLI args to viper (highest precedence)
36 config.BindCLIArgs(v, args.ToViperMap())
37
38 // Validate API key
39 if v.GetString("api_key") == "" {
40 fmt.Fprintln(os.Stderr, "Error: OpenAI-compatible API key is required. Set via --api-key, LLM_AGGREGATOR_API_KEY environment variable, or config file.")
41 os.Exit(1)
42 }
43
44 // Create runtime directly from viper configuration
45 rt := config.ViperToRuntime(v, args.FeedsFile, args.Prompt)
46
47 // Run with TUI if requested
48 if args.TUI {
49 runWithTUI(rt)
50 } else {
51 runWithoutTUI(rt, args.Verbose)
52 }
53}
54
55func runWithTUI(rt *runtime.Runtime) {
56 // 1. Create the model and pass the runtime to it.
57 model := tui.New(rt)
58
59 // 2. Create the tea.Program.
60 p := tea.NewProgram(model, tea.WithAltScreen())
61
62 // 3. Create the TUIProgress handler and give it the program instance.
63 // This is the bridge that allows the runtime to send messages to the TUI.
64 tp := tui.NewTUIProgress(p)
65
66 // 4. Inject the progress handler into the runtime.
67 rt.Progress = tp
68
69 // 5. Run the program. This is a blocking call.
70 if _, err := p.Run(); err != nil {
71 fmt.Fprintf(os.Stderr, "TUI Error: %v\n", err)
72 os.Exit(1)
73 }
74}
75
76func runWithoutTUI(rt *runtime.Runtime, verbose bool) {
77 // Setup logger based on verbose flag and inject it into the runtime
78 if verbose {
79 rt.Progress = progress.NewSimpleLogger(os.Stdout, true)
80 } else {
81 // Default is already NoopLogger, but we can be explicit
82 rt.Progress = &progress.NoopLogger{}
83 }
84
85 // Execute the runtime
86 err := rt.Execute(context.Background())
87 if err != nil {
88 fmt.Fprintf(os.Stderr, "Error: %v\n", err)
89 os.Exit(1)
90 }
91
92 // Write output
93 if rt.OutputFile != "" {
94 if err := rt.WriteOutputToFile(); err != nil {
95 fmt.Fprintf(os.Stderr, "Error writing output: %v\n", err)
96 os.Exit(1)
97 }
98 } else {
99 if err := rt.WriteOutput(os.Stdout); err != nil {
100 fmt.Fprintf(os.Stderr, "Error writing output: %v\n", err)
101 os.Exit(1)
102 }
103 }
104}