Metadata-Version: 2.4
Name: ai-marketplace-cli
Version: 2.2.23
Summary: AI Marketplace Platform CLI
Requires-Python: >=3.13
Requires-Dist: ai-marketplace-framework==0.4.8
Requires-Dist: ai-marketplace-sdk==0.6.9
Requires-Dist: click==8.1.8
Requires-Dist: httpx==0.28.1
Requires-Dist: prompt-toolkit==3.0.52
Requires-Dist: pydantic==2.10.6
Requires-Dist: pyyaml==6.0.2
Requires-Dist: rich==13.9.4
Requires-Dist: typer==0.15.1
Provides-Extra: dev
Requires-Dist: mypy==1.18.2; extra == 'dev'
Requires-Dist: pytest==8.4.2; extra == 'dev'
Requires-Dist: ruff==0.13.0; extra == 'dev'
Description-Content-Type: text/markdown

# AI Marketplace CLI

Python CLI for publishing and managing marketplace models.

## Install

Requirements:

- Python 3.13+
- Docker

Install with `uv`:

```bash
uv tool install ./cli
```

Or with `pipx`:

```bash
pipx install ./cli
```

For local development:

```bash
cd cli
uv sync --extra dev
uv run ai --help
```

## Commands

### `ai`

With no arguments on an interactive terminal, opens the branded launcher.
When the current directory contains `main.py` and `pyproject.toml`, the launcher switches to
project-aware actions such as editing publish settings. Remote
marketplace model tools are grouped under a `Marketplace Models` submenu instead of appearing
as top-level home actions.

Without interactive stdin/stdout, prints help.

Launcher controls:

- Up / Down arrows to move
- `Enter` to select
- `Esc` to cancel or go back from prompt screens
- `Ctrl-C` to exit

### `ai init`

Create a single-model framework and platform project:

- `main.py`
- `internal/` (`loader.py`, `inference.py`)
- `playground.py`
- `schemas.py`
- `tests/`
- `tuning/`
- `runtime/Dockerfile`
- `.aimp/project.toml`
- `.aimp/publish.toml`
- `studio/article.mdx`
- `studio/cover.jpg`
- `pyproject.toml`
- `PROJECT.md` and `.agents/skills/`

`pyproject.toml` is the only local source of truth for project identity and version:

- `[project]` owns `name`, `version`, and `description`
- `[tool.aimp.project]` owns author metadata such as `author`

The hidden `.aimp/project.toml` also carries one CLI-managed `[publish]` section used only
for source-bundle packaging metadata:

```toml
[publish]
artifact_path = "artifacts"
artifact_format = "tar.zst"
```

This is operational publish config, not author-facing contract logic.

Remote publish always builds from **`runtime/Dockerfile`** with **`runtime/pyproject.toml`**
at the project root. The CLI does not substitute a platform-managed Dockerfile on the publish
path; those helpers exist only for tests and legacy tooling.

`internal/loader.py` holds lifecycle hooks; `main.py` defines public modes with decorated functions:

```python
from aimp_sdk import Model, mode
from internal.loader import ExampleEngine
from schemas import ExampleSchema


class Example(Model):
    engine = ExampleEngine

    @mode(name="example")
    async def example(
        self,
        inputs: ExampleSchema.Inputs,
        params: ExampleSchema.Params,
    ) -> ExampleSchema.Outputs:
        _ = params
        return ExampleSchema.Outputs(result=inputs.query.strip())
```

Examples:

```bash
ai init my-model
ai init my-model --yes
ai init my-model --json
```

`ai init` now scaffolds the default unified project immediately. The official next steps are:

```bash
cd my-model
uv sync
ai publish --configure
ai push
```

### `ai login`

Authenticate and persist credentials in `~/.aimp/config.json`.

When the Gateway URL is not passed with `--gateway`, the CLI suggests a default in this order:
`AIMP_GATEWAY_URL` → `logs/stage-runtime.json` (from `make stage-up` or Quick Tunnel) → `STAGE_GATEWAY_PUBLIC_URL` in [`infra/vm/stage.public.env`](../../../infra/vm/stage.public.env) → `http://localhost:8080`. Malformed values in `stage.public.env` fail fast with a clear error.

Examples:

```bash
ai login
ai login --gateway http://localhost:8080 --api-key pk_live_xxx
printf '%s' "$AIMP_API_KEY" | ai login --gateway http://localhost:8080 --stdin
```

### `ai auth`

Show current authentication status.

### `ai logout`

Remove stored credentials.

### `ai publish`

Package, upload, remotely build, and publish the current model.

Requirements:

- `main.py`
- `pyproject.toml`
- `.aimp/publish.toml` or a TTY session for guided configuration
- project-local `.env` (empty file allowed for zero secrets)
- `runtime/Dockerfile` and `runtime/pyproject.toml` (both required; incomplete `runtime/` is rejected; there is no publish fallback image)

Publish does not require local registry credentials or local Docker daemon access. The CLI
packages a source bundle and the platform performs the build.

For local stack development, the platform-side Build Service still needs its own registry
Docker config secret. Use `make repair-local-registry-auth` to provision or repair that
platform secret contract before retrying a failed remote publish push.

`ai publish` now owns publish setup. If `.aimp/publish.toml` is missing or invalid and the
session is interactive, the CLI fetches the platform hardware catalog from Gateway, lets you
choose tiers and developer margin, then writes the hidden config before publishing.

Publish flow:

- CLI creates a source bundle from the local build context
- CLI derives the publish version from the canonical SDK contract extracted from project code
- CLI uploads that bundle through Gateway
- the platform creates a `publish-build` execution
- Build Service builds the image inside the platform and publishes immutable outputs
- final model publish resolves artifacts from the succeeded build job only
- required runtime secret names are derived from project-local `.env` keys only

`ai publish --configure` runs that guided flow without building or publishing.

Published Python runtime packages:

- the installed `ai` CLI and project runtime pins must come from the same release line; if
  the project pins newer `ai-marketplace-framework` or `ai-marketplace-sdk` versions than
  the CLI expects, upgrade `ai-marketplace-cli` instead of downgrading the project
- `main` publishes use released `ai-marketplace-framework` and `ai-marketplace-sdk` packages from the configured index
- non-`main` publishes build local SDK/framework wheels from the checked-out branch and bundle them into the source archive
- publish preflight waits briefly for package-index propagation only when the publish mode is `published-index`
- scaffolded projects do not add local `[tool.uv.sources]` runtime path overrides, even inside this monorepo
- older generated projects that still contain runtime path sources must remove `[tool.uv.sources]`, run `uv sync`, and then retry `ai push`
- set `AIMP_RUNTIME_DEPENDENCY_SOURCE_MODE=published-index` or `bundled-local-wheels` to override branch-based defaults when needed
- `TestPyPI` and custom indexes remain advanced overrides only; the default publish path is real `PyPI`
- if you intentionally test unpublished releases from `TestPyPI` or mirror those packages in another index, configure the CLI preflight with:
  - `AIMP_PYTHON_PACKAGE_EXTRA_INDEX_URL`
  - `AIMP_PYTHON_PACKAGE_INDEX_USERNAME`
  - `AIMP_PYTHON_PACKAGE_INDEX_PASSWORD`
- for `TestPyPI`, set only:
  - `AIMP_PYTHON_PACKAGE_EXTRA_INDEX_URL=https://test.pypi.org/simple/`
- configure the Build Service with the matching platform-side settings when remote builds must use that same extra index:
  - `PYTHON_PACKAGE_EXTRA_INDEX_URL`
  - `PYTHON_PACKAGE_INDEX_USERNAME`
  - `PYTHON_PACKAGE_INDEX_PASSWORD`
- for `TestPyPI`, set only:
  - `PYTHON_PACKAGE_EXTRA_INDEX_URL=https://test.pypi.org/simple/`
- scaffolded Dockerfiles mount this index configuration as an optional BuildKit secret during
  `pip install`, so extra-index settings are available at build time without being baked into the
  final image

Platform-owned values are never edited locally:

- infra base cost comes from the platform hardware catalog
- platform fee comes from billing config
- local publish config stores only user intent such as hardware choices, article path, and margin
- registry resolution and target image naming are platform-owned

Examples:

```bash
ai publish
ai publish --configure
ai publish --parallel 4 --verbose
ai publish --python .venv/bin/python
ai push
```

### `ai fine-tune`

Fine-tune is CLI-first in V1.

- model authors enable it locally with `ai fine-tune init`
- consumers generate a schema-matched workspace with `ai fine-tune scaffold --model <slug>`
- the workspace produces `bundle.json`, and the CLI uploads that bundle through Gateway
- the dashboard remains read-only for history/detail
- successful jobs materialize a derived output model that is executed via its model slug

Commands:

- `ai fine-tune init`
- `ai fine-tune remove --yes`
- `ai fine-tune scaffold --model <slug>`
- `ai fine-tune create --model <slug> --workspace ./<slug>-fine-tune`
- `ai fine-tune list`
- `ai fine-tune get --job <job_id>`

Examples:

```bash
ai fine-tune init
ai fine-tune scaffold --model vision-pro
python vision-pro-fine-tune/prepare_bundle.py
ai fine-tune create --model vision-pro --workspace ./vision-pro-fine-tune
ai fine-tune list --json
ai fine-tune get --job 7d2a4b2c-....
```

### `ai jobs`

Jobs are read from the CLI only.

- `ai push` still performs publish/build orchestration end-to-end
- `ai jobs list` reads build and fine-tune jobs from one surface
- `ai jobs get` reads a single job by explicit type
- `ai jobs cancel` cancels a job
- `ai jobs delete` removes a terminal job from history
- `ai push` now finalizes publish from the trusted remote build job; internal artifact URIs are not shown to the user

Examples:

```bash
ai jobs list
ai jobs list --type build
ai jobs list --type fine-tune --status running
ai jobs list --model vision-pro --page 1 --page-size 20
ai jobs get --type build --job 7d2a4b2c-....
ai jobs get --type fine-tune --job 7d2a4b2c-....
ai jobs cancel --type build --job 7d2a4b2c-....
ai jobs delete --type fine-tune --job 7d2a4b2c-....
```

Interpreter precedence for contract extraction:

1. `--python`
2. active `VIRTUAL_ENV/bin/python`
3. active `VIRTUAL_ENV/Scripts/python.exe`
4. active `VIRTUAL_ENV/Scripts/python`
5. the running CLI interpreter
6. `python`
7. `python3`

Remote build troubleshooting:

- `ai publish` does not require local Docker daemon access.
- If a publish build fails, inspect it with `ai jobs get --type build --job <job_id>`.
- If the local stack is used for development, run `make dev-up` before retrying a failed publish.

### `ai model inspect`

Fetch model metadata, analytics, and rates.

Examples:

```bash
ai model inspect --slug my-model
ai model inspect --id 7d2a4b2c-....
ai model inspect --slug my-model --json
```

By default this command prints a human-readable summary.
Use `--json` for machine-readable output.

### `ai model pull`

Pull remote studio metadata into local files.

Writes:

- `.aimp/publish.toml`
- the configured article path from the current local publish config when present
- otherwise `studio/article.mdx`
- local projects also use `studio/cover.jpg` as the default cover path

Examples:

```bash
ai model pull --slug my-model
ai model pull --id 7d2a4b2c-....
```

## Auth Resolution

Authentication uses this precedence for **API keys** and **explicit gateway** values:

1. Command flags (`--gateway`, `--api-key`)
2. Environment variables (`AIMP_GATEWAY_URL`, `AIMP_API_KEY`)
3. Stored config (`~/.aimp/config.json`)

If no gateway is selected by the above (for example before the first `ai login`, or when checking `ai auth` without saved credentials), the CLI fills a default gateway URL in this order:

1. `logs/stage-runtime.json` (repo-local; written by `make stage-up` or Quick Tunnel)
2. `STAGE_GATEWAY_PUBLIC_URL` in [`infra/vm/stage.public.env`](../../../infra/vm/stage.public.env) (public, non-secret stage origin)
3. `http://localhost:8080`

The interactive `ai login` prompt uses the same default chain, except `AIMP_GATEWAY_URL` is applied before runtime state (see `ai login` above).
If `~/.aimp/config.json` still points at the retired Cloud Run stage host (`*.a.run.app`) while
the repo public stage gateway points at the VM, `ai auth` prints a warning and keeps using the
saved config until you run `ai login` again.

Only `ai login` persists credentials.

## Contract Editing

`ai contract edit` and `ai project configure` manage public modes independently.

- `--modes` accepts any non-empty comma-separated mode list
- `--mode-inputs <mode>=<csv>` and `--mode-outputs <mode>=<csv>` customize per-mode modalities
- `search` and `index` are ordinary mode names; they do not imply vector access
- vector access is declared explicitly by the Python model contract and synced into TOML resources

## Default Starter

`ai init` scaffolds a minimal example starter with:

- `main.py` for the public example mode and `Model` class
- `playground.py` for the example Playground spec
- `runtime/` for the model runtime image files

## Assistant usage

For local coding assistants, this CLI is the primary execution surface. MCP is not required for the
local-repo workflow in this phase.

- Prefer explicit non-interactive commands over the interactive launcher
- Prefer `--json` whenever the command supports it
- Use local project skills under `.agents/skills/` as the first guidance layer
- Read `pyproject.toml`, `.aimp/project.toml`, `.aimp/publish.toml`, and `main.py` before mutating the project

Recommended patterns:

```bash
ai contract validate --json
ai contract show --json
ai model inspect --slug my-model --json
ai publish --configure --json
```

## JSON Output

These commands support `--json`:

- `ai init`
- `ai login`
- `ai auth`
- `ai logout`
- `ai publish`
- `ai push`
- `ai model inspect`
- `ai model pull`
- `ai jobs list`
- `ai jobs get`
- `ai fine-tune create`
- `ai fine-tune list`
- `ai fine-tune get`

JSON success payloads use:

```json
{
  "ok": true,
  "request_id": "cli-...",
  "data": {}
}
```

JSON error payloads use:

```json
{
  "ok": false,
  "request_id": "cli-...",
  "error": {
    "code": "gateway_unreachable",
    "category": "network",
    "message": "Authentication failed: gateway unreachable",
    "retryable": true,
    "exit_code": 5
  }
}
```

Debug logs from `--debug` are written to `stderr`.

## Exit Codes

- `0`: success or expected status result
- `2`: usage or local config validation error
- `3`: authentication or permission failure
- `4`: local environment or dependency failure
- `5`: network or downstream service failure
- `6`: remote conflict, quota, or not-found state
- `130`: interrupted

## Development

```bash
cd cli
uv run ruff check .
uv run --extra dev mypy aimp_cli tests
uv run pytest
```
