Skip to content
Merged
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
29 changes: 16 additions & 13 deletions cmd/aima/context.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package main

import (
"time"

"github.com/jguan/aima/internal/agent"
"github.com/jguan/aima/internal/k3s"
"github.com/jguan/aima/internal/knowledge"
Expand All @@ -15,17 +17,18 @@ import (
// It collects the local variables that buildToolDeps() closures previously
// captured, enabling those closures to be split into separate files.
type appContext struct {
cat *knowledge.Catalog
db *state.DB
kStore *knowledge.Store
rt runtime.Runtime // default runtime (K3S > Docker > Native)
nativeRt runtime.Runtime
dockerRt runtime.Runtime
k3sRt runtime.Runtime
proxy *proxy.Server
k3s *k3s.Client
dataDir string
digests map[string]string // factory catalog digests
support *support.Service
eventBus *agent.EventBus // shared EventBus for Explorer events
cat *knowledge.Catalog
db *state.DB
kStore *knowledge.Store
rt runtime.Runtime // default runtime (K3S > Docker > Native)
nativeRt runtime.Runtime
dockerRt runtime.Runtime
k3sRt runtime.Runtime
proxy *proxy.Server
k3s *k3s.Client
dataDir string
catalogLoadedAt time.Time
digests map[string]string // factory catalog digests
support *support.Service
eventBus *agent.EventBus // shared EventBus for Explorer events
}
3 changes: 2 additions & 1 deletion cmd/aima/diagnostics.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@ func buildDiagnosticsBundle(ctx context.Context, ac *appContext, deps *mcp.ToolD
"goarch": goruntime.GOARCH,
"data_dir": redactHomePath(dataDir),
},
"sections": map[string]any{},
"service_context": serviceContextStatus(ac),
"sections": map[string]any{},
}

sections := bundle["sections"].(map[string]any)
Expand Down
27 changes: 27 additions & 0 deletions cmd/aima/diagnostics_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,3 +124,30 @@ func TestBuildDiagnosticsBundleRecordsSectionErrors(t *testing.T) {
t.Fatalf("hardware error = %v, want context canceled", hardware["error"])
}
}

func TestServiceContextReportsStaleOverlayHint(t *testing.T) {
tmp := t.TempDir()
overlayDir := tmp + string(os.PathSeparator) + "catalog" + string(os.PathSeparator) + "user" + string(os.PathSeparator) + "models"
if err := os.MkdirAll(overlayDir, 0o755); err != nil {
t.Fatalf("mkdir overlay: %v", err)
}
overlayFile := overlayDir + string(os.PathSeparator) + "demo.patch.yaml"
if err := os.WriteFile(overlayFile, []byte("kind: model_asset_patch\nmetadata:\n name: demo\n"), 0o644); err != nil {
t.Fatalf("write overlay: %v", err)
}
newer := time.Now().Add(5 * time.Minute)
if err := os.Chtimes(overlayFile, newer, newer); err != nil {
t.Fatalf("chtimes overlay: %v", err)
}

status := serviceContextStatus(&appContext{
dataDir: tmp,
catalogLoadedAt: time.Now(),
})
if status["overlay_newer_than_catalog"] != true {
t.Fatalf("overlay_newer_than_catalog = %v, want true; status=%v", status["overlay_newer_than_catalog"], status)
}
if status["reload_hint"] == "" {
t.Fatalf("reload_hint missing: %v", status)
}
}
25 changes: 13 additions & 12 deletions cmd/aima/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,18 +154,19 @@ func run() error {
mcpServer := mcp.NewServer()
supportSvc := support.NewService(db, support.WithLogger(slog.Default()))
ac := &appContext{
cat: cat,
db: db,
kStore: knowledgeStore,
rt: rt,
nativeRt: nativeRt,
dockerRt: dockerRt,
k3sRt: k3sRt,
proxy: proxyServer,
k3s: k3sClient,
dataDir: dataDir,
digests: factoryDigests,
support: supportSvc,
cat: cat,
db: db,
kStore: knowledgeStore,
rt: rt,
nativeRt: nativeRt,
dockerRt: dockerRt,
k3sRt: k3sRt,
proxy: proxyServer,
k3s: k3sClient,
dataDir: dataDir,
catalogLoadedAt: time.Now().UTC(),
digests: factoryDigests,
support: supportSvc,
}
deps := buildToolDeps(ac)

Expand Down
65 changes: 65 additions & 0 deletions cmd/aima/service_context.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package main

import (
"os"
"path/filepath"
"strings"
"time"
)

func serviceContextStatus(ac *appContext) map[string]any {
dataDir := ""
var loadedAt time.Time
if ac != nil {
dataDir = strings.TrimSpace(ac.dataDir)
loadedAt = ac.catalogLoadedAt
}
overlayDir := ""
if dataDir != "" {
overlayDir = filepath.Join(dataDir, "catalog")
}
latestOverlay := latestModTime(overlayDir)

status := map[string]any{
"data_dir": dataDir,
"overlay_dir": overlayDir,
}
if !loadedAt.IsZero() {
status["catalog_loaded_at"] = loadedAt.Format(time.RFC3339)
}
if !latestOverlay.IsZero() {
status["overlay_latest_mtime"] = latestOverlay.Format(time.RFC3339)
if !loadedAt.IsZero() && latestOverlay.After(loadedAt) {
status["overlay_newer_than_catalog"] = true
status["reload_hint"] = "catalog overlays changed after this AIMA process loaded; restart aima-serve or reload catalog before trusting UI dry-run results"
}
}
if user := os.Getenv("USER"); user != "" {
status["user"] = user
}
if home, err := os.UserHomeDir(); err == nil && home != "" {
status["home_dir"] = home
}
return status
}

func latestModTime(root string) time.Time {
if strings.TrimSpace(root) == "" {
return time.Time{}
}
var latest time.Time
_ = filepath.WalkDir(root, func(path string, d os.DirEntry, err error) error {
if err != nil || d == nil {
return nil
}
info, statErr := d.Info()
if statErr != nil {
return nil
}
if info.ModTime().After(latest) {
latest = info.ModTime()
}
return nil
})
return latest
}
9 changes: 5 additions & 4 deletions cmd/aima/tooldeps_knowledge.go
Original file line number Diff line number Diff line change
Expand Up @@ -591,10 +591,11 @@ func buildKnowledgeDeps(ac *appContext, deps *mcp.ToolDeps) {
}
}
status := map[string]any{
"factory_assets": catalogSize(factoryCat),
"overlay_assets": catalogSize(overlayCat),
"shadowed": shadowed,
"parse_warnings": parseWarnings,
"factory_assets": catalogSize(factoryCat),
"overlay_assets": catalogSize(overlayCat),
"shadowed": shadowed,
"parse_warnings": parseWarnings,
"service_context": serviceContextStatus(ac),
}
return json.Marshal(status)
}
Expand Down
3 changes: 3 additions & 0 deletions cmd/aima/tooldeps_system.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,9 @@ func buildSystemDeps(ac *appContext, deps *mcp.ToolDeps) {
if b, e := json.Marshal(ac.support.Status(ctx)); e == nil {
status["support"] = b
}
if b, e := json.Marshal(serviceContextStatus(ac)); e == nil {
status["service_context"] = b
}
if deps.OpenClawStatus != nil {
if b, e := deps.OpenClawStatus(ctx); e == nil {
status["openclaw"] = b
Expand Down
Loading