Some checks failed
CI / build-and-test (push) Failing after -32m5s
- Implemented TraitsTab.svelte to handle CRUD operations for agent traits. - Integrated grid for displaying traits with context menu actions for add, edit, and delete. - Added trait instruction editing functionality with a dedicated editor. - Updated AdminShell to include PersonasPage for navigation. - Enhanced AppSidebar with a new entry for Personas. - Extended ShellPage type to include 'personas'. - Defined new types for AgentPersona, AgentPart, and AgentTrait in types.ts.
150 lines
3.6 KiB
Go
150 lines
3.6 KiB
Go
package app
|
|
|
|
import (
|
|
"fmt"
|
|
"log/slog"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/bitechdev/ResolveSpec/pkg/resolvespec"
|
|
"github.com/uptrace/bunrouter"
|
|
|
|
"git.warky.dev/wdevs/amcs/internal/store"
|
|
)
|
|
|
|
func registerResolveSpecAdminRoutes(mux *http.ServeMux, db *store.DB, middleware func(http.Handler) http.Handler, logger *slog.Logger) error {
|
|
rs := resolvespec.NewHandlerWithBun(db.Bun())
|
|
registerResolveSpecGuards(rs)
|
|
for _, model := range resolveSpecModels() {
|
|
if err := rs.RegisterModel(model.schema, model.entity, model.model); err != nil {
|
|
return fmt.Errorf("register resolvespec model %s.%s: %w", model.schema, model.entity, err)
|
|
}
|
|
}
|
|
|
|
rsRouter := bunrouter.New()
|
|
resolvespec.SetupBunRouterRoutes(rsRouter, rs, nil)
|
|
|
|
rsMount := http.StripPrefix("/api/rs", rsRouter)
|
|
protectedRSMount := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.URL.Path != "/" && strings.HasSuffix(r.URL.Path, "/") {
|
|
trimmed := strings.TrimRight(r.URL.Path, "/")
|
|
if trimmed == "" {
|
|
trimmed = "/"
|
|
}
|
|
clone := r.Clone(r.Context())
|
|
clone.URL.Path = trimmed
|
|
if clone.URL.RawPath != "" {
|
|
clone.URL.RawPath = strings.TrimRight(clone.URL.RawPath, "/")
|
|
if clone.URL.RawPath == "" {
|
|
clone.URL.RawPath = "/"
|
|
}
|
|
}
|
|
r = clone
|
|
}
|
|
if r.Method == http.MethodOptions {
|
|
rsMount.ServeHTTP(w, r)
|
|
return
|
|
}
|
|
middleware(rsMount).ServeHTTP(w, r)
|
|
})
|
|
|
|
mux.Handle("/api/rs/", protectedRSMount)
|
|
mux.Handle("/api/rs", http.RedirectHandler("/api/rs/openapi", http.StatusTemporaryRedirect))
|
|
|
|
if logger != nil {
|
|
logger.Info("resolvespec admin api enabled",
|
|
slog.String("prefix", "/api/rs"),
|
|
slog.Int("models", len(resolveSpecModels())),
|
|
)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func registerResolveSpecGuards(rs *resolvespec.Handler) {
|
|
mutableByEntity := map[string]map[string]struct{}{
|
|
"projects": {
|
|
"create": {},
|
|
"update": {},
|
|
"delete": {},
|
|
},
|
|
"thoughts": {
|
|
"create": {},
|
|
"update": {},
|
|
"delete": {},
|
|
},
|
|
"plans": {
|
|
"create": {},
|
|
"update": {},
|
|
"delete": {},
|
|
},
|
|
"learnings": {
|
|
"create": {},
|
|
"update": {},
|
|
"delete": {},
|
|
},
|
|
"agent_personas": {
|
|
"create": {},
|
|
"update": {},
|
|
"delete": {},
|
|
},
|
|
"agent_parts": {
|
|
"create": {},
|
|
"update": {},
|
|
"delete": {},
|
|
},
|
|
"agent_traits": {
|
|
"create": {},
|
|
"update": {},
|
|
"delete": {},
|
|
},
|
|
"agent_skills": {
|
|
"create": {},
|
|
"update": {},
|
|
"delete": {},
|
|
},
|
|
"agent_guardrails": {
|
|
"create": {},
|
|
"update": {},
|
|
"delete": {},
|
|
},
|
|
"stored_files": {
|
|
"update": {},
|
|
"delete": {},
|
|
},
|
|
}
|
|
|
|
rs.Hooks().Register(resolvespec.BeforeHandle, func(hookCtx *resolvespec.HookContext) error {
|
|
switch hookCtx.Operation {
|
|
case "read", "meta":
|
|
return nil
|
|
case "create", "update", "delete":
|
|
allowedOps, ok := mutableByEntity[hookCtx.Entity]
|
|
if !ok {
|
|
hookCtx.Abort = true
|
|
hookCtx.AbortCode = http.StatusForbidden
|
|
hookCtx.AbortMessage = fmt.Sprintf("operation %q is not allowed for %s.%s", hookCtx.Operation, hookCtx.Schema, hookCtx.Entity)
|
|
return fmt.Errorf("forbidden operation")
|
|
}
|
|
if _, ok := allowedOps[hookCtx.Operation]; !ok {
|
|
hookCtx.Abort = true
|
|
hookCtx.AbortCode = http.StatusForbidden
|
|
hookCtx.AbortMessage = fmt.Sprintf("operation %q is not allowed for %s.%s", hookCtx.Operation, hookCtx.Schema, hookCtx.Entity)
|
|
return fmt.Errorf("forbidden operation")
|
|
}
|
|
return nil
|
|
default:
|
|
hookCtx.Abort = true
|
|
hookCtx.AbortCode = http.StatusBadRequest
|
|
hookCtx.AbortMessage = fmt.Sprintf("unsupported operation %q", hookCtx.Operation)
|
|
return fmt.Errorf("unsupported operation")
|
|
}
|
|
})
|
|
}
|
|
|
|
type resolveSpecModel struct {
|
|
schema string
|
|
entity string
|
|
model any
|
|
}
|