]> git.giacomolli.uk - onechat/blob - main.go
Define child agent areas and reporting rules
[onechat] / main.go
1 // Command onechat runs the agent daemon and its terminal client.
2 package main
3
4 import (
5         "context"
6         "fmt"
7         "os"
8         "os/signal"
9         "strings"
10         "syscall"
11 )
12
13 // usage is the help text of the command.
14 const usage = `Usage: onechat [COMMAND]
15
16 Commands:
17   backup FILE              archive the running daemon data
18   restore FILE             restore into an empty data directory
19   chat                     talk to the agent in this terminal (default)
20   daemon                   run the agent, Telegram, and WhatsApp
21   import [-n] ZIP...        import WhatsApp history (stop the daemon first)
22     -n                     print the plan without model calls
23   login                    log in to ChatGPT with a device code
24   probe LOG COUNT [BUILDS] measure the compactor on a log, with real calls
25   version                  show the build version
26   help                     show this help
27
28 Chat needs a running daemon. ONECHAT_DIR sets the data directory.
29 `
30
31 // version is set by the package build and stays dev for direct Go builds.
32 var version = "dev"
33
34 func main() {
35         err := run()
36         if err != nil {
37                 fmt.Fprintln(os.Stderr, "onechat:", err)
38                 os.Exit(1)
39         }
40 }
41
42 func run() error {
43         ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
44         defer cancel()
45
46         args := os.Args[1:]
47         switch {
48         case len(args) == 1 && (args[0] == "help" || args[0] == "--help" || args[0] == "-h"):
49                 _, err := fmt.Print(usage)
50                 if err != nil {
51                         return fmt.Errorf("write command help: %w", err)
52                 }
53         case len(args) == 1 && args[0] == "version":
54                 _, err := fmt.Println(version)
55                 if err != nil {
56                         return fmt.Errorf("write build version: %w", err)
57                 }
58         case len(args) == 1 && args[0] == "login":
59                 err := chatgptLogin(ctx, os.Stdout)
60                 if err != nil {
61                         return err
62                 }
63
64         // A signal is the normal stop of the daemon and the client, so their
65         // result does not include ctx.Err().
66         case len(args) == 1 && args[0] == "daemon":
67                 return daemon(ctx, os.Stdout)
68         case len(args) == 0 || len(args) == 1 && args[0] == "chat":
69                 return terminal(ctx, os.Stdin, os.Stdout)
70         case len(args) == 2 && args[0] == "backup":
71                 return backup(ctx, args[1], os.Stdout)
72         case len(args) == 2 && args[0] == "restore":
73                 return restore(ctx, args[1], os.Stdout)
74         case len(args) > 0 && args[0] == "import":
75                 return whatsappImport(ctx, args[1:], os.Stdout)
76         case len(args) > 0 && args[0] == "probe":
77                 err := probe(ctx, args[1:], os.Stdout)
78                 if err != nil {
79                         return err
80                 }
81         default:
82                 return fmt.Errorf("unknown command %q\n\n%s", strings.Join(args, " "), strings.TrimSuffix(usage, "\n"))
83         }
84
85         return ctx.Err()
86 }