Add scoped identity listings and backend inventory

This commit is contained in:
2026-10-17 06:22:22 +00:00
parent 15f79ce7b1
commit c9b623b374
10 changed files with 438 additions and 21 deletions

View File

@@ -94,10 +94,27 @@ type resourceNamespaces struct {
}
type identityInventory interface {
ListBackends(context.Context) ([]string, error)
ListProfiles(context.Context) ([]string, error)
ListPrompts(context.Context) ([]domain.PromptIdentity, error)
}
// ListingFilterMode selects namespace membership for one identity listing.
type ListingFilterMode uint8
const (
ListingDefault ListingFilterMode = iota
ListingNamed
ListingUnqualified
ListingAll
)
// ListingFilter optionally overrides the namespace selected by an engine view.
type ListingFilter struct {
Mode ListingFilterMode
Name string
}
// New assembles an engine from facade-selected configuration and collaborators.
func New(cfg Config, options Options) (*Engine, error) {
defaultRetries, err := domain.ResolveTransientRetries(2, cfg.TransientRetries)
@@ -231,12 +248,99 @@ func (e *Engine) InspectProfile(ctx context.Context, id string) (*domain.Profile
return e.runner.InspectProfile(ctx, id)
}
func (e *Engine) ListProfiles(ctx context.Context) ([]string, error) {
return e.listing.ListProfiles(ctx)
func (e *Engine) ListBackends(ctx context.Context, filters ...ListingFilter) ([]string, error) {
return filterIdentityList(ctx, filters, e.namespaces.backend, e.listing.ListBackends)
}
func (e *Engine) ListPrompts(ctx context.Context) ([]domain.PromptIdentity, error) {
return e.listing.ListPrompts(ctx)
func (e *Engine) ListProfiles(ctx context.Context, filters ...ListingFilter) ([]string, error) {
return filterIdentityList(ctx, filters, e.namespaces.profile, e.listing.ListProfiles)
}
func (e *Engine) ListPrompts(ctx context.Context, filters ...ListingFilter) ([]domain.PromptIdentity, error) {
mode, namespace, err := resolveListingFilter(filters, e.namespaces.prompt)
if err != nil {
return nil, err
}
prompts, err := e.listing.ListPrompts(ctx)
if err != nil {
return nil, err
}
var filtered []domain.PromptIdentity
for _, prompt := range prompts {
if matchesListingNamespace(prompt.ID, mode, namespace) {
filtered = append(filtered, prompt)
}
}
if err := ctx.Err(); err != nil {
return nil, err
}
return filtered, nil
}
func filterIdentityList(
ctx context.Context,
filters []ListingFilter,
scope string,
list func(context.Context) ([]string, error),
) ([]string, error) {
mode, namespace, err := resolveListingFilter(filters, scope)
if err != nil {
return nil, err
}
ids, err := list(ctx)
if err != nil {
return nil, err
}
var filtered []string
for _, id := range ids {
if matchesListingNamespace(id, mode, namespace) {
filtered = append(filtered, id)
}
}
if err := ctx.Err(); err != nil {
return nil, err
}
return filtered, nil
}
func resolveListingFilter(filters []ListingFilter, scope string) (ListingFilterMode, string, error) {
if len(filters) > 1 {
return 0, "", fmt.Errorf("%w: at most one listing filter is allowed", usecase.ErrInvalidRequest)
}
filter := ListingFilter{}
if len(filters) == 1 {
filter = filters[0]
}
switch filter.Mode {
case ListingDefault:
if scope == "" {
return ListingAll, "", nil
}
return ListingNamed, scope, nil
case ListingNamed:
if !domain.IsValidResourceNamespace(filter.Name) {
return 0, "", fmt.Errorf("%w: invalid listing namespace", usecase.ErrInvalidRequest)
}
return ListingNamed, filter.Name, nil
case ListingUnqualified, ListingAll:
return filter.Mode, "", nil
default:
return 0, "", fmt.Errorf("%w: unknown listing filter mode", usecase.ErrInvalidRequest)
}
}
func matchesListingNamespace(id string, mode ListingFilterMode, namespace string) bool {
switch mode {
case ListingNamed:
idNamespace, _, qualified := strings.Cut(id, "/")
return qualified && idNamespace == namespace
case ListingUnqualified:
return !strings.Contains(id, "/")
case ListingAll:
return true
default:
return false
}
}
func (e *Engine) Prepare(ctx context.Context, request domain.RunRequest) (*domain.PreparedRun, error) {
request, err := e.qualifyRunRequest(request)