# ocx_sdk

*module*

Python SDK for [OCX](https://github.com/ocx-sh/ocx).

`ocx-sdk` drives the ocx binary rather than reimplementing it: ocx owns
resolution, verification, and the identifier grammar, and this package gives
you typed, CWD-independent handles over the commands it exposes.

```python
from ocx_sdk import Ocx, bootstrap

ocx = Ocx(exe=bootstrap.ensure())
project = ocx.project("/srv/build")
project.pull()
project.exec(["task", "verify"])
```

**This module is the API.** Everything listed in `__all__` is the stable
surface; every other module is underscored and package-private, and the one
public submodule is `ocx_sdk.bootstrap`. Reaching into an underscored path
means the next release may move it without notice — pre-1.0, breaking
changes ship without shims.

Start at `Ocx` for the runtime API and `bootstrap.ensure` for provisioning.

[View source](https://github.com/ocx-sh/ocx-sdk-python/blob/main/src/ocx_sdk/__init__.py#L1-L1)

## ocx_sdk.PackageCommands

*class* · *dataclass*

```python
class PackageCommands
```

Re-exported from: `ocx_sdk._client`

The `ocx package` command group — machine tier.

Package operations act on the `$OCX_HOME` store and its candidate and
current symlinks. They take no project path and are CWD-independent by
construction; a path appears only where the CLI itself takes one.

Every method is multi-identifier native, mirroring the CLI's `PKG...`
with one shared resolution.

[View source](https://github.com/ocx-sh/ocx-sdk-python/blob/main/src/ocx_sdk/_client.py#L1470-L3367)

### ocx_sdk.PackageCommands.claim

*method*

```python
def claim(package: str, *, repository: str, owners: Iterable[str] = (), upstream_org: str | None = None, upstream_repository_url: str | None = None, upstream_disclaimer: str | None = None, index_repo: str | None = None, forge: Forge | None = None, transport: Transport | None = None, timeout: MaybeTimeout = UNSET, retry: MaybeRetry = UNSET) -> ClaimReport
```

Claim a package in the index so its tags can be announced (C-060).

Renders the package's index entry and opens a pull or merge request.
`mutating=True` (D5): retries are off by default. Needs the same forge
credential `announce` does.

**A package that is already claimed is refused** with exit 65 and an
error envelope — not a report, and not a distinguishable subclass:
the envelope carries no `detail` for it, and the SDK never classifies
by message text. An idempotent CI step therefore reads as
`except DataError`, with `error_envelope(err)` as the machine handle
on what ocx said.

ocx's `--fork`/`--out` are not wrapped; reach them through `invoke`.

**Parameters**

- `package` (`str`) — The package to claim, as `<namespace>/<package>`.
- `repository` (`str`) — The physical OCI repository the bytes live in, as `oci://HOST/PATH` — what every later `announce` resolves tags against.
- `owners` (`Iterable[str]`) (default: `()`) — Owners as `LOGIN` or `LOGIN:ID`, in the order to record. Giving any **replaces** the detected list; the invoking identity is not added. A bare `LOGIN` is resolved against the forge's users API.
- `upstream_org` (`str | None`) (default: `None`) — The upstream organization a third-party package mirrors. Anchors the other two `upstream_*` arguments — ocx refuses either without it.
- `upstream_repository_url` (`str | None`) (default: `None`) — The upstream repository, as an absolute `http`/`https` URL with no embedded credentials.
- `upstream_disclaimer` (`str | None`) (default: `None`) — A disclaimer recorded on the index entry.
- `index_repo` (`str | None`) (default: `None`) — The index repository, as `[HOST/]NAMESPACE/PROJECT`.
- `forge` (`Forge | None`) (default: `None`) — Which forge hosts it; required for a self-hosted host.
- `transport` (`Transport | None`) (default: `None`) — `"api"` (ocx's default) or `"git"`.
- `timeout` (`MaybeTimeout`) (default: `UNSET`) — Seconds per attempt. Omitted takes the config's.
- `retry` (`MaybeRetry`) (default: `UNSET`) — Retry policy. Defaults to no retries.

**Returns**

- (`ClaimReport`) — The rendered entry: status, owners, request URL, capability checks.

**Raises**

- `AuthError` — No forge credential resolved (exit 80).
- `DataError` — The package is already claimed (exit 65).
- `UsageError` — A self-hosted `index_repo` without `forge`, or an
`upstream_*` argument without `upstream_org` (exit 64).

[View source](https://github.com/ocx-sh/ocx-sdk-python/blob/main/src/ocx_sdk/_client.py#L3138-L3210)

### ocx_sdk.PackageCommands.description_pull

*method*

```python
def description_pull(*refs: PackageLike, save_readme: str | Path | None = None, save_logo: str | Path | None = None, timeout: MaybeTimeout = UNSET, retry: MaybeRetry = UNSET) -> InfoResult
```

Show the description metadata a registry holds for packages.

**Parameters**

- `*refs` (`PackageLike`) (default: `()`) — Package repositories to query.
- `save_readme` (`str | Path | None`) (default: `None`) — Write the README to this file or directory. ocx accepts it for a single package only.
- `save_logo` (`str | Path | None`) (default: `None`) — Write the logo to this file or directory. Single package only, as above.
- `timeout` (`MaybeTimeout`) (default: `UNSET`) — Seconds per attempt. Omitted takes the config's.
- `retry` (`MaybeRetry`) (default: `UNSET`) — Retry policy. `None` opts out; omitted takes the config's.

**Returns**

- (`InfoResult`) — One entry per identifier as given, `None` where the registry
- (`InfoResult`) — holds no description metadata.

**Raises**

- `ValueError` — A save target was given for anything other than
exactly one package — ocx would refuse it, and refusing here
names which argument to drop.

[View source](https://github.com/ocx-sh/ocx-sdk-python/blob/main/src/ocx_sdk/_client.py#L1906-L1946)

### ocx_sdk.PackageCommands.deselect

*method*

```python
def deselect(*refs: PackageLike, timeout: MaybeTimeout = UNSET, retry: MaybeRetry = UNSET) -> tuple[RemovalResult, ...]
```

Drop the `current` symlink, leaving the candidates installed.

**Parameters**

- `*refs` (`PackageLike`) (default: `()`) — Package identifiers.
- `timeout` (`MaybeTimeout`) (default: `UNSET`) — Seconds per attempt. Omitted takes the config's.
- `retry` (`MaybeRetry`) (default: `UNSET`) — Retry policy. `None` opts out; omitted takes the config's.

**Returns**

- (`tuple[RemovalResult, ...]`) — One row per removal.

[View source](https://github.com/ocx-sh/ocx-sdk-python/blob/main/src/ocx_sdk/_client.py#L1577-L1594)
