Team Ai
Apppublic

openenv/echo_env

sourceHugging Faceupdated 2d agoView on Hugging Face
6likes
catalog.py122 linesDownload Raw Back to commands
1# SPDX-License-Identifier: BSD-3-Clause2 3"""Metadata catalog production and inspection. No environment is instantiated."""4 5import json6from pathlib import Path7 8import typer9from openenv.discovery import (10    build_catalog,11    CatalogError,12    load_catalog,13    resolve_entry,14    search_catalog,15    write_catalog,16)17 18app = typer.Typer(19    help="Build and inspect versioned, metadata-only environment catalogs."20)21 22 23@app.command("build")24def build(25    repository: Path = typer.Option(..., help="Local Git repository to read."),26    repository_uri: str = typer.Option(..., help="Declared GitHub HTTPS clone URI."),27    publisher: str = typer.Option(..., help="Explicit catalog publication authority."),28    output: Path = typer.Option(..., help="Destination snapshot JSON."),29    revision: str = typer.Option("HEAD", help="Source commit or ref."),30    root: str = typer.Option("envs", help="Direct-child environment inventory root."),31    namespace: str = typer.Option("openenv", help="Publisher-owned namespace."),32) -> None:33    """Build from committed metadata; uncommitted files and candidate code are not used."""34    try:35        snapshot = build_catalog(36            repository,37            repository_uri=repository_uri,38            publisher=publisher,39            namespace=namespace,40            revision=revision,41            root=root,42        )43        write_catalog(snapshot, output)44    except CatalogError as error:45        typer.echo(f"Catalog error: {error}", err=True)46        raise typer.Exit(1) from error47    typer.echo(48        f"{len(snapshot.entries)} records, snapshot {snapshot.digest}, "49        f"complete={str(snapshot.complete).lower()}",50        err=True,51    )52    for issue in snapshot.issues:53        typer.echo(f"{issue.severity}: {issue.path}: {issue.message}", err=True)54    if not snapshot.complete:55        raise typer.Exit(1)56 57 58@app.command("inspect")59def inspect(60    identifier: str = typer.Argument(..., help="Exact revision-card identifier."),61    catalog: Path = typer.Option(..., help="Complete configured snapshot."),62) -> None:63    """Return the complete selected entry without guessing or fetching a URL."""64    try:65        entry = resolve_entry(load_catalog(catalog), identifier)66    except CatalogError as error:67        typer.echo(f"Catalog error: {error}", err=True)68        raise typer.Exit(1) from error69    typer.echo(entry.model_dump_json(by_alias=True, exclude_none=True, indent=2))70 71 72def discover(73    query: str = typer.Argument("", help="Task to find an environment for."),74    catalog: Path = typer.Option(75        ..., help="Complete, explicitly configured local snapshot."76    ),77    limit: int = typer.Option(20, min=1, max=1000),78    filter: list[str] = typer.Option([], "--filter", help="Exact field=value filter."),79    as_json: bool = typer.Option(False, "--json", help="Emit complete ARD entry data."),80) -> None:81    """Find unfamiliar environments by task before any installation or execution."""82    filters = {}83    for item in filter:84        key, separator, value = item.partition("=")85        if not separator or not key or key in filters:86            typer.echo(87                "Catalog error: filters must be distinct field=value pairs", err=True88            )89            raise typer.Exit(1)90        filters[key] = value91    try:92        snapshot = load_catalog(catalog)93        matches = search_catalog(snapshot, query, filters=filters, limit=limit)94    except CatalogError as error:95        typer.echo(f"Catalog error: {error}", err=True)96        raise typer.Exit(1) from error97    if as_json:98        results = [99            {100                **match.entry.model_dump(by_alias=True, exclude_none=True, mode="json"),101                "score": match.score,102            }103            for match in matches104        ]105        typer.echo(106            json.dumps(107                {"results": results, "snapshot": snapshot.digest, "metadataOnly": True},108                indent=2,109                ensure_ascii=False,110            )111        )112        return113    for match in matches:114        card = match.entry.data115        typer.echo(f"{match.entry.display_name}: {card.description}")116        typer.echo(f"  source: {card.source.uri}")117        typer.echo(f"  path: {card.source.path}")118        typer.echo(f"  revision: {card.source.revision}")119        typer.echo(f"  license: {card.license}; artifact: {card.artifact_availability}")120        typer.echo(f"  identifier: {match.entry.identifier}")121    typer.echo("Metadata only. Execution approval remains a separate decision.")122