# 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.announce

*method*

```python
def announce(package: str, *, tags: Iterable[str] = (), tags_file: str | Path | None = None, tags_from_registry: bool = False, refresh: bool = False, index_repo: str | None = None, forge: Forge | None = None, transport: Transport | None = None, timeout: MaybeTimeout = UNSET, retry: MaybeRetry = UNSET) -> AnnounceReport
```

Publish a package's curated tag set into the index (C-061).

Re-observes the named tags on the registry, rebuilds the index entry
and opens — or updates — a pull or merge request against the index
repository. `mutating=True` (D5): retries are off by default. A run
that changes nothing reports `status="unchanged"` and commits nothing.

Needs a forge credential: `OcxConfig.forge_token` (or an ambient
`OCX_ANNOUNCE_TOKEN`), or the job token under `transport="git"`
inside a GitLab job. Without one ocx exits 80 before touching the
network. `HostEnv.minimal()` drops the whole CI identity ladder, so a
hermetic handle has to name the token in its config.

Exactly one tag source is required and ocx enforces it (exit 64):
`tags` replaces the curated set, the other three add to it, and they
are mutually exclusive. Nothing is checked here — clap's usage error
is the category, and it names the rule.

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

**Parameters**

- `package` (`str`) — The package to announce, as `<namespace>/<package>`.
- `tags` (`Iterable[str]`) (default: `()`) — **Replace** the curated tag set with these. A committed tag not named here is dropped; a reserved `__ocx` or legacy `sha256.<hex>` tag named here is dropped and reported.
- `tags_file` (`str | Path | None`) (default: `None`) — **Add** the tags listed in this file — the one `cascade_repair(announce_tags=...)` wrote, typically.
- `tags_from_registry` (`bool`) (default: `False`) — **Add** every tag the registry repository currently holds.
- `refresh` (`bool`) (default: `False`) — Re-observe every committed tag, picking up a digest that moved, without changing which tags are curated.
- `index_repo` (`str | None`) (default: `None`) — The index repository, as `[HOST/]NAMESPACE/PROJECT`. `None` takes ocx's default, `ocx-sh/index`.
- `forge` (`Forge | None`) (default: `None`) — Which forge hosts it. Inferred for github.com and gitlab.com; required for a self-hosted host.
- `transport` (`Transport | None`) (default: `None`) — `"api"` (ocx's default) or `"git"`, the only way a GitLab job token can open a merge request.
- `timeout` (`MaybeTimeout`) (default: `UNSET`) — Seconds per attempt. Omitted takes the config's.
- `retry` (`MaybeRetry`) (default: `UNSET`) — Retry policy. Defaults to no retries.

**Returns**

- (`AnnounceReport`) — What reached the index: status, request URL, capability checks.

**Raises**

- `AuthError` — No forge credential resolved (exit 80).
- `UsageError` — No tag source, more than one, or a self-hosted
`index_repo` without `forge` (exit 64).
- `ForgeCapabilityUnavailableError` — The forge refused a job-token
push (exit 86).

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