9dc3f7910b
CI / lint (push) Successful in 1m7s
CI / typecheck (push) Successful in 1m8s
CI / security (push) Successful in 1m7s
CI / quality (push) Successful in 41s
CI / integration_tests (push) Successful in 1m13s
CI / build (push) Successful in 1m22s
CI / unit_tests (push) Successful in 3m13s
CI / coverage (push) Successful in 3m9s
CI / status-check (push) Successful in 3s
Created comprehensive MkDocs-formatted API documentation under docs/registry/ covering all public API surfaces from the registry subsystem with real-life constructive examples: - index.md: Architecture overview, module relationships, quickstart - types.md: PackageType, PackageId, PackageReference, PackageContent - client.md: RegistryClient with all 4 endpoints, auth modes, async patterns - canonical.md: Canonicalizer pipeline — NFC, RFC-8785, SHA-1, lifecycle stripping - resolver.md: ReferenceResolver — 3 reference schemes, version alias resolution - exceptions.md: RegistryError hierarchy — all 9 typed exceptions with HTTP mapping - cache.md: RegistryCache — LRU eviction, TTL, SHA-1 tamper detection, singleflight - integration.md: 4 end-to-end workflows (ordering pipeline, email categorization, CI/CD verification, multi-tenant provisioning) Updated mkdocs.yml nav tree with docs/registry/ entries. Refs: #51
4.1 KiB
4.1 KiB
Package Registry Client
CleverActors Package Registry Subsystem — Client-Side Implementation of the Package Registry Standard v1.0.0
Overview
The Package Registry Client implements the Package Registry Standard v1.0.0 on the client side. It provides a complete, async-first library for discovering, resolving, fetching, caching, and validating versioned packages of AI components — actors, graphs, agents, templates, skills, MCP servers, and LSP services — from any compliant registry server.
Architecture
src/cleveractors/registry/
├── __init__.py Public re-exports
├── types.py Core data types (PackageType, PackageId, PackageReference, PackageContent)
├── exceptions.py Typed exception hierarchy (RegistryError + 9 subclasses)
├── client.py Async HTTP client (RegistryClient) — all 4 API endpoints
├── canonical.py Canonicalizer — RFC-8785 canonical JSON + SHA-1 hashing
├── resolver.py ReferenceResolver — parse and resolve all 3 reference schemes
├── reference_resolver.py PackageContentResolver — multi-server client pool with caching
├── cache.py RegistryCache — LRU + TTL + tamper detection with singleflight
└── local_store.py LocalPackageStore — resolve `local:<path>` references from disk
Module Relationships
PackageReference ──► ReferenceResolver ──► PackageId
│ │
│ ▼
│ RegistryClient (HTTP)
│ │
│ ▼
└──────────► PackageContentResolver
│
┌──────────┼──────────┐
▼ ▼ ▼
RegistryCache Canonicalizer LocalPackageStore
Module Map
| Module | Role |
|---|---|
| Types | PackageType, PackageId, PackageReference, PackageContent — frozen dataclasses and enums |
| Client | RegistryClient — async HTTP client implementing all 4 endpoints (§8) |
| Canonicalizer | Canonicalizer — deterministic RFC-8785 JSON, NFC normalization, SHA-1 hashing |
| Resolver | ReferenceResolver — parse all 3 reference schemes, resolve version aliases |
| Exceptions | RegistryError hierarchy — 9 typed exceptions with HTTP status mapping |
| Cache | RegistryCache — LRU eviction, TTL expiration, SHA-1 tamper detection, singleflight |
| Integration | End-to-end workflows combining multiple modules |
Quickstart
import asyncio
from cleveractors.registry import (
RegistryClient,
RegistryCache,
CacheFactory,
ReferenceResolver,
PackageReference,
)
async def main() -> None:
# 1. Create an authenticated client for your registry server
client = RegistryClient(
base_url="https://registry.example.com",
api_key="your-api-key",
)
# 2. Wrap it with transparent caching (optional but recommended)
cache = RegistryCache(client, max_size=512, ttl=600.0)
# 3. Fetch a package by its content-addressed ID
content = await cache.get_package(
"pkg_act_a1b2c3d4e5f67890abcdef1234567890abcdef"
)
print(f"Package name: {content['name']}")
# 4. Resolve a named package with version
resolved = await cache.resolve_package(
package_type="actor",
namespace="acme",
name="order-validator",
version="v1.0.0",
)
print(f"Resolved to: {resolved['package_id']}")
# 5. Parse and resolve references with the resolver
resolver = ReferenceResolver(client=client)
ref = resolver.parse("registry.example.com:acme/order-validator@latest")
package_id = await resolver.resolve(ref.original_reference)
print(f"Resolved ID: {package_id}")
await cache.close()
if __name__ == "__main__":
asyncio.run(main())