Skip to content

mine_cmd

mine_cmd

jarvis mine command group.

Classes

Functions

mine

mine() -> None

Configure and run Pearl mining.

Source code in src/openjarvis/cli/mine_cmd.py
@click.group()
def mine() -> None:
    """Configure and run Pearl mining."""

models

models() -> None

List Pearl model support status.

Source code in src/openjarvis/cli/mine_cmd.py
@mine.command("models")
def models() -> None:
    """List Pearl model support status."""
    click.echo("Pearl Mining Models")
    click.echo(
        f"{'Status':<10} {'Model':<42} {'Base model':<28} {'VRAM':<8} {'Context':<8}"
    )
    for spec in iter_pearl_model_specs():
        click.echo(
            f"{spec.status:<10} {spec.model_id:<42} {spec.base_model_id:<28} "
            f"{spec.min_vram_gb:.0f} GB   {spec.default_max_model_len:<8}"
        )
        if spec.notes:
            click.echo(f"  {spec.notes}")

doctor

doctor() -> None

Diagnose mining capability with one row per check.

Source code in src/openjarvis/cli/mine_cmd.py
@mine.command()
def doctor() -> None:
    """Diagnose mining capability with one row per check."""
    _ensure_providers_registered()
    hw = _detect_hardware()
    load_config.cache_clear()
    cfg = load_config()
    mining_cfg = cfg.mining

    click.echo("Pearl Mining Doctor")
    click.echo("Hardware")
    gpu = hw.gpu
    _row(
        "GPU vendor",
        bool(gpu and gpu.vendor == "nvidia"),
        gpu.vendor if gpu else "none",
    )
    compute_capability = gpu.compute_capability if gpu else "n/a"
    _row(
        "Compute capability",
        bool(gpu and compute_capability.startswith("9.0")),
        compute_capability,
    )
    vram_gb = gpu.vram_gb if gpu else 0.0
    _row("VRAM", vram_gb >= 70.0, f"{vram_gb:.0f} GB")

    click.echo("Docker")
    ok, info = check_docker_available()
    _row("Daemon", ok, info)

    click.echo("Disk")
    ok, info = check_disk_free(Path.home())
    _row("Free space", ok, info)

    click.echo("Pearl node")
    if mining_cfg is None:
        _row("RPC", False, "no [mining] config - run `jarvis mine init`")
    else:
        url = mining_cfg.extra.get("pearld_rpc_url", DEFAULT_PEARLD_RPC_URL)
        user = mining_cfg.extra.get("pearld_rpc_user", "rpcuser")
        password_env = mining_cfg.extra.get(
            "pearld_rpc_password_env",
            "PEARLD_RPC_PASSWORD",
        )
        ok, info = check_pearld_reachable(url, user, os.environ.get(password_env, ""))
        _row("RPC", ok, info)

    click.echo("Wallet")
    if mining_cfg is None:
        _row("Address format", False, "no [mining] config")
    else:
        ok, info = check_wallet_address_format(mining_cfg.wallet_address)
        _row("Address format", ok, info)

    click.echo("Provider capability")
    engine_id = "vllm"
    model = DEFAULT_PEARL_MODEL
    configured_provider = None
    if mining_cfg is not None:
        model = mining_cfg.extra.get("model", DEFAULT_PEARL_MODEL)
        configured_provider = mining_cfg.provider
    spec = get_pearl_model_spec(model)
    if spec is None:
        planned = pearl_variant_for_base_model(model)
        info = f"raw model; planned Pearl variant {planned}" if planned else "unknown"
        _row("Model registry", False, info)
    else:
        _row("Model registry", spec.is_validated, f"{spec.status}: {spec.model_id}")

    for provider_id, provider_cls in MinerRegistry.items():
        cap = provider_cls.detect(hw, engine_id, model)
        suffix = " (configured)" if provider_id == configured_provider else ""
        if cap.supported:
            click.echo(f"  {provider_id:<22} SUPPORTED{suffix}")
        else:
            reason = cap.reason or "unsupported"
            click.echo(f"  {provider_id:<22} UNSUPPORTED - {reason}{suffix}")

    click.echo("Session")
    sidecar = Sidecar.read(SIDECAR_PATH)
    if sidecar is None:
        click.echo("  Sidecar                absent (not running)")
    else:
        click.echo(f"  Sidecar                present ({SIDECAR_PATH})")
        click.echo(f"  Container              {sidecar.get('container_id', '?')}")

init

init(provider: str, wallet: str, pearld_url: str, pearld_user: str, pearld_password_env: str, model: str, local_model_path: Path | None, vllm_args: tuple[str, ...], image: str, cuda_visible_devices: str, gateway_host: str, gateway_port: int, gateway_metrics_port: int) -> None

Interactive setup for the v1 Pearl mining providers.

Source code in src/openjarvis/cli/mine_cmd.py
@mine.command()
@click.option(
    "--provider",
    type=click.Choice(["auto", "vllm-pearl", "cpu-pearl", "apple-mps-pearl"]),
    default="vllm-pearl",
    show_default=True,
)
@click.option(
    "--wallet",
    "--wallet-address",
    prompt="Pearl wallet address (prl1q/prl1p...)",
)
@click.option(
    "--pearld-url",
    "--pearld-rpc-url",
    default=DEFAULT_PEARLD_RPC_URL,
    prompt="pearld RPC URL",
)
@click.option(
    "--pearld-user",
    "--pearld-rpc-user",
    default="rpcuser",
    prompt="pearld RPC user",
)
@click.option(
    "--pearld-password-env",
    "--pearld-rpc-password-env",
    default="PEARLD_RPC_PASSWORD",
    prompt="env var holding pearld password",
)
@click.option("--model", default=DEFAULT_PEARL_MODEL)
@click.option(
    "--local-model-path",
    type=click.Path(file_okay=False, path_type=Path),
    default=None,
    help="Local converted Pearl checkpoint directory to mount into the container.",
)
@click.option(
    "--vllm-arg",
    "vllm_args",
    multiple=True,
    help="Extra argument to pass through to `vllm serve`; repeat as needed.",
)
@click.option("--image", default=PEARL_IMAGE_TAG)
@click.option(
    "--cuda-visible-devices",
    default="",
    help="Comma-separated NVIDIA GPU IDs to expose to the vLLM Pearl container.",
)
@click.option("--gateway-host", default="127.0.0.1", show_default=True)
@click.option("--gateway-port", default=DEFAULT_GATEWAY_RPC_PORT, show_default=True)
@click.option(
    "--gateway-metrics-port",
    "--metrics-port",
    default=DEFAULT_GATEWAY_METRICS_PORT,
    show_default=True,
)
def init(
    provider: str,
    wallet: str,
    pearld_url: str,
    pearld_user: str,
    pearld_password_env: str,
    model: str,
    local_model_path: Path | None,
    vllm_args: tuple[str, ...],
    image: str,
    cuda_visible_devices: str,
    gateway_host: str,
    gateway_port: int,
    gateway_metrics_port: int,
) -> None:
    """Interactive setup for the v1 Pearl mining providers."""
    _ensure_providers_registered()
    selected_provider = _select_provider(provider)
    if not MinerRegistry.contains(selected_provider):
        raise click.ClickException(f"Unknown mining provider: {selected_provider}")

    ok, info = check_wallet_address_format(wallet)
    if not ok:
        raise click.ClickException(f"Invalid wallet address: {info}")

    if selected_provider == "vllm-pearl":
        hw = _detect_hardware()
        if local_model_path is None:
            cap = detect_for_engine_model(
                hw=hw,
                engine_id="vllm",
                model=model,
                provider_id="vllm-pearl",
            )
            if not cap.supported:
                raise click.ClickException(
                    f"vllm-pearl not supported on this host: {cap.reason}\n"
                    "See `jarvis mine models` and `jarvis mine doctor` for details."
                )
        elif not local_model_path.exists():
            raise click.ClickException(
                f"local Pearl model path does not exist: {local_model_path}"
            )
        model_spec = get_pearl_model_spec(model)
        max_model_len = (
            model_spec.default_max_model_len if model_spec is not None else 8192
        )
        gpu_memory_utilization = (
            model_spec.default_gpu_memory_utilization
            if model_spec is not None
            else 0.96
        )

        ok, info = check_docker_available()
        if not ok:
            raise click.ClickException(f"Docker unavailable: {info}")

        ok, info = check_disk_free(Path.home())
        if not ok:
            raise click.ClickException(f"Insufficient disk: {info}")
    else:
        max_model_len = 8192
        gpu_memory_utilization = 0.96

    if pearld_password_env not in os.environ:
        click.echo(
            f"Warning: ${pearld_password_env} is not set. "
            "Set it before `jarvis mine start`.",
            err=True,
        )

    from openjarvis.core.config import DEFAULT_CONFIG_PATH

    config_path = Path(os.environ.get("OPENJARVIS_CONFIG", DEFAULT_CONFIG_PATH))
    config_path.parent.mkdir(parents=True, exist_ok=True)
    section = f"""
[mining]
provider = "vllm-pearl"
wallet_address = "{wallet}"
submit_target = "solo"
fee_bps = 0
fee_payout_address = ""

[mining.extra]
docker_image_tag = "{image}"
model = "{model}"
cuda_visible_devices = "{cuda_visible_devices.strip()}"
gateway_port = {gateway_port}
gateway_metrics_port = {gateway_metrics_port}
vllm_port = 8000
gpu_memory_utilization = {gpu_memory_utilization}
max_model_len = {max_model_len}
pearld_rpc_url = "{pearld_url}"
pearld_rpc_user = "{pearld_user}"
pearld_rpc_password_env = "{pearld_password_env}"
hf_token_env = "HF_TOKEN"
"""
    if local_model_path is not None:
        path = local_model_path.expanduser().resolve()
        section += f'local_model_path = "{path}"\n'
    if vllm_args:
        encoded_args = ", ".join(json.dumps(arg) for arg in vllm_args)
        section += f"vllm_args = [{encoded_args}]\n"
    if selected_provider != "vllm-pearl":
        section = f"""
[mining]
provider = "{selected_provider}"
wallet_address = "{wallet}"
submit_target = "solo"
fee_bps = 0
fee_payout_address = ""

[mining.extra]
gateway_host = "{gateway_host}"
gateway_port = {gateway_port}
metrics_port = {gateway_metrics_port}
pearld_rpc_url = "{pearld_url}"
pearld_rpc_user = "{pearld_user}"
pearld_rpc_password_env = "{pearld_password_env}"
"""
    if config_path.exists():
        existing = config_path.read_text()
        if "[mining]" in existing:
            click.echo("[mining] section already present; not overwriting.")
            return
        config_path.write_text(existing.rstrip() + "\n" + section)
    else:
        config_path.write_text(section.lstrip())
    load_config.cache_clear()

    if selected_provider == "vllm-pearl":
        click.echo(f"Resolving image {image}...")
        PearlDockerLauncher(client=_docker_from_env()).ensure_image(image)
    click.echo("Done. Run `jarvis mine start` to begin mining.")

start

start() -> None

Launch the configured mining provider.

Source code in src/openjarvis/cli/mine_cmd.py
@mine.command()
def start() -> None:
    """Launch the configured mining provider."""
    _ensure_providers_registered()
    load_config.cache_clear()
    cfg = load_config().mining
    if cfg is None:
        raise click.ClickException(
            "no [mining] section in config - run `jarvis mine init`"
        )
    provider = MinerRegistry.get(cfg.provider)()
    asyncio.run(provider.start(cfg))
    click.echo(f"Started {cfg.provider}. Run `jarvis mine status` for live stats.")

stop

stop() -> None

Stop the configured mining provider.

Source code in src/openjarvis/cli/mine_cmd.py
@mine.command()
def stop() -> None:
    """Stop the configured mining provider."""
    _ensure_providers_registered()
    sidecar = Sidecar.read(SIDECAR_PATH)
    if sidecar and (sidecar.get("gateway_pid") or sidecar.get("miner_loop_pid")):
        _terminate_pid(sidecar.get("miner_loop_pid"), grace_seconds=2.0)
        _terminate_pid(sidecar.get("gateway_pid"), grace_seconds=5.0)
        Sidecar.remove(SIDECAR_PATH)
        click.echo("Mining stopped.")
        return
    load_config.cache_clear()
    cfg = load_config().mining
    if cfg is None:
        click.echo("no [mining] section - nothing to stop")
        return
    provider = MinerRegistry.get(cfg.provider)()
    asyncio.run(provider.stop())
    click.echo("Mining stopped.")

status

status() -> None

Print live mining stats.

Source code in src/openjarvis/cli/mine_cmd.py
@mine.command()
def status() -> None:
    """Print live mining stats."""
    _ensure_providers_registered()
    sidecar = Sidecar.read(SIDECAR_PATH)
    if sidecar is not None:
        provider_id = str(sidecar.get("provider", "unknown"))
        click.echo(f"provider:           {provider_id}")
        if "gateway_pid" in sidecar:
            gateway_pid = sidecar.get("gateway_pid")
            miner_pid = sidecar.get("miner_loop_pid")
            click.echo(
                f"gateway pid:        {gateway_pid} "
                f"({'alive' if _pid_alive(gateway_pid) else 'dead'})"
            )
            click.echo(
                f"miner pid:          {miner_pid} "
                f"({'alive' if _pid_alive(miner_pid) else 'dead'})"
            )
        metrics_url = sidecar.get("metrics_url")
        if metrics_url:
            stats, error = _stats_from_metrics_url(str(metrics_url), provider_id)
            if error:
                click.echo(f"metrics error:      {error}")
                return
            if stats is not None:
                click.echo(f"Shares submitted:   {stats.shares_submitted}")
                click.echo(f"Shares accepted:    {stats.shares_accepted}")
                click.echo(f"Blocks found:       {stats.blocks_found}")
                return

    load_config.cache_clear()
    cfg = load_config().mining
    if cfg is None:
        click.echo("No active mining session")
        return
    provider = MinerRegistry.get(cfg.provider)()
    stats = provider.stats()
    click.echo(f"provider:           {stats.provider_id}")
    click.echo(f"shares submitted:   {stats.shares_submitted}")
    click.echo(f"shares accepted:    {stats.shares_accepted}")
    click.echo(f"blocks found:       {stats.blocks_found}")
    click.echo(f"hashrate:           {stats.hashrate:.2f}")
    click.echo(f"uptime (s):         {stats.uptime_seconds:.0f}")
    click.echo(f"last share at:      {stats.last_share_at or '-'}")
    click.echo(f"last error:         {stats.last_error or '-'}")
    click.echo(f"payout target:      {stats.payout_target}")
    click.echo(f"fees owed:          {stats.fees_owed}")

validate_model

validate_model(model: str | None, vllm_endpoint: str | None, gateway_metrics_url: str | None, allow_planned: bool, prompt: str | None, output: Path | None, timeout: float) -> None

Collect runtime evidence for Pearl model validation.

Source code in src/openjarvis/cli/mine_cmd.py
@mine.command("validate-model")
@click.option("--model", default=None, help="Pearl model id to validate.")
@click.option(
    "--vllm-endpoint",
    default=None,
    help="OpenAI-compatible vLLM endpoint, defaults to the mining sidecar.",
)
@click.option(
    "--gateway-metrics-url",
    default=None,
    help="Pearl gateway metrics URL, defaults to the mining sidecar.",
)
@click.option(
    "--allow-planned",
    is_flag=True,
    default=False,
    help="Allow planned models while collecting validation evidence.",
)
@click.option(
    "--prompt",
    default=None,
    help="Optional chat-completion smoke prompt to run through vLLM.",
)
@click.option(
    "--output",
    type=click.Path(dir_okay=False, path_type=Path),
    default=None,
    help="Write a JSON validation artifact.",
)
@click.option("--timeout", default=10.0, show_default=True, type=float)
def validate_model(
    model: str | None,
    vllm_endpoint: str | None,
    gateway_metrics_url: str | None,
    allow_planned: bool,
    prompt: str | None,
    output: Path | None,
    timeout: float,
) -> None:
    """Collect runtime evidence for Pearl model validation."""
    sidecar = Sidecar.read(SIDECAR_PATH)
    sidecar_model = sidecar.get("model") if sidecar else None
    model_id = model or sidecar_model or DEFAULT_PEARL_MODEL
    endpoint = vllm_endpoint or (sidecar.get("vllm_endpoint") if sidecar else None)
    metrics_url = gateway_metrics_url or (
        sidecar.get("gateway_metrics_url") if sidecar else None
    )

    click.echo("Pearl Model Validation")
    click.echo(f"model:              {model_id}")
    results: list[bool] = []
    records: list[dict[str, Any]] = []

    def record(name: str, ok: bool, info: str) -> None:
        records.append({"name": name, "ok": ok, "info": info})
        _validation_row(results, name, ok, info)

    spec = get_pearl_model_spec(str(model_id))
    if spec is None:
        planned = pearl_variant_for_base_model(str(model_id))
        info = f"raw model; planned Pearl variant {planned}" if planned else "unknown"
        record("Model registry", False, info)
    elif spec.is_validated:
        record("Model registry", True, f"validated: {spec.model_id}")
    else:
        record(
            "Model registry",
            allow_planned,
            f"{spec.status}: {spec.model_id}",
        )

    if sidecar is None:
        record(
            "Sidecar",
            False,
            "absent - run `jarvis mine start` first",
        )
    else:
        record("Sidecar", True, f"present ({SIDECAR_PATH})")
        record(
            "Sidecar model",
            sidecar_model == model_id,
            str(sidecar_model or "missing"),
        )

    if endpoint is None:
        record("vLLM endpoint", False, "missing")
    else:
        endpoint = endpoint.rstrip("/")
        try:
            models_payload = _get_json(f"{endpoint}/models", timeout=timeout)
            model_ids = _extract_model_ids(models_payload)
            models_info = (
                str(model_id) if str(model_id) in model_ids else f"{len(model_ids)} ids"
            )
            record(
                "vLLM /models",
                str(model_id) in model_ids,
                models_info,
            )
        except Exception as exc:  # noqa: BLE001
            record("vLLM /models", False, str(exc).splitlines()[0])

        if prompt:
            try:
                payload = {
                    "model": model_id,
                    "messages": [{"role": "user", "content": prompt}],
                    "max_tokens": 16,
                    "stream": False,
                }
                response = _post_json(
                    f"{endpoint}/chat/completions",
                    payload,
                    timeout=timeout,
                )
                choices = response.get("choices")
                record(
                    "Chat completion",
                    bool(choices),
                    "choices returned" if choices else "no choices",
                )
            except Exception as exc:  # noqa: BLE001
                record(
                    "Chat completion",
                    False,
                    str(exc).splitlines()[0],
                )
        else:
            click.echo("  Chat completion        skipped (pass --prompt to run)")

    metrics_candidates: list[tuple[str, str]] = []
    if metrics_url is not None:
        gateway_metrics_url = str(metrics_url).rstrip("/")
        if not gateway_metrics_url.endswith("/metrics"):
            gateway_metrics_url = f"{gateway_metrics_url}/metrics"
        metrics_candidates.append(("gateway", gateway_metrics_url))
        metrics_url = gateway_metrics_url
    if endpoint is not None:
        vllm_metrics_url = str(endpoint).rstrip("/").removesuffix("/v1") + "/metrics"
        if not any(url == vllm_metrics_url for _, url in metrics_candidates):
            metrics_candidates.append(("vLLM", vllm_metrics_url))

    if not metrics_candidates:
        record("Gateway metrics", False, "missing")
    else:
        errors: list[str] = []
        for label, candidate_url in metrics_candidates:
            stats, error = _stats_from_metrics_url(candidate_url, "vllm-pearl")
            if error:
                errors.append(f"{label}: {error}")
                continue
            submitted = stats.shares_submitted if stats is not None else 0
            accepted = stats.shares_accepted if stats is not None else 0
            record(
                "Gateway metrics",
                True,
                f"{label} submitted={submitted} accepted={accepted}",
            )
            break
        else:
            record("Gateway metrics", False, " | ".join(errors))

    passed = bool(results) and all(results)
    if output is not None:
        output.parent.mkdir(parents=True, exist_ok=True)
        artifact = {
            "schema_version": 1,
            "model": str(model_id),
            "status": "passed" if passed else "failed",
            "allow_planned": allow_planned,
            "prompt_ran": bool(prompt),
            "vllm_endpoint": endpoint,
            "gateway_metrics_url": metrics_url,
            "sidecar_path": str(SIDECAR_PATH),
            "checks": records,
        }
        output.write_text(json.dumps(artifact, indent=2, sort_keys=True) + "\n")
        click.echo(f"Validation artifact written to {output}")

    if not passed:
        raise click.ClickException("Pearl model validation checks failed.")
    click.echo("Validation checks passed.")

inspect_model

inspect_model(model: str, allow_planned: bool, hf_token_env: str, output: Path | None, timeout: float) -> None

Inspect a Pearl model artifact before spending GPU time.

Source code in src/openjarvis/cli/mine_cmd.py
@mine.command("inspect-model")
@click.option("--model", default=DEFAULT_PEARL_MODEL, help="Pearl model id to inspect.")
@click.option(
    "--allow-planned",
    is_flag=True,
    default=False,
    help="Inspect planned models even though they are not enabled for mining yet.",
)
@click.option(
    "--hf-token-env",
    default="HF_TOKEN",
    show_default=True,
    help="Environment variable containing a Hugging Face token.",
)
@click.option(
    "--output",
    type=click.Path(dir_okay=False, path_type=Path),
    default=None,
    help="Write a JSON artifact inspection report.",
)
@click.option("--timeout", default=10.0, show_default=True, type=float)
def inspect_model(
    model: str,
    allow_planned: bool,
    hf_token_env: str,
    output: Path | None,
    timeout: float,
) -> None:
    """Inspect a Pearl model artifact before spending GPU time."""

    click.echo("Pearl Model Artifact Inspection")
    click.echo(f"model:              {model}")
    results: list[bool] = []
    records: list[dict[str, Any]] = []
    local_path = Path(model)
    is_local = local_path.exists()

    def record(name: str, ok: bool, info: str) -> None:
        records.append({"name": name, "ok": ok, "info": info})
        _validation_row(results, name, ok, info)

    spec = None if is_local else get_pearl_model_spec(model)
    if is_local:
        record("Model registry", True, "local artifact (not registered)")
    elif spec is None:
        planned = pearl_variant_for_base_model(model)
        info = f"raw model; planned Pearl variant {planned}" if planned else "unknown"
        record("Model registry", False, info)
    elif spec.is_validated:
        record("Model registry", True, f"validated: {spec.model_id}")
    else:
        record("Model registry", allow_planned, f"{spec.status}: {spec.model_id}")

    token = os.environ.get(hf_token_env, "")
    model_payload: Any = None
    paths: set[str] = set()
    if is_local:
        try:
            model_payload, paths = _local_artifact_payload_and_paths(local_path)
            record("Local artifact", True, str(local_path))
        except Exception as exc:  # noqa: BLE001
            record("Local artifact", False, str(exc).splitlines()[0])
    else:
        try:
            model_payload = _hf_json(
                _hf_model_path(model, ""),
                token=token,
                timeout=timeout,
            )
            record("HF model", True, "accessible")
        except urllib.error.HTTPError as exc:
            info = f"HTTP {exc.code}"
            if exc.code in {401, 403} and not token:
                info += f"; set ${hf_token_env} for gated/private models"
            record("HF model", False, info)
        except Exception as exc:  # noqa: BLE001
            record("HF model", False, str(exc).splitlines()[0])

    if model_payload is not None:
        quant_method = _artifact_quant_method(model_payload)
        record(
            "Pearl quantization",
            quant_method == "pearl",
            quant_method or "missing quantization_config.quant_method",
        )

        if is_local:
            record("File list", bool(paths), f"{len(paths)} files")
        else:
            try:
                tree_payload = _hf_json(
                    _hf_model_path(model, "/tree/main?recursive=false"),
                    token=token,
                    timeout=timeout,
                )
                paths = _artifact_file_paths(tree_payload)
                record("HF file list", bool(paths), f"{len(paths)} files")
            except Exception as exc:  # noqa: BLE001
                record("HF file list", False, str(exc).splitlines()[0])

    if paths:
        has_config = "config.json" in paths
        record(
            "config.json",
            has_config,
            "present" if has_config else "missing",
        )
        tokenizer_ok = bool({"tokenizer.json", "tokenizer_config.json"} & paths)
        record(
            "Tokenizer metadata",
            tokenizer_ok,
            "present"
            if tokenizer_ok
            else "missing tokenizer.json/tokenizer_config.json",
        )
        has_weights = _artifact_has_weights(paths)
        record(
            "Weights",
            has_weights,
            "present" if has_weights else "missing",
        )

        architectures = _artifact_architectures(model_payload)
        is_gemma4 = any("Gemma4" in arch for arch in architectures)
        if is_gemma4:
            missing = [
                name
                for name in ("processor_config.json", "preprocessor_config.json")
                if name not in paths
            ]
            record(
                "Gemma4 processor metadata",
                not missing,
                "present" if not missing else "missing " + ", ".join(missing),
            )

    passed = bool(results) and all(results)
    if output is not None:
        output.parent.mkdir(parents=True, exist_ok=True)
        artifact = {
            "schema_version": 1,
            "model": model,
            "status": "passed" if passed else "failed",
            "allow_planned": allow_planned,
            "hf_token_env": hf_token_env,
            "checks": records,
        }
        output.write_text(json.dumps(artifact, indent=2, sort_keys=True) + "\n")
        click.echo(f"Inspection artifact written to {output}")

    if not passed:
        raise click.ClickException("Pearl model artifact inspection failed.")
    click.echo("Artifact inspection passed.")

attach

attach(vllm_endpoint: str, gateway_url: str, gateway_metrics_url: str, model: str, container_id: str, wallet: str) -> None

Manual mode: write a sidecar for an externally-started miner.

Source code in src/openjarvis/cli/mine_cmd.py
@mine.command()
@click.option("--vllm-endpoint", required=True)
@click.option("--gateway-url", required=True)
@click.option("--gateway-metrics-url", required=True)
@click.option("--model", default=DEFAULT_PEARL_MODEL)
@click.option("--container-id", default="external")
@click.option("--wallet", default="")
def attach(
    vllm_endpoint: str,
    gateway_url: str,
    gateway_metrics_url: str,
    model: str,
    container_id: str,
    wallet: str,
) -> None:
    """Manual mode: write a sidecar for an externally-started miner."""
    Sidecar.write(
        SIDECAR_PATH,
        {
            "provider": "vllm-pearl",
            "vllm_endpoint": vllm_endpoint,
            "model": model,
            "gateway_url": gateway_url,
            "gateway_metrics_url": gateway_metrics_url,
            "container_id": container_id,
            "wallet_address": wallet,
            "started_at": int(time.time()),
        },
    )
    click.echo(f"Sidecar written to {SIDECAR_PATH}")

logs

logs(tail_n: int, follow: bool) -> None

Print the Pearl miner container log tail.

Source code in src/openjarvis/cli/mine_cmd.py
@mine.command()
@click.option("-n", "--tail", "tail_n", default=200, type=int)
@click.option("-f", "--follow", is_flag=True, default=False)
def logs(tail_n: int, follow: bool) -> None:
    """Print the Pearl miner container log tail."""
    if follow:
        click.echo("note: -f follow is not implemented in v1; printing tail", err=True)
    client = _docker_from_env()
    launcher = PearlDockerLauncher(client=client)
    try:
        launcher._container = client.containers.get("openjarvis-pearl-miner")
    except Exception as exc:  # noqa: BLE001
        raise click.ClickException(f"no mining container: {exc}") from exc
    click.echo(launcher.get_logs(tail=tail_n))