Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions cmd/desktop/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ func main() {
PrometheusHeadersFromEnv: fileCfg.PrometheusHeadersFromEnv,
Version: version,
MCPEnabled: fileCfg.MCPEnabledOr(true),
AIHistory: fileCfg.AIHistoryOr(true),
AIHistoryDBPath: fileCfg.AIHistoryDBPath,
}

app.SetGlobals(cfg)
Expand Down
13 changes: 13 additions & 0 deletions cmd/explorer/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/skyhook-io/radar/internal/auth"
"github.com/skyhook-io/radar/internal/cloud"
"github.com/skyhook-io/radar/internal/config"
"github.com/skyhook-io/radar/internal/diagnosecli"
"github.com/skyhook-io/radar/internal/k8s"
mcppkg "github.com/skyhook-io/radar/internal/mcp"
"github.com/skyhook-io/radar/internal/server"
Expand All @@ -32,6 +33,14 @@ var (
)

func main() {
// Subcommand dispatch (before flag parsing — subcommands own their flags).
// `radar diagnose <kind>/<name>` is a thin client for a RUNNING instance.
if len(os.Args) > 1 && os.Args[1] == "diagnose" {
os.Exit(diagnosecli.Run(os.Args[2:], func(url string) {
app.OpenBrowser(url, "")
}))
}

startupStart := time.Now()

// Propagate the build-time version to the cloud dialer so the agent
Expand Down Expand Up @@ -69,6 +78,8 @@ func main() {
timelineDBPath := flag.String("timeline-db", fileCfg.TimelineDBPath, "Path to timeline database file (default: ~/.radar/timeline.db)")
timelineRetention := flag.Duration("timeline-retention", fileCfg.TimelineRetentionOr(7*24*time.Hour), "How long to retain timeline events when --timeline-storage=sqlite (e.g. 168h, 720h). 0 disables age-based cleanup.")
timelineMaxSize := flag.String("timeline-max-size", fileCfg.TimelineMaxSizeOr("1Gi"), "Maximum SQLite timeline storage size before pruning oldest events (e.g. 800Mi, 8Gi). 0 disables size-based pruning.")
// AI history (Diagnose investigations)
aiHistory := flag.Bool("ai-history", fileCfg.AIHistoryOr(true), "Persist AI investigations (transcripts + verdicts) to ~/.radar/ai-runs.db so they survive restarts")
// Traffic/metrics options
prometheusURL := flag.String("prometheus-url", fileCfg.PrometheusURL, "Manual Prometheus/VictoriaMetrics URL (skips auto-discovery)")
// --prometheus-header Key=Value, repeatable. Defaults populated from
Expand Down Expand Up @@ -222,6 +233,8 @@ func main() {
PrometheusHeaders: resolvedPrometheusHeaders,
PrometheusHeadersFromEnv: promHeadersFromEnv.value(),
MCPEnabled: mcpEnabled,
AIHistory: *aiHistory,
AIHistoryDBPath: fileCfg.AIHistoryDBPath,
Version: version,
AuthConfig: auth.Config{
Mode: *authMode,
Expand Down
33 changes: 26 additions & 7 deletions internal/ai/diagnoser.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,14 +84,26 @@ type Request struct {
// ResourceHealthSignal is the compact server-side health frame captured when a
// run starts. Issue fields describe live operational findings; audit fields are
// static posture findings and should not be treated as proof of an outage.
// Issues/AuditFindings carry the top actual rows (capped) — aggregates alone
// read as vague in the UI and starve the prompt of detail Radar already has.
type ResourceHealthSignal struct {
Health string `json:"health,omitempty"`
IssueCount int `json:"issueCount,omitempty"`
HighestSeverity string `json:"highestSeverity,omitempty"`
TopReason string `json:"topReason,omitempty"`
AuditCount int `json:"auditCount,omitempty"`
AuditSeverity string `json:"auditSeverity,omitempty"`
TopFinding string `json:"topFinding,omitempty"`
Health string `json:"health,omitempty"`
IssueCount int `json:"issueCount,omitempty"`
HighestSeverity string `json:"highestSeverity,omitempty"`
TopReason string `json:"topReason,omitempty"`
Issues []HealthLine `json:"issues,omitempty"`
AuditCount int `json:"auditCount,omitempty"`
AuditSeverity string `json:"auditSeverity,omitempty"`
TopFinding string `json:"topFinding,omitempty"`
AuditFindings []HealthLine `json:"auditFindings,omitempty"`
}

// HealthLine is one concrete issue/finding row: what Radar's issue engine or
// audit suite actually said, not just a count.
type HealthLine struct {
Severity string `json:"severity,omitempty"`
Reason string `json:"reason,omitempty"` // issue Reason / audit CheckID
Message string `json:"message,omitempty"` // human detail, capped
}

// Diagnosis is the engine's final result.
Expand Down Expand Up @@ -473,6 +485,13 @@ func healthFrame(target string, health *ResourceHealthSignal) string {
}
}
b.WriteString(".")
for _, line := range health.Issues {
fmt.Fprintf(&b, " [%s] %s", line.Severity, line.Reason)
if line.Message != "" {
fmt.Fprintf(&b, ": %s", line.Message)
}
b.WriteString(".")
}
Comment thread
cursor[bot] marked this conversation as resolved.
case health.Health == "healthy":
fmt.Fprintf(&b, "Radar currently reports %s as healthy and flags 0 active issues.", target)
case health.Health != "":
Expand Down
Loading