// SPDX-License-Identifier: MIT package cli import ( "errors" "fmt" "os/exec" "github.com/spf13/cobra" "prx/internal/config" "prx/internal/engine" "prx/internal/proto" "prx/internal/version" ) func (a *app) newLogsCmd() *cobra.Command { var follow bool cmd := &cobra.Command{ Use: "logs", Short: "Show proxy engine journal logs", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, _ []string) error { if _, err := exec.LookPath("journalctl"); err != nil { return errors.New("journalctl not found; install systemd to use 'prx logs'") } cfg, err := a.loadConfig() if err != nil { return err } if cfg.Engine.ServiceName == "" { return errors.New("engine.service_name is not configured; cannot read logs") } args := []string{"-u", cfg.Engine.ServiceName, "--no-pager"} if follow { args = append(args, "-f") } else { args = append(args, "-n", "100") } c := exec.CommandContext(cmd.Context(), "journalctl", args...) c.Stdout = a.out c.Stderr = a.errOut if err := c.Run(); err != nil { return fmt.Errorf("journalctl: %w (you may need to be in the systemd-journal group)", err) } return nil }, } cmd.Flags().BoolVarP(&follow, "follow", "f", false, "follow log output") return cmd } func (a *app) newVersionCmd() *cobra.Command { return &cobra.Command{ Use: "version", Short: "Print version information", Args: cobra.NoArgs, RunE: func(_ *cobra.Command, _ []string) error { fmt.Fprintf(a.out, "prx %s (commit %s, built %s)\n", version.Version, version.Commit, version.Date) return nil }, } } func (a *app) newInitCmd() *cobra.Command { var ( force bool withEngine bool engineConfig string ) cmd := &cobra.Command{ Use: "init", Short: "Write a default configuration file", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, _ []string) error { if config.Exists(a.configPath) && !force { fmt.Fprintf(a.out, "config already exists at %s (use --force to overwrite)\n", a.configPath) } else { if err := config.Save(a.configPath, config.Default()); err != nil { return err } fmt.Fprintf(a.out, "wrote default config to %s\n", a.configPath) } if !withEngine { return nil } cfg, err := a.loadConfig() if err != nil { return err } path := engineConfig if path == "" { path = cfg.Engine.ConfigPath } body, err := engine.Template(cfg) if err != nil { return err } return a.dispatch(cmd.Context(), cfg, proto.ActionInitEngine, func(p *proto.Plan) { p.EngineConfig = path p.EngineConfigBody = body }) }, } cmd.Flags().BoolVar(&force, "force", false, "overwrite an existing config file") cmd.Flags().BoolVar(&withEngine, "engine-config", false, "also generate a baseline engine configuration") cmd.Flags().StringVar(&engineConfig, "engine-config-path", "", "engine config path (defaults to engine.config_path)") return cmd }