E1: split monolithic files — cmd/nomos (main.go → server.go + mcp.go + workers.go),
internal/mcp/tools.go → entity_tools/ops_tools/knowledge_tools/analysis_tools,
internal/httpapi/impl.go → domain files (entities, events, signals, ontology,
fleet_health, client_context, client_lifecycle, entity_mutations, query_audit).
E2: migrate raw pool.Exec queries to sqlc (entities/relationships queries + generated).
E3: unify SSH — consolidate crypto/ssh dial into actuator/client.go (+client_test).
E4/E5: add tests — db/lifecycle, checkdefaults/build, ontology/preconditions, policy/risk.
88 lines
2.2 KiB
Go
88 lines
2.2 KiB
Go
package httpapi
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/dtoro/oikos/internal/httpapi/gen"
|
|
)
|
|
|
|
func (s *Server) GetClientContext(ctx context.Context, req gen.GetClientContextRequestObject) (gen.GetClientContextResponseObject, error) {
|
|
slug := string(req.Slug)
|
|
_, err := s.resolveEntityID(ctx, slug)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var version int64
|
|
_ = s.pool.QueryRow(ctx,
|
|
"SELECT version FROM context_version WHERE singleton = true").Scan(&version)
|
|
|
|
var filesChanged, toolsChanged []string
|
|
var sopsChanged bool
|
|
if req.Params.Since != nil {
|
|
rows, qErr := s.pool.Query(ctx,
|
|
"SELECT path FROM context_files WHERE last_changed > $1", *req.Params.Since)
|
|
if qErr == nil {
|
|
defer rows.Close()
|
|
for rows.Next() {
|
|
var p string
|
|
if scanErr := rows.Scan(&p); scanErr == nil {
|
|
// Matches tools/setup-*.sh (the auto-setup convention —
|
|
// see tools/post-pull.sh). Was tools/*.setup.sh until
|
|
// 2026-07-12, which never matched any real filename.
|
|
if strings.HasPrefix(p, "tools/setup-") && strings.HasSuffix(p, ".sh") {
|
|
toolsChanged = append(toolsChanged, p)
|
|
} else if p == ".sops.yaml" {
|
|
sopsChanged = true
|
|
} else {
|
|
filesChanged = append(filesChanged, p)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if filesChanged == nil {
|
|
filesChanged = []string{}
|
|
}
|
|
if toolsChanged == nil {
|
|
toolsChanged = []string{}
|
|
}
|
|
now := time.Now().UTC()
|
|
|
|
return gen.GetClientContext200JSONResponse{
|
|
AgentFilesChanged: &filesChanged,
|
|
SopsConfigChanged: &sopsChanged,
|
|
ToolsChanged: &toolsChanged,
|
|
Version: int(version),
|
|
Since: &now,
|
|
}, nil
|
|
}
|
|
|
|
func (s *Server) GetClientSecrets(ctx context.Context, req gen.GetClientSecretsRequestObject) (gen.GetClientSecretsResponseObject, error) {
|
|
slug := string(req.Slug)
|
|
_, err := s.resolveEntityID(ctx, slug)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var keys []string
|
|
if s.secretsManager != nil {
|
|
list, listErr := s.secretsManager.List(ctx)
|
|
if listErr == nil {
|
|
prefix := "clients/" + slug + "/"
|
|
for _, k := range list {
|
|
if strings.HasPrefix(k, prefix) || strings.HasPrefix(k, "shared/") {
|
|
keys = append(keys, k)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if keys == nil {
|
|
keys = []string{}
|
|
}
|
|
|
|
return gen.GetClientSecrets200JSONResponse{Keys: keys}, nil
|
|
} |