From d58d21a9a3d32e62bed1f7966333d4d2c8722b05 Mon Sep 17 00:00:00 2001 From: phani Date: Wed, 25 Feb 2026 16:30:53 -0500 Subject: [PATCH 1/2] feat(cli): add `kernel status` command Displays overall system status and per-group/component breakdown from the API's /status endpoint, with color-coded output matching the dashboard indicator. --- cmd/root.go | 3 +- cmd/status.go | 120 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 cmd/status.go diff --git a/cmd/root.go b/cmd/root.go index 5542dcb..8179d8c 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -90,7 +90,7 @@ func isAuthExempt(cmd *cobra.Command) bool { // Check if the top-level command is in the exempt list switch topLevel.Name() { - case "login", "logout", "help", "completion", "create", "mcp", "upgrade": + case "login", "logout", "help", "completion", "create", "mcp", "upgrade", "status": return true case "auth": // Only exempt the auth command itself (status display), not its subcommands @@ -147,6 +147,7 @@ func init() { rootCmd.AddCommand(createCmd) rootCmd.AddCommand(mcp.MCPCmd) rootCmd.AddCommand(upgradeCmd) + rootCmd.AddCommand(statusCmd) rootCmd.PersistentPostRunE = func(cmd *cobra.Command, args []string) error { // running synchronously so we never slow the command diff --git a/cmd/status.go b/cmd/status.go new file mode 100644 index 0000000..20fe5d2 --- /dev/null +++ b/cmd/status.go @@ -0,0 +1,120 @@ +package cmd + +import ( + "encoding/json" + "fmt" + "net/http" + "os" + "strings" + "time" + + "github.com/pterm/pterm" + "github.com/spf13/cobra" +) + +type statusComponent struct { + Name string `json:"name"` + Status string `json:"status"` +} + +type statusGroup struct { + Name string `json:"name"` + Status string `json:"status"` + Components []statusComponent `json:"components"` +} + +type statusResponse struct { + Status string `json:"status"` + Groups []statusGroup `json:"groups"` +} + +const defaultBaseURL = "https://api.onkernel.com" + +var statusCmd = &cobra.Command{ + Use: "status", + Short: "Check the operational status of Kernel services", + RunE: runStatus, +} + +func init() { + statusCmd.Flags().StringP("output", "o", "", "Output format (json)") +} + +func getBaseURL() string { + if u := os.Getenv("KERNEL_BASE_URL"); strings.TrimSpace(u) != "" { + return strings.TrimRight(u, "/") + } + return defaultBaseURL +} + +func runStatus(cmd *cobra.Command, args []string) error { + output, _ := cmd.Flags().GetString("output") + + client := &http.Client{Timeout: 10 * time.Second} + resp, err := client.Get(getBaseURL() + "/status") + if err != nil { + pterm.Error.Println("Could not reach Kernel API. Check https://status.kernel.sh for updates.") + return fmt.Errorf("request failed: %w", err) + } + defer resp.Body.Close() + + var status statusResponse + if err := json.NewDecoder(resp.Body).Decode(&status); err != nil { + return fmt.Errorf("invalid response: %w", err) + } + + if output == "json" { + enc := json.NewEncoder(os.Stdout) + enc.SetIndent("", " ") + return enc.Encode(status) + } + + printStatus(status) + return nil +} + +// Colors match the dashboard's api-status-indicator.tsx +var statusDisplay = map[string]struct { + label string + rgb pterm.RGB +}{ + "operational": {label: "Operational", rgb: pterm.NewRGB(31, 163, 130)}, + "degraded_performance": {label: "Degraded Performance", rgb: pterm.NewRGB(245, 158, 11)}, + "partial_outage": {label: "Partial Outage", rgb: pterm.NewRGB(242, 85, 51)}, + "full_outage": {label: "Major Outage", rgb: pterm.NewRGB(239, 68, 68)}, + "maintenance": {label: "Maintenance", rgb: pterm.NewRGB(36, 99, 235)}, + "unknown": {label: "Unknown", rgb: pterm.NewRGB(128, 128, 128)}, +} + +func getStatusDisplay(status string) (string, pterm.RGB) { + if d, ok := statusDisplay[status]; ok { + return d.label, d.rgb + } + return "Unknown", pterm.NewRGB(128, 128, 128) +} + +func coloredDot(rgb pterm.RGB) string { + return rgb.Sprint("●") +} + +func printStatus(resp statusResponse) { + label, rgb := getStatusDisplay(resp.Status) + header := fmt.Sprintf("Kernel Status: %s", rgb.Sprint(label)) + pterm.Println() + pterm.Println(" " + header) + + for _, group := range resp.Groups { + pterm.Println() + if len(group.Components) == 0 { + groupLabel, groupColor := getStatusDisplay(group.Status) + pterm.Printf(" %s %s %s\n", coloredDot(groupColor), pterm.Bold.Sprint(group.Name), groupLabel) + } else { + pterm.Println(" " + pterm.Bold.Sprint(group.Name)) + for _, comp := range group.Components { + compLabel, compColor := getStatusDisplay(comp.Status) + pterm.Printf(" %s %-20s %s\n", coloredDot(compColor), comp.Name, compLabel) + } + } + } + pterm.Println() +} From c19a361591a60d5fd85f6b2b6ea0f8c9b316d74f Mon Sep 17 00:00:00 2001 From: phani Date: Wed, 25 Feb 2026 17:15:42 -0500 Subject: [PATCH 2/2] fix(cmd/status): handle non-2xx responses from Kernel API Added error handling to the `runStatus` function to log an error message and return an error when the response status code from the Kernel API is not in the 2xx range. This improves user feedback when the API is unreachable. --- cmd/status.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cmd/status.go b/cmd/status.go index 20fe5d2..749416c 100644 --- a/cmd/status.go +++ b/cmd/status.go @@ -58,6 +58,11 @@ func runStatus(cmd *cobra.Command, args []string) error { } defer resp.Body.Close() + if resp.StatusCode < 200 || resp.StatusCode >= 300 { + pterm.Error.Println("Could not reach Kernel API. Check https://status.kernel.sh for updates.") + return fmt.Errorf("status request failed: %s", resp.Status) + } + var status statusResponse if err := json.NewDecoder(resp.Body).Decode(&status); err != nil { return fmt.Errorf("invalid response: %w", err)