Skip to main content

rrun

PyPI Python License publish

中文文档

Agent-friendly remote execution over SSH. Run local scripts on remote machines — via stdin pipes, never via command-line arguments — so quoting, escaping, and CJK/UTF-8 encoding survive the bash → ssh → cmd/powershell journey intact. Credentials live in a local machines.json (mode 600) and never enter your agent's context.

  • Built for AI agents and humans alike: write a script file, rrun exec host file, get verbatim output plus the remote exit code — no quoting puzzles, no passwords in prompts
  • python / powershell / bash, inferred from the file extension or the remote OS
  • ssh ControlMaster multiplexing: ~0.5s on first contact, ~0.02s afterwards
  • One-command provisioning of a unified remote Python 3.12 venv (rrun setup) — the remote never touches the internet
  • Zero third-party Python dependencies

Install

pipx install rrun-cli    # provides the `rrun` command (plus a `remote-machine` alias)
# or: pip install rrun-cli

The PyPI distribution is named rrun-cli (rrun sits on PyPI's prohibited-name list as it is confusable with run); the installed command is plain rrun.

Control machine: Linux or macOS (Windows works via WSL), Python ≥ 3.10, plus ssh and sshpass:

sudo apt install sshpass                        # Debian/Ubuntu
brew install hudochenkov/sshpass/sshpass        # macOS (sshpass is not in homebrew-core)

Remote machines: an OpenSSH server with password auth enabled. Windows remotes execute via PowerShell, Mac/Linux remotes via bash.

Quickstart

Scaffold the machine inventory, fill in your machines, verify:

rrun config init                         # create ~/.rrun/machines.json from the bundled template (mode 600)
rrun config edit                         # open it in $EDITOR — replace the my-* example entries
# or: rrun config add                    # interactive wizard that appends one machine

rrun machines                            # verify the inventory is picked up (redacted)
printf 'Write-Output "hello 中文"\n' > demo.ps1
rrun exec my-win-box demo.ps1            # powershell, inferred from .ps1

That's the whole loop: write a script locally → it runs remotely → you get its output and exit code.

Why

Running commands on remote Windows machines from a POSIX shell is a minefield: sshd lands you in cmd with a GBK codepage, quotes and $ get eaten by one of the three shells along the way, and any non-ASCII argument gets mojibake'd. rrun's rules:

  • Script content (UTF-8, CJK welcome) always travels through stdin — never the command line.
  • PowerShell payloads are base64-wrapped into a single-line pure-ASCII wrapper (powershell -Command - executes stdin line-by-line; multi-line input fails silently), then decoded and invoked as a ScriptBlock remotely.
  • Command-line arguments are restricted to ASCII and passed through safely (sys.argv / $@ / $args). Put anything fancier in the script itself or in a JSON file.

Agents get a second dividend: no credential ever has to appear in a conversation, prompt, or log — the agent calls rrun exec my-host ... and the password stays inside the local machines.json. And because "write a file, then exec it" is deterministic, the agent burns zero turns on quoting debugging.

The full set of hard-won conventions and internals: docs/remote-exec-conventions.md (中文).

Use with AI agents

rrun ships an Agent Skills skill — skills/rrun (中文) — that teaches any coding agent the whole workflow: bootstrap checks, credential discipline, exec/push/pull patterns, exit codes, and the CJK/quoting rules.

Tell your agent:

Install the rrun skill from https://github.com/waqiju/rrun

or run the cross-agent installer yourself (Claude Code, Codex, Cursor, pi, and 75+ more):

npx skills add waqiju/rrun          # detects your agents; add -g for user-level
npx skills add waqiju/rrun --list   # preview without installing

pi users can alternatively install this repo as a pi package: pi install git:github.com/waqiju/rrun. Manual fallback: copy skills/rrun/ into your agent's skills directory (e.g. ~/.agents/skills/rrun/).

With the skill installed, agents also know to keep your passwords out of the conversation — when a machine is missing they will ask you to run rrun config add in your own terminal instead of asking for the password.

Subcommands

Command Purpose
rrun exec <host> <script|-c ...> Execute a local script / inline content remotely
rrun push <host> <local> <remote> Copy a file to the remote (atomic temp+rename, sha256-verified; - = stdin; parents auto-created)
rrun pull <host> <remote> <local> Copy a file from the remote (atomic, sha256-verified; - = stdout)
rrun machines [--json] List merged machines (redacted, with source)
rrun config [init|add|edit] Manage the user inventory (init scaffolds from the bundled template, add appends via wizard/flags, edit opens $EDITOR); bare config diagnoses the source chain
rrun setup <host|--all> [--force] Provision the unified remote Python 3.12 venv (idempotent)
rrun pip <host> -- list Run pip inside the remote unified venv
rrun doctor <host|--all> Health-check ssh + auth + remote python (--all opens real connections to every machine)
rrun close [<host>|--all] Close ssh ControlMaster multiplexed connections

Useful exec flags: --lang bash|powershell|python, --workdir/--env K=V (not for Windows+python), --timeout, --python <path> (skip detection), --no-mux, -q.

push/pull transfer single files (directories are rejected — tar over the pipe is the planned extension). A trailing / or an existing directory keeps the source basename; ~ expands on the remote. Design rationale and the OpenSSH-Windows stdio pitfalls behind the Windows transport: docs/push-pull-design.md.

Exit codes

Code Meaning
0–254 The remote script's own exit code, passed through unchanged
3 push/pull integrity check failed (sha256 mismatch; temp file deleted, nothing renamed)
255 ssh transport failure (unreachable / auth failure / connection dropped)
124 local --timeout expired; the local ssh client was killed

Configuration: machines.json

Credentials live in local machines.json files. rrun config init writes the bundled template to ~/.rrun/machines.json (also browsable at src/rrun/machines.template.json):

{
  "defaults": { "windows": { "os": "Windows" } },
  "machines": [
    { "name": "my-win-box", "ip": "192.168.1.10", "os": "Windows",
      "user": "admin", "password": "secret" },
    { "name": "cloud-vm", "ip": "1.2.3.4", "port": 2222, "os": "Linux",
      "user": "root", "identity_file": "~/.ssh/id_ed25519" }
  ]
}

Per-machine fields: name/ip/user are required; password (plaintext, via sshpass) or leave it empty for key-based auth (identity_file optional — the default ssh key chain / agent / ~/.ssh/config applies, with BatchMode=yes so a missing key fails fast instead of prompting); port (default 22); os (Windows / Mac / Linux); optional hostname (also resolvable), description, python (explicit remote interpreter, skips auto-detection).

Sources are merged by machine name, highest priority first (all optional, failures skipped silently):

  1. $RRUN_CONFIG (os.pathsep-separated, multiple files allowed)
  2. $REMOTE_MACHINE_CONFIG (legacy name, still honored)
  3. ./machines.json (current working directory)
  4. ~/.rrun/machines.json
  5. ~/.rrun/machines.d/*.json (sorted by filename — point each inventory at its own file/symlink; rrun config init creates this directory with a README inside)
  6. Legacy: ~/.remote-machine/machines.json (POSIX) / C:\tools\remote-machine\machines.json (Windows)

Per-file defaults sections (keyed by os, lowercased) are applied before merging. Inspect the chain with rrun config; list machines (redacted) with rrun machines.

Remote Python: unified 3.12 environment

For python scripts, rrun requires a 3.12.x interpreter on the remote, probed in order:

  1. Unified venv: C:\tools\remote-machine\venv\Scripts\python.exe / ~/.remote-machine/venv/bin/python
  2. Standalone base: ...\python312\python.exe / ~/.remote-machine/python312/bin/python3
  3. Existing installs: C:\Python\Python312\python.exe / python3.12 on PATH

If none match, run rrun setup <host>. It is idempotent and non-destructive: if the machine lacks a venv-capable Python 3.12 — none installed, or the interpreter cannot create venvs (Debian/Ubuntu split ensurepip into the separate python3.12-venv package) — a python-build-standalone tarball (Windows/macOS/Linux) is downloaded to the local cache (~/.cache/rrun/) and streamed over ssh stdin — no registry, no PATH changes, no admin rights, and the remote never touches GitHub. A half-created venv left by an interrupted earlier setup (python runs but pip is missing) is detected and recreated automatically. rrun doctor <host> reports all of these states before you provision. The venv gets its own pip.ini/pip.conf (global pip config untouched) plus the standard packages from the bundled remote-requirements.txt (override with ~/.rrun/remote-requirements.txt).

Security

  • machines.json stores plaintext passwords. Keep it local and never commit it — rrun config init/add already write the user inventory with mode 600 — or leave password empty and use key-based auth instead.
  • Key-based auth runs ssh with BatchMode=yes (no interactive prompts; a missing/unauthorized key fails fast instead of eating the script from stdin).
  • The audit log never records passwords, and --env values are logged as keys only.
  • Agent workflows: agents only ever invoke rrun <host> ..., so passwords never enter a conversation, prompt, or log. The bundled skill explicitly forbids reading credential files — see skills/rrun/SKILL.md.

Auditing & state

  • Every execution appends a JSON line to ~/.rrun/log/remote-exec.jsonl (host, lang, script sha1, exit code, duration).
  • Inline -c payloads are archived under ~/.rrun/drops/ for replay.
  • The state root defaults to ~/.rrun/ and is overridable via $RRUN_HOME.

Upgrading / uninstalling

pipx upgrade rrun-cli
pipx uninstall rrun-cli

Contributing

Issues and PRs are welcome. Development setup: clone → python3.12 -m venv .venv && .venv/bin/pip install -e ".[dev]" → hack → pytest + ruff check . → smoke-test with rrun machines. CI runs unit tests, ruff, and an end-to-end suite against a real sshd container. Releases are cut by pushing a vX.Y.Z tag; CI builds and publishes to PyPI via trusted publishing. The version lives in three places: pyproject.toml, the fallback in src/rrun/__init__.py, and package.json (agent-skill metadata) — bump all three. See CHANGELOG.md.

License

MIT

Metadata

Release files for rrun-cli 0.3.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for rrun-cli 0.3.1
File Size Uploaded
rrun_cli-0.3.1.tar.gz 56.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for rrun-cli 0.3.1
File Interpreter ABI Platform
rrun_cli-0.3.1-py3-none-any.whl Python 3 none any Details

Total release size: 104.1 kB

Release files / rrun_cli-0.3.1.tar.gz

Download URL rrun_cli-0.3.1.tar.gz
Size 56.8 kB
Tags Source
SHA-256 checksum
How to use checksums
281fb6de9ea389879f8250dc486f0a838be6d221bac3198cd0f9187b90ec90dc
BLAKE2b-256 checksum
How to use checksums
84f6806c74450b317fccae6c237abd248b6abd0b549143e8986964fe295fb5ac
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 Sep 28, 2026.

Transparency log

Release files / rrun_cli-0.3.1-py3-none-any.whl

Download URL rrun_cli-0.3.1-py3-none-any.whl
Size 47.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
f5b7e767fe4664ca6d49d10be94729885f5ab61826a22f4d74c03d89113bf59c
BLAKE2b-256 checksum
How to use checksums
649044a6410f6f7f207bd0f68d8dc45b0a0a078aec3407ab73c1db78bbed2d3c
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 Sep 28, 2026.

Transparency log

Release history Release notifications | RSS feed

0.3.2

2 release files

This release

0.3.1 This release

2 release files

0.3.0

2 release files

0.2.4

2 release files

0.2.3

2 release files

0.2.2

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page