// Command atlas9api is a command-line client for the Atlas9 demo API. It wraps // the generated client (atlas9.dev/c/demo/api/generated) with credential // storage and a command per endpoint. // // Usage: // // atlas9api logout // atlas9api status [--url ] // atlas9api [--url ] [-d ''|-d @file] // // The request body for an endpoint is read from -d (inline JSON or @file) or, // if -d is omitted, from stdin when it is piped. The bearer token cannot be // passed on the command line: it comes from the ATLAS9_TOKEN env var, else the // stored credentials file. The base URL resolves in the order: --url flag > // ATLAS9_URL env > credentials file > built-in default. package main import ( "context" "encoding/json" "flag" "fmt" "io" "os" "strings" client "atlas9.dev/c/demo/api/generated" ) func main() { if err := run(os.Args[1:]); err != nil { fmt.Fprintf(os.Stderr, "error: %s\n", err) os.Exit(1) } } func run(args []string) error { if len(args) == 0 { usage() return nil } cmd := args[0] rest := args[1:] switch cmd { case "-h", "--help", "help": usage() return nil case "login": return runLogin(rest) case "logout": return runLogout(rest) case "status": return runStatus(rest) default: return runEndpoint(cmd, rest) } } // urlFlag registers --url on fs and returns a pointer to it. func urlFlag(fs *flag.FlagSet) *string { return fs.String("url", "", "API base URL (overrides env and stored config)") } func runLogin(args []string) error { // The old flag-based login (accepting the secret on the command line) is // gone. A real login is a future interactive flow. Until then, authenticate // with the ATLAS9_TOKEN env var or a stored credentials file. return fmt.Errorf("login is not implemented yet; set ATLAS9_TOKEN or use a stored credentials file") } func runLogout(args []string) error { fs := flag.NewFlagSet("logout", flag.ContinueOnError) if err := fs.Parse(args); err != nil { return err } cfg, err := loadConfig() if err != nil { return err } cfg.Token = "" if err := saveConfig(cfg); err != nil { return err } fmt.Println("Logged out.") return nil } func runStatus(args []string) error { fs := flag.NewFlagSet("status", flag.ContinueOnError) url := urlFlag(fs) if err := fs.Parse(args); err != nil { return err } stored, err := loadConfig() if err != nil { return err } cfg := resolve(stored, *url) fmt.Printf("Base URL: %s\n", cfg.BaseURL) if cfg.Token == "" { fmt.Println("Token: (none)") } else { fmt.Printf("Token: %s\n", maskToken(cfg.Token)) } return nil } func runEndpoint(name string, args []string) error { fs := flag.NewFlagSet(name, flag.ContinueOnError) url := urlFlag(fs) data := fs.String("d", "", "request body: inline JSON, or @file to read from a file") if err := fs.Parse(args); err != nil { return err } body, err := requestBody(*data) if err != nil { return err } stored, err := loadConfig() if err != nil { return err } cfg := resolve(stored, *url) c := client.NewClient(cfg.BaseURL) c.Token = cfg.Token res, err := client.Invoke(context.Background(), c, name, body) if err != nil { return err } out, err := json.MarshalIndent(res, "", " ") if err != nil { return err } fmt.Println(string(out)) return nil } // requestBody resolves the -d value into a JSON body. "@path" reads a file, an // empty value reads piped stdin (nothing when stdin is a terminal), and any // other value is used verbatim. func requestBody(d string) ([]byte, error) { if strings.HasPrefix(d, "@") { return os.ReadFile(d[1:]) } if d != "" { return []byte(d), nil } info, err := os.Stdin.Stat() if err != nil { return nil, err } if info.Mode()&os.ModeCharDevice != 0 { // Interactive terminal: no piped body. return nil, nil } return io.ReadAll(os.Stdin) } func maskToken(t string) string { // Show only the key ID (before the dot); never echo the secret. if id, _, ok := strings.Cut(t, "."); ok { return id + ".***" } if len(t) <= 4 { return "***" } return t[:4] + "***" } func usage() { fmt.Fprint(os.Stderr, `atlas9api - command-line client for the Atlas9 demo API Usage: atlas9api logout atlas9api status [--url ] atlas9api [--url ] [-d ''|-d @file] The bearer token cannot be passed on the command line: it comes from the ATLAS9_TOKEN env var, else the stored credentials file. The base URL resolves in the order: --url flag > ATLAS9_URL env > stored file > built-in default. Run an endpoint with no -d to read the JSON request body from piped stdin. `) fmt.Fprintf(os.Stderr, "Endpoints (%d):\n", len(client.CommandNames)) for _, n := range client.CommandNames { fmt.Fprintf(os.Stderr, " %s\n", n) } }