Skip to content

Commit 66e2e62

Browse files
Sodawyxclaude
andcommitted
feat(nuitka): add Nuitka --onefile migration design
Switches the CLI binary toolchain from PyInstaller to Nuitka so warm invocations hit the payload-hash cache at ~/.agentrun/cache/ and avoid the ~2s per-call re-extraction, unblocking the agent-hot-path scenario. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent df0e5c1 commit 66e2e62

12 files changed

Lines changed: 222 additions & 112 deletions

File tree

.github/workflows/release.yml

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ jobs:
7777
include:
7878
# Linux builds use ubuntu-22.04 (glibc 2.35) rather than a manylinux
7979
# container: manylinux ships a static CPython without libpython*.so,
80-
# which PyInstaller cannot embed. The glibc-2.35 floor still covers
80+
# which Nuitka cannot embed. The glibc-2.35 floor still covers
8181
# Ubuntu 22.04+, Debian 12+, RHEL 9+, and all current modern distros.
8282
- target: linux-amd64
8383
runner: ubuntu-22.04
@@ -111,23 +111,39 @@ jobs:
111111
with:
112112
python-version: ${{ env.PYTHON_VERSION }}
113113

114-
- name: Install project + PyInstaller
114+
- name: Install Nuitka build toolchain (Linux)
115+
if: startsWith(matrix.target, 'linux-')
116+
shell: bash
117+
run: |
118+
sudo apt-get update -qq
119+
sudo apt-get install -y -qq patchelf ccache
120+
121+
- name: Install project + Nuitka
115122
shell: bash
116123
run: |
117124
python -m pip install --upgrade pip
118125
python -m pip install -e .
119-
python -m pip install pyinstaller
126+
python -m pip install "nuitka>=2.4" "zstandard>=0.22"
120127
121128
- name: Build binary
122129
shell: bash
123130
run: |
124-
pyinstaller --clean --noconfirm agentrun.spec
131+
bash scripts/build-binary.sh
125132
ls -lh dist/
126133
127134
- name: Smoke test binary
128135
shell: bash
129136
run: |
130-
./dist/agentrun${{ matrix.ext }} --version
137+
BIN="./dist/agentrun${{ matrix.ext }}"
138+
"$BIN" --version
139+
"$BIN" --help >/dev/null
140+
"$BIN" config --help >/dev/null
141+
"$BIN" model --help >/dev/null
142+
"$BIN" sandbox --help >/dev/null
143+
"$BIN" skill --help >/dev/null
144+
"$BIN" super-agent --help >/dev/null
145+
"$BIN" tool --help >/dev/null
146+
echo "All 8 subcommand invocations OK on ${{ matrix.target }}"
131147
132148
# --- Package (Unix) -----------------------------------------------
133149
- name: Package tar.gz (Unix)

.gitignore

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,11 @@ venv/
1212
.idea/
1313
.vscode/
1414
*.spec
15-
!agentrun.spec
15+
# Nuitka build artifacts
16+
*.dist/
17+
*.build/
18+
*.bin
19+
*.onefile-build/
1620
.coverage
1721
coverage.json
1822
htmlcov/

AGENTS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ make test # Run all tests
2020
.venv/bin/pytest tests/test_cli_basic.py::TestConfigCommands::test_set_and_get -v # Single test
2121

2222
# Build standalone binary
23-
make build # PyInstaller binary → dist/ar
23+
make build # Nuitka --onefile binary → dist/agentrun (warm-start cache: ~/.agentrun/cache/)
2424
make build-all # macOS + Linux (via Docker)
2525
```
2626

Makefile

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,7 @@ VENV := .venv
55
BIN := $(VENV)/bin
66
PIP := $(BIN)/pip
77
PIP_MIRROR := -i https://mirrors.aliyun.com/pypi/simple/
8-
PYINST := $(BIN)/pyinstaller
98
APP_NAME := agentrun
10-
SPEC := agentrun.spec
119
VERSION := $(shell $(PYTHON) -c "from src.agentrun_cli import __version__; print(__version__)" 2>/dev/null || echo "0.1.0")
1210

1311
help: ## Show this help message
@@ -21,7 +19,6 @@ install: ## Install the package in editable mode
2119
dev: ## Install with dev dependencies
2220
$(PYTHON) -m venv $(VENV)
2321
$(PIP) install $(PIP_MIRROR) -e ".[dev]" || $(PIP) install $(PIP_MIRROR) -e .
24-
$(PIP) install $(PIP_MIRROR) pyinstaller
2522

2623
lint: ## Run ruff linter
2724
$(BIN)/ruff check src/ tests/
@@ -45,18 +42,18 @@ clean: ## Remove build artifacts
4542
rm -rf build/ dist/ *.spec __pycache__
4643
find . -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true
4744

48-
build: ## Build binary for the current platform (uses agentrun.spec)
49-
DISABLE_BREAKING_CHANGES_WARNING=1 \
50-
$(PYINST) --clean --noconfirm $(SPEC)
45+
build: ## Build single-file binary for the current platform (uses Nuitka)
46+
bash scripts/build-binary.sh
5147
@echo ""
5248
@echo "Binary built: dist/$(APP_NAME)"
5349
@ls -lh dist/$(APP_NAME)
5450

5551
build-macos: build ## Alias for build (on macOS, just run 'make build')
5652
@echo "macOS binary ready at dist/$(APP_NAME)"
5753

58-
# Cross-compiling Python to Linux is not supported by PyInstaller.
59-
# Use this target inside a Linux environment (Docker / CI).
54+
# Nuitka compiles Python to native C, so cross-compiling is not supported.
55+
# Use this target inside a Linux environment (Docker / CI) to produce a
56+
# Linux binary when you're on a non-Linux host.
6057
build-linux: build ## Build Linux binary (run inside Linux or Docker)
6158
@echo "Linux binary ready at dist/$(APP_NAME)"
6259

@@ -69,10 +66,9 @@ build-all: ## Build for all platforms (macOS local + Linux via Docker)
6966
tar cf - --exclude=.venv --exclude=.git --exclude=build --exclude=dist --exclude=__pycache__ --exclude='*.pyc' . | \
7067
docker run --rm -i -v $(PWD)/dist:/out python:3.10-slim sh -c \
7168
"mkdir /build && cd /build && tar xf - && \
72-
apt-get update -qq && apt-get install -y -qq binutils >/dev/null 2>&1 && \
73-
pip install $(PIP_MIRROR) -e . && pip install $(PIP_MIRROR) pyinstaller && \
74-
DISABLE_BREAKING_CHANGES_WARNING=1 \
75-
pyinstaller --clean --noconfirm $(SPEC) && \
69+
apt-get update -qq && apt-get install -y -qq binutils patchelf ccache gcc >/dev/null 2>&1 && \
70+
pip install $(PIP_MIRROR) -e . && pip install $(PIP_MIRROR) nuitka zstandard && \
71+
bash scripts/build-binary.sh && \
7672
cp dist/$(APP_NAME) /out/$(APP_NAME)"
7773
@mkdir -p dist/linux && cp dist/$(APP_NAME) dist/linux/$(APP_NAME)
7874
@echo ""

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ agents that you configure declaratively without writing or deploying any runtime
1818
- **Multiple output formats**`json` (default), `table`, `yaml`, and `quiet` for shell piping.
1919
- **Agent-friendly** — JSON-by-default output, deterministic exit codes, no interactive prompts when stdin isn't a TTY.
2020
- **Rich sandbox primitives** — code execution, file system, process management, and CDP/VNC-backed browser automation.
21-
- **Single-file distribution**PyInstaller produces standalone `ar` / `agentrun` binaries for Linux, macOS and Windows (x86_64 + arm64).
21+
- **Single-file distribution**Nuitka `--onefile` produces standalone `ar` / `agentrun` binaries for Linux, macOS and Windows (x86_64 + arm64) with warm-start caching under `~/.agentrun/cache/`.
2222

2323
## Installation
2424

README_zh.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ Agent)**:一种由平台托管、用户只需声明配置、无需编写或
1717
- **多种输出格式** — 默认 `json`,支持 `table` / `yaml` / `quiet`(适合 shell 管道)。
1818
- **对 Agent 友好** — 默认 JSON 输出、确定性退出码、非 TTY 下不弹交互提示。
1919
- **完整沙箱能力** — 代码执行、文件系统、进程管理、CDP/VNC 浏览器自动化。
20-
- **单文件分发**PyInstaller 产出 Linux / macOS / Windows(x86_64 + arm64)上的独立 `ar` / `agentrun` 二进制。
20+
- **单文件分发**基于 Nuitka `--onefile` 产出 Linux / macOS / Windows(x86_64 + arm64)上的独立 `ar` / `agentrun` 二进制,热启动复用 `~/.agentrun/cache/` 缓存
2121

2222
## 安装
2323

agentrun.spec

Lines changed: 0 additions & 89 deletions
This file was deleted.

docs/en/index.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,14 @@ make build # local binary → dist/agentrun
6969
After installation, both `ar` and `agentrun` are available as entry points and behave
7070
identically. `ar` is shorter; the examples in this manual use it.
7171

72+
### Binary startup cache
73+
74+
The prebuilt binary is a Nuitka `--onefile` executable. On first launch it extracts its payload into `~/.agentrun/cache/agentrun-<version>/` (about 20 MB); subsequent launches reuse the cache, bringing warm start-up below 300 ms.
75+
76+
- **Safe to delete.** Remove `~/.agentrun/cache/` at any time; the next invocation re-extracts.
77+
- **Upgrades.** A new binary version writes to a new subdirectory; old ones stay until you clean them up.
78+
- **Read-only `$HOME`.** If `~/.agentrun/cache/` is not writable, the bootstrap falls back to `$TMPDIR` with full re-extraction on every run (~2 s). Either grant write access or run from a shell where `$HOME` points somewhere writable.
79+
7280
## Authentication
7381

7482
The CLI resolves credentials from three sources, in this order:

docs/zh/index.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,14 @@ make build # 本地打独立二进制 → dist/agentrun
6868
安装完成后 `ar``agentrun` 都是入口点,行为完全一致。`ar` 更短,文档里的示例
6969
默认用 `ar`
7070

71+
### 二进制启动缓存
72+
73+
预编译二进制是一个 Nuitka `--onefile` 可执行文件。首次运行会把内置 payload 解压到 `~/.agentrun/cache/agentrun-<version>/`(约 20 MB),之后每次启动复用缓存,热启动耗时低于 300 ms。
74+
75+
- **可以随时删除。**`~/.agentrun/cache/` 删掉后下一次运行自动重建。
76+
- **升级行为。**新版本二进制会写入新的子目录,老目录保留直到手动清理。
77+
- **`$HOME` 只读的情况。**`~/.agentrun/cache/` 不可写,bootstrap 会回落到 `$TMPDIR` 且每次都完整解压(~2 秒)。请确保目录可写,或从 `$HOME` 指向可写位置的 shell 启动。
78+
7179
## 认证
7280

7381
CLI 按以下顺序解析凭证(上面优先):

pyproject.toml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ Issues = "https://github.com/Serverless-Devs/agentrun-cli/issues"
3838
dev = [
3939
"pytest>=8.0.0", "pytest-cov>=6.0.0",
4040
"pytest-asyncio>=1.2.0",
41-
"pyinstaller>=6.0.0",
41+
"nuitka>=2.4",
42+
"zstandard>=0.22",
4243
"ruff>=0.14.0",
4344
"mypy>=1.11.0",
4445
"types-PyYAML>=6.0",
@@ -48,7 +49,8 @@ dev = [
4849
dev = [
4950
"pytest>=8.0.0", "pytest-cov>=6.0.0",
5051
"pytest-asyncio>=1.2.0",
51-
"pyinstaller>=6.0.0",
52+
"nuitka>=2.4",
53+
"zstandard>=0.22",
5254
"ruff>=0.14.0",
5355
"mypy>=1.11.0",
5456
"types-PyYAML>=6.0",

0 commit comments

Comments
 (0)