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

*class*

```python
class OcxProcessError(OcxExecutionError)
```

Bases: `ocx_sdk._errors.OcxExecutionError`

Re-exported from: `ocx_sdk._errors`

An ocx process exited non-zero.

**Attributes**

- `exit_code` (`int`) — The exit status. `int` rather than `ExitCode` because a process killed by a signal exits with a code ocx never assigns, and building an error must not itself raise.
- `attempts` (`int`) — Attempts made; greater than 1 when a retry policy was active. `_retry` sets it on the final failure.
- `stdout` (`str`) — Captured stdout in full, verbatim. Never read by `_summary()` or `__str__` — that is the entire point: a partial-failure report on stdout can carry data that must not reach a log through the exception message. Never redacted either, unlike `stderr` — substituting inside it would corrupt the JSON a caller is about to parse. Read it through `partial_report(err)`, which is the sanctioned consumer; never hand the raw error or this attribute to a log sink, to a logger that serializes `__dict__`, or to a pickle crossing into a lower-trust process.

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

### ocx_sdk.OcxProcessError.attempts

*attribute* · *instance attribute*

```python
attempts: int = attempts
```

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

### ocx_sdk.OcxProcessError.exit_code

*attribute* · *instance attribute*

```python
exit_code: int = exit_code
```

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

### ocx_sdk.OcxProcessError.stdout

*attribute* · *instance attribute*

```python
stdout: str = stdout
```

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

### ocx_sdk.OcxProcessError.retryable

*property*

```python
retryable: bool
```

Whether ocx called this failure transient (exit 75).

Fixed semantics, independent of any `RetryPolicy`: this reports what
ocx said about the failure, not whether a caller chose to retry it.

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

## ocx_sdk.OcxTimeoutError

*class*

```python
class OcxTimeoutError(OcxExecutionError)
```

Bases: `ocx_sdk._errors.OcxExecutionError`

Re-exported from: `ocx_sdk._errors`

ocx did not finish within the timeout and the child was terminated.

**Attributes**

- `timeout` (`float`) — The per-attempt budget, in seconds, that expired.
- `argv` (`tuple[str, ...]`) — The argv that ran.
- `stderr` (`str`) — Whatever stderr was captured before the child was killed.

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

### ocx_sdk.OcxTimeoutError.timeout

*attribute* · *instance attribute*

```python
timeout: float = timeout
```

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