Metadata-Version: 2.3
Name: uv-launch
Version: 0.2.1
Summary: Immediate Python command startup with atomic background updates through uv
Requires-Dist: packaging>=24
Requires-Python: >=3.11
Project-URL: Repository, https://github.com/nimashoghi/uv-launch
Description-Content-Type: text/markdown

# uv-launch

Install Python commands that start immediately and update in the background. The foreground shell shim executes an already installed environment. It does not invoke uv or wait for network access.

Requires macOS or Linux, Python 3.11+, and [uv](https://docs.astral.sh/uv/). Use `uvx uv-launch` from [PyPI](https://pypi.org/project/uv-launch/) to install commands without maintaining a bootstrap environment. A stable `uv tool install uv-launch` also works. Each managed application includes its own updater, so later launches do not depend on the bootstrap interpreter. Then install commands:

```sh
uv-launch install ruff ruff
uv-launch install my-command 'package-name @ git+https://github.com/owner/project.git@main' \
  --entrypoint actual-console-script --with another-package --python 3.13
my-command --help
uv-launch update my-command
uv-launch status my-command
```

`--bin-dir` defaults to `~/.local/bin`. Use a separate directory for evaluation. The installer refuses to overwrite commands it does not own. `UV_LAUNCH_HOME` selects the installation-state directory; its default is `~/.local/share/uv-launch`.

Each invocation captures the current immutable generation and starts a detached update check. Concurrent checks for the same tool coalesce under a process lock. A check resolves all requested packages together, pinning Git dependencies to concrete revisions. If the resolved installation is unchanged, it keeps the existing environment. Otherwise it creates a new environment at its final path, checks dependencies and loads the Python entrypoint (or verifies ownership of a wheel-provided executable), then atomically switches the generation used by future launches. Virtual environments are never moved after creation, because their scripts contain absolute paths.

The launched command retains its arguments, working directory, environment and exit status. Updates never mutate its environment. An offline or failed update leaves the installed version usable. `status` reports the current generation and the last completed update result; background diagnostics are in `<UV_LAUNCH_HOME>/<command>/update.log`. Checks use the existing uv/Git authentication configuration and do not change it.

An initial installation requires dependency access. Automatic updates have a 600-second timeout per subprocess by default; configure it with `install --timeout`. Validation checks installation and entrypoint integrity, not application-specific behavior. Keep old generations while processes may still use them; this release deliberately does not automatically delete them. The updater is resolved with the application and runs from the captured generation. A successful update can therefore deliver a newer updater as well as newer application code. Existing installations created before 0.2.1 need one reinstall to adopt this behavior.

## Source toolfuncs

Install a standalone toolfunc together with its adjacent repository files:

```sh
uvx uv-launch install-source wiki https://github.com/owner/wiki scripts/wiki --prepare prepare-runtime
```

`SOURCE` must be a repository-relative PEP 723 toolfunc whose filename matches the command. `--revision` defaults to `main`; `--with`, `--python`, `--bin-dir` and `--timeout` work as for package installations. Put `--prepare` last: its remaining arguments invoke a candidate's toolfunc to prepare and validate application-specific integrations before activation.

Each check fetches one exact Git revision and resolves the script's declared dependencies together with toolfuncs and uv-launch. It validates the prepared tool and optional preparation command, then selects the complete generation atomically. Source-only changes reuse the same immutable dependency environment. Running commands keep their captured source and interpreter; failed builds and offline checks keep the last successful installation.

The foreground calls `toolfuncs run-prepared`, which loads the selected source in its already prepared environment. It does not resolve or install another environment. `managed_target` resolves a source installation to its executable source for static discovery. The source repository is delivery state; applications should keep mutable user work elsewhere.

## Service startup and companion commands

A service can require the latest successfully resolved dependencies at startup:

```sh
uv-launch run --refresh my-command -- serve --port 8080
```

Unlike the ordinary background-updating shim, `run --refresh` waits for a successful update and fails without executing stale code when that update fails. It then replaces itself with the selected command. Arguments, environment, working directory and exit status are preserved. SIGTERM during refresh terminates and reaps the resolver/build process group; after execution the application receives signals directly. Old generations remain available for workers that outlive their launcher.

Run a companion executable from the same installation with `uv-launch run --command python my-command -- -c 'print("probe")'`. Options to `run` precede the installation name. For several operations that must use exactly the same generation, capture once and pass the returned opaque `generation` value:

```sh
uv-launch select --refresh my-command
uv-launch run --generation GENERATION --command python my-command -- -c 'print("probe")'
uv-launch run --generation GENERATION my-command -- serve
```

The Python equivalents are `select_installation(name, refresh_dependencies=True)` and `execute_installation(installation, arguments, command=None)` from `uv_launch`. `execute_installation` uses `exec`, so successful execution does not return. Consumers should use these interfaces instead of reading installation symlinks or reconstructing generation paths.

## Script runtimes and Python imports

`uv_launch.dependencies` provides shared preparation for tools such as toolfuncs:

- `prepare_environment(requirements, python=..., background=True)` returns a ready immutable virtual environment. Warm calls reuse a generation without foreground resolution; first use and changed requirements wait for preparation. It does not snapshot the caller's script.
- `resolve_dependencies(requirements, python=..., constraints=(), background=True)` returns an immutable pinned requirements file. Resolutions are cached by requirements, interpreter/platform and constraints.
- `install_dependencies(requirements)` adds missing packages to the **current interpreter's environment**. It reuses satisfying installed dependencies, constrains resolution to preserve existing distributions, checks the selected graph before installation, and raises `DependencyConflictError` if preservation is impossible. It never synchronizes the whole environment or replaces existing distributions. Background resolution refresh does not install into the active interpreter or reload modules.
- `managed_target(path)` statically resolves a generated uv-launch command to its installed executable. It validates the shim against its recorded installation without executing shell code, importing the application, or requesting an update. This lets discovery tools inspect package metadata behind managed commands.

The shared resolver accepts floating Git branches and records exact commit IDs in each cached resolution. No separate script registration or checked-in lockfile is required. A process already using an installed package retains that package; a new cached resolution is not a hot reload. Use a compatible environment when requirements conflict. Each installed source distribution is identified by its recorded origin or package version via `distribution_requirement(distribution)`.

## Codex-router example

All five packages are resolved in one operation, so a moving Git ref produces one consistent suite revision:

```sh
uv-launch install codexr \
  'codex-router @ git+https://github.com/nimashoghi/codex-router.git@main#subdirectory=packages/codex-router' \
  --with 'codex-router-sdk @ git+https://github.com/nimashoghi/codex-router.git@main#subdirectory=packages/codex-router-sdk' \
  --with 'codex-monitor @ git+https://github.com/nimashoghi/codex-router.git@main#subdirectory=packages/codex-monitor' \
  --with 'codex-recovery @ git+https://github.com/nimashoghi/codex-router.git@main#subdirectory=packages/codex-recovery' \
  --with 'codex-accounts @ git+https://github.com/nimashoghi/codex-router.git@main#subdirectory=packages/codex-accounts' \
  --bin-dir ~/.local/share/codexr-evaluation/bin
```

Use that isolated command before choosing to replace an existing launcher. Updating the local codexr suite and preparing that exact suite on an SSH host remain separate responsibilities; uv-launch contains no Codex or SSH runtime logic.

## Development

```sh
uv sync
uv run pytest -q
uv run ruff check .
uv run ruff format --check .
```

Integration tests create local Git packages and exercise installation, immutable revisions, background publication, failed updates, concurrent checks, offline launches, and preservation of unrelated commands.
