uv-launch
Install Python commands that check for updates before execution and run from validated immutable environments. A failed check reports an error; it never silently executes the previous version. Explicit offline execution reuses a cached generation.
Requires macOS or Linux, Python 3.11+, and uv. Use uvx uv-launch from PyPI to install commands without maintaining a bootstrap environment. For a persistent manager command, use uvx uv-launch install uv-launch uv-launch; it then manages its own updates. Each managed application includes its own updater, so later launches do not depend on the bootstrap interpreter. Then install commands:
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
Directory defaults come from platformdirs: user_bin_path() for installed commands and user_data_path("uv-launch", appauthor=False) for durable installations. On macOS the data directory is ~/Library/Application Support/uv-launch; on Linux it follows XDG_DATA_HOME, normally ~/.local/share/uv-launch. Both platforms normally install commands in ~/.local/bin. --bin-dir and a nonempty UV_LAUNCH_HOME override those defaults. Use a separate directory for evaluation. The installer refuses to overwrite commands it does not own.
To adopt the platform directory after upgrading from 0.2.2 or earlier, repeat the original install or install-source command with its existing requirements and options, without a UV_LAUNCH_HOME override. This builds and validates a new installation before replacing its launcher. Keep the old directory while any process uses it; do not move its virtual environments, whose executable paths are absolute. Existing launchers retain their recorded directory until reinstalled. To operate on an old installation directly, set UV_LAUNCH_HOME to its recorded root.
Each unpinned invocation resolves current requirements before selecting its immutable generation. Checks serialize under a process lock. All requested packages resolve together, with Git dependencies pinned to concrete revisions. An unchanged resolution reuses the existing environment. Otherwise uv-launch creates an environment at its final path, checks dependencies and entrypoints, runs configured validation, and atomically activates it. 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, with a configured generation variable set to the selected ID. Updates never mutate its environment. Use uv-launch run --offline NAME or UV_LAUNCH_OFFLINE=1 NAME to explicitly reuse the installed version. UV_OFFLINE=1 is also recognized. Offline mode requires a usable existing generation. status reports the current generation and last completed update result. Checks use the existing uv/Git authentication configuration and do not change it.
Version 0.2.10 changes the default from background refresh to foreground freshness. Run an explicit update or reinstall with the new manager to migrate existing launchers, including launchers that already have discovery records. Applications using prepare_environment or resolve_dependencies must replace the old background argument with offline=True only when cached execution is intended. Explicit generation pins and caller-owned session selections continue to use their captured environments. Old generations remain available after a failed refresh.
An initial installation requires dependency access. Automatic updates have a 600-second timeout per subprocess by default; configure it with install --timeout. Without --validate, validation checks installation and entrypoint integrity only. 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.
To repair a damaged package generation, use uv-launch update my-command --rebuild, or repeat the original install command with all its options and add --rebuild before --validate. Available since 0.2.5, --rebuild creates a fresh generation even when the resolved packages and installation settings are unchanged. It performs the usual dependency, executable, entrypoint, and configured validation checks, with the same validation isolation and timeout, before atomic activation. Failures preserve the current generation, even if damaged, and report diagnostics through status as usual. Existing generations remain untouched, so pinned sessions keep their captured generation. This is a one-time request: later automatic updates still reuse unchanged generations. Resolution still checks for newer packages; rebuild does not freeze the current versions. Source installations do not support --rebuild.
Rebuild does not read the old generation's installation.json, so missing or corrupt resolution metadata can also be repaired when config.json remains intact. If termination arrives at activation, current selects either the previous generation or the fully checked candidate; cleanup retains whichever generation it references.
Package candidate validation
An application can check its packaged resources, hooks, companion commands, and startup before a new generation becomes active:
uv-launch install my-command 'package-name @ git+https://github.com/owner/project.git@main' \
--entrypoint actual-console-script --validation-timeout 30 \
--validate package-check --smoke-test
Put --validate last. It takes the name of an executable in the candidate's venv/bin followed by fixed arguments, passed directly without a shell. The executable need not have a public shim. The command and timeout are saved in the installation and apply to initial installation, reinstall, foreground updates, and background updates. --validation-timeout defaults to 60 seconds and must be finite and positive. An unchanged resolution does not run validation again unless --rebuild is requested.
When validation is configured, entrypoint import checks and the validation command run with a fresh temporary working directory and clean environment. HOME, XDG_CONFIG_HOME, XDG_DATA_HOME, XDG_STATE_HOME, XDG_CACHE_HOME, XDG_RUNTIME_DIR, XDG_CONFIG_DIRS, XDG_DATA_DIRS, TMPDIR, and UV_LAUNCH_HOME point to private temporary directories, removed after the check. Application-specific environment variables and credentials are not inherited. PATH starts with the candidate's venv/bin and retains the calling process's PATH for external tools. VIRTUAL_ENV names the candidate environment, UV_LAUNCH_GENERATION gives its absolute generation directory, and a configured --generation-env variable contains its opaque generation ID. Dependency resolution still uses the user's normal uv/Git authentication. This isolates conventional configuration and state paths; it is not an operating-system sandbox against code that deliberately accesses other paths.
A zero exit status permits the existing atomic activation. A nonzero exit, missing validation executable, or timeout rejects and removes the candidate while retaining the active generation and its shims. Timeout cleanup terminates the command's process group, allowing up to five seconds for the command to exit before forced termination. uv-launch status my-command exposes last_validation, including the attempted generation, command, returncode (null if it could not start or timed out), timed_out, checked_at, duration_seconds, and the final 8192 bytes of combined stdout/stderr decoded as UTF-8. The record is also stored in <UV_LAUNCH_HOME>/<command>/last-validation.json. A failed first validation is inspectable with status, with active and config set to null. Update failures also appear in last_update, and background failures in update.log.
Repeat the original install command with new options to enable validation for an existing installation. New package installations include uv-launch >=0.2.10,<0.3. Configuration schema 3 stores package_requirements and prevents older managers from publishing candidates that lack discovery records. Existing legacy configurations migrate under the installation lock before preparation, and the guard remains if the candidate fails. Source installations preserve their execution fields and rename the updater's repository field. Captured generations retain their installation identity.
Source toolfuncs
Install a standalone toolfunc together with its adjacent repository files:
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. Running commands keep their captured source and interpreter. Failed builds retain the last successful installation for explicit offline use.
Declare toolfuncs>=1,<2 in the script's PEP 723 dependencies, or pass --with 'toolfuncs>=1,<2', to select toolfuncs 1.x. uv-launch 0.2.9 honors explicit toolfuncs requirements, including extras, direct sources and environment markers, while retaining the minimum supported version of 0.12.2. An applicable explicit declaration removes the default upper bound; markers are evaluated against the requested Python interpreter. Scripts with no applicable toolfuncs declaration keep the historical >=0.12.2,<0.13 default.
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
Package installations can expose selected companion console scripts alongside the main command:
uv-launch install my-command package-name --entrypoint actual-console-script \
--companion package-helper --companion package-admin \
--generation-env MY_APP_GENERATION \
--validate package-check --smoke-test
Each repeatable --companion names a candidate executable and creates a shim of that name in --bin-dir. Main and companion entrypoints are checked before activation; unrelated commands are never overwritten. All shims select from the same installation, so they switch together on an update. --companion and --generation-env are package-mode options.
Reinstalling without a previously selected companion retires it only after the new candidate succeeds. If no generation variable was configured, its owned shim is removed. Otherwise the shim remains available for pinned sessions: unpinned calls report that it is retired, while an explicit pin still runs the executable from that retained generation. A retired shim continues to use the variable configured before retirement, even if the reinstall changes or omits --generation-env. Retirement records persist across updates and reinstalls; selecting the companion again restores normal dispatch. User-owned replacements are preserved, and rejected reinstalls preserve both the active generation and its shims. Retired shims and old generations can be removed manually once no sessions use them.
With --generation-env MY_APP_GENERATION, every active shim exports its captured generation ID in that variable. Child shells and companion commands inherit the pin, even if shell startup files put the global shim directory first on PATH. A nonempty pin must name an existing generation of this installation; invalid or unavailable pins fail instead of switching to the active generation. An unset or empty pin requires a successful foreground update. Explicitly pinned launches do not start an updater from the old runtime. Use a distinct application-specific variable name, such as MY_APP_GENERATION. Standard configuration and launcher variables, shell-managed names such as PPID, IFS, UID, EUID, and RANDOM, and the BASH* and COMP_* namespaces are rejected. Old generations are retained, so an update cannot retarget a running session. managed_target also honors the pin when inspecting main, companion, and retired shims; inspecting an unpinned retired shim reports retirement.
A service can require the latest successfully resolved dependencies at startup:
uv-launch run --refresh my-command -- serve --port 8080
run and ordinary shims now require this foreground check by default; --refresh remains an explicit spelling and cannot be combined with offline execution. After selection, the process replaces itself with the 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.
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:
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, offline=False) and execute_installation(installation, arguments, command=None) from uv_launch. execute_installation uses exec, so successful execution does not return. Explicit refresh_dependencies=False also captures an installed generation without refreshing. run and execute_installation export a configured generation variable for the generation they explicitly select. The opaque generation returned by select can also be assigned to the configured variable when invoking a shim.
For a session whose child commands must retain one selection per installation or dependency environment, call uv_launch.session.create_session(directory) and export the returned path as UV_LAUNCH_SESSION in its children. The first use refreshes and records an exact generation; subsequent uses share it even after another session updates the installation. The same ledger covers named applications, script environments, and dependency resolutions. Start a new ledger for a new session or stopped resume. The caller owns the directory and removes it only after its children stop. A missing ledger or selected generation is an error, not permission to silently switch versions.
PATH consumers can use uv_launch.records.launcher_record(path) to read validated installation ownership and prepare_command(path, offline=False) to refresh that owner before capturing its target. Both return None for unmanaged commands. resolve_command(path) remains a read-only operation and honors the inherited session selection. Explicit generation pins take precedence over a session selection; retired companions still require their original explicit pin.
Script runtimes and Python imports
uv_launch.dependencies provides shared preparation for tools such as toolfuncs:
prepare_environment(requirements, python=..., offline=False)checks current requirements and returns an immutable virtual environment. Unchanged resolutions reuse cached artifacts. It does not snapshot the caller's script.resolve_dependencies(requirements, python=..., constraints=(), offline=False)checks current requirements and returns an immutable pinned requirements file. Resolutions are cached by requirements, interpreter/platform and constraints.offline=Trueexplicitly reuses a prepared resolution.install_dependencies(requirements)adds missing packages to the current interpreter's environment. It reuses satisfying installed dependencies, constrains resolution to preserve every existing distribution, checks the selected graph before installation, and raisesDependencyConflictErrorif preservation is impossible. It never synchronizes the whole environment or replaces existing distributions.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:
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 ./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
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, isolated candidate validation and its failure diagnostics, companion generation pins and retirement, rebuilding damaged and unchanged generations, failed updates, concurrent checks, offline launches, and preservation of unrelated commands. A frozen copy of the 0.2.3 updater exercises updates delayed across a reinstall. Shell tests cover dash and bash when installed, including unset, empty, valid, invalid, and unavailable pins and reserved generation-variable names.
Static command discovery
uv-launch 0.2.6 writes version-1 launcher, generation, and environment records during installation and update. A shim contains a locator for its saved record; the record stores the installed shim checksum. Discovery captures the selected generation once, honors its configured generation pin, and verifies containment and installed bytes without executing the shim or recreating its template. Package environments record their actual metadata directories. Package interface and ownership checks belong to the metadata consumer, such as toolfuncs.
uv_launch.dependencies.managed_target(path) returns the captured target, and uv_launch.records.metadata_paths(target) returns the recorded metadata roots. Both are read-only and offline. Invalid records produce an explicit error; there is no executable-text inference fallback. These consistency checks trust the installer and do not authenticate packages.
Lifecycle configuration version 3 prevents older updaters from publishing generations without discovery records. Source execution fields and retained installation identities stay valid. Dependency runtime cache identities include this version, so an old process cannot overwrite a new runtime's configuration.
Normal installation or update automatically adds records to complete retained generations and migrates old owned shims. Existing running processes keep their generations. Failed candidates leave the selected generation intact. Unsupported record versions require a compatible manager; discovery never initiates repair.
Provider scripts
Use uv-launch run-script PATH [ARGS...] for ordinary Python scripts with a PEP 723 dependency block. The script runs as __main__ with its original stdin, stdout, arguments, and exit status. Dependencies refresh before execution, unless selected by an inherited session ledger or explicitly reused with run-script --offline PATH. Timed provider hooks should use an already prepared session selection; do not perform initial dependency resolution on the hook path.
A portable bootstrap shebang is #!/usr/bin/env -S uvx --from uv-launch>=0.2.6,<0.3 uv-launch run-script. Installed package commands should use uv-launch install; toolfunc source commands retain their toolfuncs shebang.
Metadata
Release files for uv-launch 0.2.10
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| uv_launch-0.2.10.tar.gz | 28.4 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| uv_launch-0.2.10-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 59.8 kB
Release files / uv_launch-0.2.10.tar.gz
| Download URL | uv_launch-0.2.10.tar.gz |
|---|---|
| Size | 28.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
5ec4b0f1d17fd4467b4eb71285549beb616ecc85045280d835fdf108eb27dfd4
|
|
BLAKE2b-256 checksum How to use checksums |
e7dfa7475fbb1ed6726ec1a5b37834882afc4c39c8afb5fdf77f9d9b96d56cbc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.
Transparency logRelease files / uv_launch-0.2.10-py3-none-any.whl
| Download URL | uv_launch-0.2.10-py3-none-any.whl |
|---|---|
| Size | 31.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2cca5972a313c8555bc2cfb9a716474b9088b0822e8b355291d236e093cbe703
|
|
BLAKE2b-256 checksum How to use checksums |
a58c9b34bb3ff50a21d92a137004e48808c6dfb2cca917e4fc3aed9fc28d3be9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.
Transparency logSecurity for uv-launch 0.2.10
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for uv_launch-0.2.10.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.