mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-13 13:46:33 +00:00
c179bdab3c
The Termux update path (PR #22814) prebuilds psutil from a marker-patched sdist so 'platform android is not supported' doesn't kill it. The same psutil setup.py error blocks fresh installs via scripts/install.sh — only the update path was wired up. Without this, a brand-new Termux user can't get past the very first 'pip install -e .[termux-all]' call. - New scripts/install_psutil_android.py — standalone version of the same patcher hermes_cli/main.py uses, callable from bash. - scripts/install.sh detects sys.platform == 'android' and runs the patcher before pip install. - TODO note added to both copies pointing at upstream https://github.com/giampaolo/psutil/pull/2762; remove both when that ships. Note: we keep psutil as a base dep on Android (do not adopt the proposed sys_platform != 'android' marker in pyproject). Removing it would crash five unguarded 'import psutil' sites at runtime (tools/code_execution_tool.py, tools/tts_tool.py, tools/process_registry.py (2x), gateway/platforms/whatsapp.py).
118 lines
3.8 KiB
Python
Executable File
118 lines
3.8 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Install psutil on Termux/Android by patching upstream platform detection.
|
|
|
|
psutil's setup currently gates Linux sources behind
|
|
``sys.platform.startswith('linux')``. On Termux, Python reports
|
|
``sys.platform == 'android'``, so ``pip install psutil`` aborts with
|
|
"platform android is not supported" — even though psutil compiles fine
|
|
when the Linux source path is reused.
|
|
|
|
This script downloads the official psutil sdist, applies a one-line
|
|
patch (``LINUX = sys.platform.startswith(("linux", "android"))``), and
|
|
installs the patched tree with ``pip install --no-build-isolation``.
|
|
|
|
Usage:
|
|
python scripts/install_psutil_android.py [--pip "/path/to/pip"] [--uv]
|
|
|
|
When neither flag is given, the script auto-detects ``uv`` on PATH and
|
|
falls back to ``<sys.executable> -m pip``.
|
|
|
|
This is a stopgap. Remove once psutil upstream merges
|
|
https://github.com/giampaolo/psutil/pull/2762 and ships a release.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
import tarfile
|
|
import tempfile
|
|
import urllib.request
|
|
from pathlib import Path
|
|
|
|
# Pin a version we know patches cleanly. Update when a newer psutil
|
|
# changes the marker line shape and we need to follow upstream.
|
|
PSUTIL_URL = (
|
|
"https://files.pythonhosted.org/packages/aa/c6/"
|
|
"d1ddf4abb55e93cebc4f2ed8b5d6dbad109ecb8d63748dd2b20ab5e57ebe/"
|
|
"psutil-7.2.2.tar.gz"
|
|
)
|
|
|
|
MARKER = 'LINUX = sys.platform.startswith("linux")'
|
|
REPLACEMENT = 'LINUX = sys.platform.startswith(("linux", "android"))'
|
|
|
|
|
|
def _resolve_install_cmd(pip_arg: str | None, prefer_uv: bool) -> list[str]:
|
|
if pip_arg:
|
|
return pip_arg.split()
|
|
if prefer_uv:
|
|
uv = shutil.which("uv")
|
|
if not uv:
|
|
sys.exit("--uv requested but no uv on PATH")
|
|
return [uv, "pip"]
|
|
auto_uv = shutil.which("uv")
|
|
if auto_uv:
|
|
return [auto_uv, "pip"]
|
|
return [sys.executable, "-m", "pip"]
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument(
|
|
"--pip",
|
|
help="Explicit installer command (e.g. '/usr/bin/uv pip' or 'python -m pip')",
|
|
)
|
|
parser.add_argument(
|
|
"--uv",
|
|
action="store_true",
|
|
help="Force using uv (errors out if uv is not on PATH)",
|
|
)
|
|
args = parser.parse_args()
|
|
|
|
install_cmd_prefix = _resolve_install_cmd(args.pip, args.uv)
|
|
|
|
print(
|
|
"→ Termux/Android: prebuilding psutil with Linux source path "
|
|
"compatibility shim (see psutil#2762)..."
|
|
)
|
|
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
tmp_path = Path(tmp)
|
|
archive = tmp_path / "psutil.tar.gz"
|
|
urllib.request.urlretrieve(PSUTIL_URL, archive)
|
|
with tarfile.open(archive) as tar:
|
|
tar.extractall(tmp_path)
|
|
|
|
try:
|
|
src_root = next(
|
|
p for p in tmp_path.iterdir()
|
|
if p.is_dir() and p.name.startswith("psutil-")
|
|
)
|
|
except StopIteration:
|
|
sys.exit("psutil sdist did not contain a psutil-* directory")
|
|
|
|
common_py = src_root / "psutil" / "_common.py"
|
|
content = common_py.read_text(encoding="utf-8")
|
|
if MARKER not in content:
|
|
sys.exit(
|
|
"psutil Android compatibility patch marker not found — "
|
|
"upstream may have changed the LINUX detection line. "
|
|
"Update MARKER/REPLACEMENT in this script."
|
|
)
|
|
common_py.write_text(content.replace(MARKER, REPLACEMENT), encoding="utf-8")
|
|
|
|
cmd = install_cmd_prefix + ["install", "--no-build-isolation", str(src_root)]
|
|
print(f" $ {' '.join(cmd)}")
|
|
result = subprocess.run(cmd)
|
|
if result.returncode != 0:
|
|
return result.returncode
|
|
|
|
print("✓ psutil installed via Android compatibility shim")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|