catalog.md 4.0 KB

Catalog

Overview

The Catalog is the persistence layer for all agent configuration. Resources (agents, models, tools, knowledge bases) are stored as Kubernetes CR YAML files in a Git-backed store. The Catalog class is the top-level entry point; it holds one WorkspaceCatalog per workspace, each backed by a GitClient appropriate to the workspace's persistence type.

Architecture

Catalog
└── Map<workspaceId, { workspace, gitClient, catalog: WorkspaceCatalog }>

WorkspaceCatalog
├── models: ModelCatalog
├── tools: ToolCatalog
├── kbs: KBCatalog
├── documents: DocumentCatalog
├── agents: AgentCatalog
├── stacks: StackCatalog
└── secrets: SecretCatalog

*Catalog
├── list()
├── listChanges(id)    → PR history + commit log for that resource's file
├── create(entity)
├── update(entity)
└── delete(id)

Catalog is initialized by Server at startup. getWorkspaceCatalog(workspaceId) returns the WorkspaceCatalog for the requested workspace ID; route handlers call this using locals.workspace.id.

GitCatalog — generic base

GitCatalog<T> is an abstract base class that implements the full CRUD lifecycle for any entity type. Concrete subclasses (GitModelCatalog, GitToolCatalog, GitKBCatalog, GitDocumentCatalog, GitAgentCatalog) extend it, supplying entity-specific YAML parse and serialize functions.

Initialization + sync: on the first list() call, GitCatalog reads the directory tree and parses all YAML files into entity objects. After that, it also registers gitClient.onFileChange and reloads entities when matching files change on the configured branch.

Mutation flow:

create / update / delete
  → render CR YAML
  → gitClient.createFile / updateFile / deleteFile  (commits to branch)
  → gitClient.createPR(title, branch)
  → if PR is auto-merged: update in-memory list immediately

For the local persistence type, createPR returns a synthetic merged PR, so the in-memory list is always updated immediately.

ID generation: create() derives the resource ID from the entity name via slugify() (lowercase, spaces → hyphens, non-alphanumeric stripped).

Branch naming: feat/create-entity-{name}, feat/update-entity-{id}, feat/delete-entity-{id}.

Git Client

GitClient is a provider-agnostic interface with operations for reading and writing files, traversing directory trees, listing and creating pull requests, and listing commits. The normalized PR and commit types carry only the fields needed by the catalog.

LocalClient

Used when workspace.persistence.type === 'local'. Reads and writes files directly on the local filesystem using isomorphic-git for commit tracking.

  • All write operations are serialized through a promise queue to prevent concurrent commit conflicts.
  • branch parameter is ignored; all operations target the working tree.
  • listPRs returns []; createPR returns a synthetic already-merged PR so catalog mutations always take the auto-merge path.
  • Auto-initializes the git repository if .git does not exist.

GitHubClient

Used when workspace.persistence.type === 'git'. Auth via Authorization: Bearer <TOKEN>. Supports GitHub Enterprise via optional baseUrl. Uses BranchCache for all read operations.

Branch Snapshot Cache

BranchCache (used by GitHubClient) avoids per-file HTTP round-trips by downloading the entire repository as a gzip-compressed tar archive in a single request, parsing it in memory, and serving subsequent reads from the snapshot.

Refresh strategy: stale-while-revalidate (TTL: 60 s). A background refresh is triggered on the first access after the TTL elapses or after explicit invalidate(). Concurrent accesses during a refresh share the same in-flight promise and see the previous snapshot until the refresh completes.

Invalidation happens automatically when any mutation completes (invalidate after write), and lazily when the TTL elapses on the next read.