Skip to main content

Module cli_install

Module cli_install 

Source
Expand description

Where provider CLIs (claude, codex, …) are installed, shared by every resolver: resolvecli, install.start / install.check, agent.open’s launch path and the picker’s read-only lookup.

Shared, keyed by pinned CLI version: <shared_dir>/cli/<provider>/<pinned_version>/. The install used to live in <home>/instances/v<AGENTMUX_VERSION>/cli/<provider>/, i.e. inside the AgentMux version dir, so every upgrade, portable or dev build started with no CLI and the first open of each provider ran a blocking npm install of the very same pinned CLI (~3 s, 16 times in a week of logs — SPEC_AGENT_OPEN_LATENCY_2026_09_27.md F1). The CLI’s identity is its pinned version, not AgentMux’s, so that is the key now.

Legacy fallback (read-only): an existing per-version install still resolves, so nothing breaks for a version that already has one; new installs always go to the shared dir.

Cross-instance safety: the shared dir is written by every AgentMux instance on the machine, so installs into it serialize on an exclusive lock file (<provider>/<pinned_version>.lock, blocking, both platforms — the same primitive the agent registry uses) and re-check for a finished install after acquiring it, so a second instance waits and then reuses the first one’s result instead of running npm into the same directory. A shared dir only counts as installed once COMPLETE_MARKER is written, after npm succeeded and the shim was verified; an unmarked dir (a failed or in-progress install), or a marked one whose shim is gone, is cleared — marker and all — before the next attempt. install.start waits for the lock with try_lock_install so the wait stays cancellable.

Structs§

InstallGuard
Held for the duration of an install into a shared dir; dropping it releases the lock. None inside for the legacy (per-instance) dir, which needs no cross-instance lock.
NpmInstallRequest
What to install: npm_package@pinned_version, whose shim is cli_command.
NpmRun
What npm install produced.

Enums§

EnsureError
Why ensure_installed did not hand back a CLI.
InstallError
InstallOutcome

Constants§

AGENTMUX_VERSION 🔒
COMPLETE_MARKER
Written into a shared install dir only after npm succeeded and the shim was verified, under the install lock. npm creates .bin shims before its lifecycle scripts and the rest of the install finish, so a shim alone doesn’t mean “installed” — a reader could otherwise run a CLI another instance is still installing, and a failed install’s leftover shim would poison the shared cache for every later AgentMux version (Codex P2 on #3927).
WARM_INSTALL_DELAY
How long after srv start the warm-up begins: after the first IPC traffic, well before most users open an agent.

Functions§

clear_unless_valid
Under the install lock, before (re)installing into dir: unless it holds a valid install, remove it entirely — leftovers of a failed or interrupted install, and a stale completion marker whose shim is gone. Removing the marker is the point: a repair must not run with the old marker visible, or readers would accept the half-repaired install as soon as npm recreates the shim, and a failed repair would leave marker + shim accepted indefinitely (Codex P2 on #3927, second pass). No-op for a missing or valid dir.
ensure_installed
ensure_installed_with running the real npm install.
ensure_installed_with
The pinned CLI for a provider whose install is missing: install it, waiting up to wait. install is injected so tests don’t need npm.
find_installed
The installed CLI binary, if any: the shared dir (only once marked complete), then the legacy per-version dir (which predates the marker, so its shim is enough).
find_installed_for_provider
Same, with the pinned version taken from the backend provider registry (the source of truth) rather than a caller-supplied string.
install_dir
The directory new installs go to: the shared dir when the provider is pinned, else the legacy one.
install_pinned_cli
Install req into its install dir, blocking — call it from spawn_blocking or a plain thread. The one install routine for resolvecli and the startup warm-up (warm_used_providers):
install_pinned_cli_with
install_pinned_cli with the npm runner injected (tests).
is_complete
Whether dir holds a finished, verified install.
is_safe_provider_component 🔒
Same rule for the provider id, which is also a path component.
is_safe_version_component
A pinned version string is used as a path component, so it must be a plain version: letters, digits and ., -, _, +, no .., bounded. Anything else (including empty — an unpinned provider) has no shared dir and falls back to the legacy per-version location.
is_valid_install
Whether dir holds a usable install: marked complete AND its shim is still there. A marker alone isn’t enough — a finished install can lose its shim later (corruption, antivirus cleanup).
legacy_cli_dir
The pre-2026-09-27 per-AgentMux-version location, kept for read-only fallback and for unpinned providers.
lock_install
Take the cross-instance install lock for dir when it is a shared dir. Blocks until any other instance’s install into the same dir finishes — call it from a blocking context (spawn_blocking). The lock file sits next to the dir and is never deleted (deleting a locked file races waiters onto a different inode; see registry::leases::CriticalSection).
mark_complete
Mark dir complete (write-then-rename, so a reader never sees a torn marker). Call under the install lock, after verifying the shim.
npm_bin
<dir>/node_modules/.bin/<cli> — .cmd on Windows, where npm writes a batch shim.
npm_registry_args 🔒
Registry flags for installing package. AgentMux’s own packages (@agentmuxai/*) are published to the public npm registry, but a machine can route that scope elsewhere in its .npmrc (a @agentmuxai:registry=https://npm.pkg.github.com line, for internal packages), and the install then 404s. The flag pins the scope for this one command only; other packages use the user’s config as before.
open_lock_file 🔒
The shared dir’s lock file, opened (created if missing); None for a provider/pin with no shared dir.
providers_needing_install
The npm-installed providers among provider_ids whose pinned CLI has no install yet, deduplicated, in first-seen order. Pure over the registry and the filesystem.
run_npm_install 🔒
Run npm install --prefix <dir> <package> to completion. package is one or more space-separated name@version specs.
shared_cli_dir
<shared_dir>/cli/<provider>/<pinned_version>/, or None when either component isn’t safe to use as a path segment.
try_lock_install
Non-blocking lock_install: Ok(None) while another instance holds the lock. For waiters that must stay cancellable (install.start polls this and races install.cancel, Codex P2 on #3927).
warm_used_providers
SPEC_AGENT_OPEN_LATENCY_2026_09_27.md §4.1: install the pinned CLI of every provider the user’s agents use, in the background, so the first open after a pin bump doesn’t wait on npm. Only providers in provider_ids (the user’s own agent definitions — not every template’s), only pins with no install yet, one at a time, npm below normal priority. An open that arrives mid-install waits on the same lock and then reuses the result. Failures (no npm, offline) are logged and left for the open to report.