Metadata-Version: 2.5
Name: runlocal-external-interface
Version: 0.2.0
Summary: Portable Runlocal workload authoring, compilation, and validation
Author: Runlocal
Requires-Python: <3.15,>=3.8
Requires-Dist: eval-type-backport>=0.2; python_version < '3.10'
Requires-Dist: filelock<4,>=3.12
Requires-Dist: jsonschema<5,>=4.18
Requires-Dist: platformdirs<5,>=3
Requires-Dist: pydantic<3,>=2.8
Requires-Dist: referencing<1,>=0.30
Requires-Dist: rfc8785<1,>=0.1.2
Requires-Dist: typing-extensions>=4.6
Provides-Extra: onnx
Requires-Dist: onnx<2,>=1.16; extra == 'onnx'
Provides-Extra: test
Requires-Dist: build<2,>=1; extra == 'test'
Requires-Dist: pytest<9,>=7; extra == 'test'
Description-Content-Type: text/markdown

# Runlocal External Interface

Portable workload authoring, compilation, validation, and authenticated cloud storage.

```text
source JSON + local artifacts
        |
        v
 authoring/compilation -----> content/bundle (request + exact object bytes)
        |                           |
        v                           v
 request/contract <-------- interface/codec (strict JSON + schemas)
        |                           |
        v                           v
 validation/contract <---- validation/onnx
        |
        v
 response/validation -----> interface/cli (JSON results)
                                    |
                                    v
                           interface/transport
                                    |
                                    v
                         private request cloud API
```

The request and response contract modules own the portable DTOs. The codec owns
strict JSON and canonical encoding. Compilation produces a `RequestBundle`.
Validation consumes the submission and its content reader. The transport sends
only that explicit bundle and its client validation report. It does not read
private local bindings or execute customer code.

## Install and start

```sh
uv tool install --python 3.12 "runlocal-external-interface[onnx]"
rx login
rx auth status
rx guide
```

Give your coding agent this instruction:

> Read the installed guide with `rx guide`. Inspect my ML codebase and prepare
> a truthful Runlocal request. Keep private weights local and preserve shared
> parameters. Validate, review `rx upload --dry-run`, then upload the reviewed
> request to my workspace.

The guide and example are included in the installed wheel. No source checkout
is required. The long command `runlocal-external-interface` remains an alias.

```sh
rx example --out ./example
rx compile ./example/source.json --out ./request
rx upload ./request --dry-run
rx upload ./request
rx list
```

The example uses one ONNX linear operation twice with the same runtime weight
parameter. The package guides the coding agent's export; it does not provide
an automatic exporter for every ML stack or strip arbitrary model weights.

## Browser login

`rx login` opens WorkOS in your browser. Confirm the displayed device code with
the same account you use on the website. For SSH, use `rx login --no-browser`
and open the displayed URL on another device. Login progress is written to
stderr. The final stdout result is JSON and contains no tokens.

The CLI stores its session in the OS user configuration directory for
`runlocal`, outside the project. Files use private permissions on POSIX,
atomic replacement, and a process lock during refresh. Expired access tokens
are refreshed automatically. `rx auth status` checks ownership with the
backend. `rx logout` removes the local session; it does not end browser sessions.

The default API is the hosted Runlocal service. `--api-url` or
`RUNLOCAL_API_URL` selects another origin. Saved sessions are bound to their
origin. `RUNLOCAL_CONFIG_DIR` selects a separate configuration directory for
tests or isolated environments.

## Review an upload

`rx upload ./request --dry-run` validates and prepares the exact transport
without authentication or network access. It reports object sizes/digests,
local-only parameters, and embedded or external tensor findings without tensor
values. Public constants are allowed. The scan cannot prove the absence of
private or derived weight values; review every object before uploading.

## API keys for automation

Sign in to https://dropzone-eight-rust.vercel.app and create a key under
**CLI & API keys**. Keys grant upload, list, and download access to your private
workspace. They expire after 90 days and can be revoked from that page.

```sh
export RUNLOCAL_API_URL='https://veracious-bison-418.eu-west-1.convex.site'
read -s RUNLOCAL_API_TOKEN
export RUNLOCAL_API_TOKEN
rx upload ./bundle
rx list --limit 20
rx list --cursor CURSOR
rx download REQUEST_ID --out ./restored
```

`--api-url` overrides the origin environment variable. An environment API key takes precedence over browser login.
Unset RUNLOCAL_API_TOKEN to return to the saved login. HTTPS is required except on localhost. Redirects are refused.
The CLI has a 60-second request timeout. It does not retry automatically. An
identical upload is safe to repeat: the server returns the existing revision.
A changed document under the same lineage/revision fails with a conflict.

Upload runs the installed validator first. `--require-complete`, `--workload`,
`--region`, and `--no-native` have the same meaning as on `validate`. Invalid
requests do not upload. Incomplete or unsupported scopes retain those states;
without `--require-complete` they can be stored for inspection. Local-only
parameter bindings remain identifiers; their real values are never fetched.

The server checks wire shape and exact object size/digest. It retains the
client report with checked digest binding, but does not endorse its claims or
run native/semantic checks itself. No upload starts optimization.

The transport limit is 8 MiB, including base64 overhead; request JSON and the
optional client report each have a 512 KiB limit. There are at most 128 objects.
List pages contain 1–50 requests; `next_cursor: null` ends the list. Download
writes a new portable bundle directory and verifies all cataloged object bytes.
It never replaces an existing directory.

Commands default to JSON output, except the Markdown guide and use nonzero exit codes on failure.
HTTP failures retain the server code/message in an operational diagnostic.

## Publish this package

Use the manual release script from the repository root. It releases only
`runlocal-external-interface`. A Git push does not run this script.

First, build and check the release without an upload:

```sh
uv run --script packages/runlocal-external-interface/scripts/publish.py
```

To build, check, and publish to PyPI:

```sh
uv run --script packages/runlocal-external-interface/scripts/publish.py --publish
```

The script reads `pypi.password` from the repository root `.env` as a PyPI API
token. It also accepts `UV_PUBLISH_TOKEN` or `PYPI_TOKEN` in that file or in the
environment. Environment tokens take priority. `pypi.username` is not needed.
The script does not execute the `.env` file, print the token, or pass it in
command arguments. Only the upload process receives the token.

```text
package source -> source archive -> wheel
                                     |
                      install in a temporary environment
                                     |
                      metadata + CLI + package tests
                                     |
                       retained artifacts + SHA-256 list
                                     |
                          --publish -> PyPI
```

Each build has a separate directory under `dist/releases/`. The script prints
that path. It retains the wheel, source archive, and `release.json` with their
SHA-256 hashes. Old files from other builds are not uploaded. Checks run against
the installed wheel, with ONNX support and the tests from the source archive.
The extracted package source is removed before tests to prevent local imports.
Failed commands stop the release and return a nonzero exit code.

To publish a checked build or retry a partial upload, use its exact directory:

```sh
uv run --script packages/runlocal-external-interface/scripts/publish.py \
  --artifacts packages/runlocal-external-interface/dist/releases/ARTIFACT_DIRECTORY \
  --publish
```

This runs the checks again without rebuilding. The package name and version
must match `pyproject.toml`. PyPI uploads use an explicit list of two files.
Identical files already on PyPI can be skipped; conflicting files cause failure.
For a changed release, set a new version in this package's `pyproject.toml`.

The release script uses Python 3.12 or newer. `uv` manages its dependencies.
Wheel tests use Python 3.12 by default; use `--python VERSION` to select another
test interpreter. Use `--env-file PATH` to select another token file, or
`--help` to list the options. Package runtime requirements are unchanged.

After a successful release, users can install the CLI with:

```sh
uv tool install --python 3.12 "runlocal-external-interface[onnx]"
```

For the release process, see the [uv publishing guide](https://docs.astral.sh/uv/guides/package/).


## Release checks

The release script tests the installed wheel, both CLI names, packaged schemas,
the generated example, authentication failure paths, session refresh, and
upload preview. The separate PyTorch check runs with test-only dependencies:

```sh
uv run --python 3.12 --script packages/runlocal-external-interface/scripts/verify_torch_export.py \
  --rx /absolute/path/to/rx
```

It compares a small shared-weight PyTorch pipeline with its explicit-parameter
ONNX execution on CPU, then compiles and previews the request. This is a
bounded test, not a proof of fidelity for arbitrary customer models.

The Convex website repository provides `npm run test:rx-cloud` for actual
browser login, token refresh, upload, database/storage inspection, account
isolation, and dashboard checks.
