package cli import ( "encoding/json" "fmt" "net/url" "strconv" "strings" "github.com/spf13/cobra" "gitea.mynodes.xyz/ilker/llmgw/internal/control/adminapi" ) func newLogsCmd(client func() *ctlClient, asJSON *bool) *cobra.Command { var ( source string lines int ) c := &cobra.Command{ Use: "logs NODE", Short: "Show the recent lines of a node's agent log, or of an engine on it", Long: "Read through the agent's own stream, so it needs no access to the node:\n" + "no ssh, no jump host, no root for the journal. The agent keeps its last\n" + "2000 lines and each engine's last few hundred.\n\n" + " llmgw ctl logs llmgw-node06 the agent\n" + " llmgw ctl logs llmgw-node06 --source SHARD an engine's output", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { q := url.Values{} q.Set("source", source) q.Set("lines", strconv.Itoa(lines)) var v adminapi.LogsView err := client().get(cmd.Context(), "/api/v1/nodes/"+url.PathEscape(args[0])+"/logs?"+q.Encode(), &v) if *asJSON { return json.NewEncoder(cmd.OutOrStdout()).Encode(v) } if err != nil { return err } for _, l := range v.Lines { fmt.Fprintln(cmd.OutOrStdout(), l) } if v.Error != "" { return fmt.Errorf("%s", v.Error) } // Where else to look, since the source list is the one thing a // person cannot guess. fmt.Fprintf(cmd.ErrOrStderr(), "\n-- %s on %s; other sources: %s\n", v.Source, v.Node, strings.Join(v.Sources, ", ")) return nil }, } c.Flags().StringVar(&source, "source", "agent", `"agent", or a shard id for that engine's output`) c.Flags().IntVarP(&lines, "lines", "n", 200, "how many of the most recent lines") return c }