From 81406887e570617e1915f481425b9f451d120190 Mon Sep 17 00:00:00 2001 From: SaladDay <1203511142@qq.com> Date: Sat, 10 Oct 2026 16:59:26 +0000 Subject: [PATCH 1/2] Preserve Runtime PATH in login shells --- Makefile | 1 + docs/maintainers.md | 4 +- docs/zh/maintainers.md | 6 ++- scripts/build-claude-runtime.sh | 1 + scripts/build-codex-runtime.sh | 1 + scripts/build-mcode-runtime.sh | 1 + services/core/deploy/claude/Dockerfile | 2 + services/core/deploy/codex/Dockerfile | 2 + services/core/deploy/mcode/Dockerfile | 2 + services/core/deploy/runtime_profile.py | 27 ++++++++++++ services/core/deploy/runtime_profile_test.py | 44 ++++++++++++++++++++ 11 files changed, 88 insertions(+), 3 deletions(-) create mode 100644 services/core/deploy/runtime_profile.py create mode 100644 services/core/deploy/runtime_profile_test.py diff --git a/Makefile b/Makefile index 5a93f513b..888e671c2 100644 --- a/Makefile +++ b/Makefile @@ -183,6 +183,7 @@ check-microsandbox-provider: .PHONY: check-distribution build-core-distribution check-distribution: + PYTHONDONTWRITEBYTECODE=1 python3 services/core/deploy/runtime_profile_test.py node --test scripts/build-native-catalog.test.mjs go test ./services/web ./services/core/cmd/oac -count=1 PYTHONDONTWRITEBYTECODE=1 python3 -m unittest discover -s deploy/node -p 'test_*.py' diff --git a/docs/maintainers.md b/docs/maintainers.md index a410ad2d1..d0154a883 100644 --- a/docs/maintainers.md +++ b/docs/maintainers.md @@ -59,6 +59,8 @@ The catalog records the commit, the Runtime protocol version, each archive's SHA `make build-core-distribution` builds all of these. Build one on its own to test a Harness image or a helper. Run every command from the repository root; default outputs go under `${OAC_DEV_HOME:-$HOME/.oac}/build`. +The three maintained Runtime images share a build-time adjustment to the pinned Debian login profile: an inherited, exported `PATH` is preserved, while an unset `PATH` receives Debian’s defaults. This keeps the Runtime’s initialized package and user paths available in login and non-login tools without another package-path setting. The combined image inherits the same profile. A changed upstream profile fails the build for review; custom shell startup files can still explicitly change `PATH`. + **Codex Runtime image.** Extract the official npm package `@openai/codex@0.153.4-linux-x64` under `~/.oac` (for example with `npm pack --ignore-scripts` and `tar -xzf`), then: ```sh @@ -67,7 +69,7 @@ make build-codex-runtime docker build --platform linux/amd64 -t oac-runtime:codex "${OAC_DEV_HOME:-$HOME/.oac}/build/codex-runtime" ``` -The script checks the package version, builds `oac-daemon` for Linux amd64 and prepares a context with only the daemon, the unmodified native executable, its resources and `services/core/deploy/codex/Dockerfile`. +The script checks the package version, builds `oac-daemon` for Linux amd64 and prepares a context with only the daemon, the unmodified native executable, its resources, the shared login-profile build step and `services/core/deploy/codex/Dockerfile`. **Claude Code Runtime image.** Node 20 or newer and pnpm are required. diff --git a/docs/zh/maintainers.md b/docs/zh/maintainers.md index 924c3b19f..c9be4699a 100644 --- a/docs/zh/maintainers.md +++ b/docs/zh/maintainers.md @@ -1,7 +1,7 @@ --- title: "构建并发布 OpenAgentCore" source: docs/maintainers.md -source_hash: aaadeb3a5e99b8d926e9f78b7fc41c7aba808e0d2af58969119e67954f9d7a58 +source_hash: 494618f4d605d96bfcb9b1413f8224722324e1037764cc5b38baca102a8f1e90 --- 本指南面向负责构建和发布 OpenAgentCore 的维护者。要安装 Core 和 Web,请使用 [安装指南](getting-started/install.md)。安装器代码遵循的规则见 [部署](https://github.com/MiniMax-AI/OpenAgentCore/blob/main/deploy/README.md) 和 [节点安装器](https://github.com/MiniMax-AI/OpenAgentCore/blob/main/deploy/node/README.md);必需检查见 [CONTRIBUTING](https://github.com/MiniMax-AI/OpenAgentCore/blob/main/CONTRIBUTING.md#required-checks)。 @@ -61,6 +61,8 @@ export OAC_NATIVE_INSTALLER_BUILD_DIR=OUTPUT_DIR `make build-core-distribution` 会构建以下全部内容。也可以单独构建其中一项,以测试某个 Harness 镜像或辅助程序。所有命令都必须从仓库根目录运行;默认输出位于 `${OAC_DEV_HOME:-$HOME/.oac}/build` 下。 +三个维护的 Runtime 镜像共用一个构建期调整:固定 Debian 登录 profile 会保留继承且已导出的 `PATH`,未设置时仍使用 Debian 默认值。这样,登录和非登录工具都能使用 Runtime 初始化的包路径与用户路径,无需另一份包路径设置。组合镜像继承同一 profile。上游 profile 结构变化会使构建失败以便审查;自定义 shell 启动文件仍可以显式更改 `PATH`。 + **Codex Runtime 镜像。** 在 `~/.oac` 下解压官方 npm 包 `@openai/codex@0.153.4-linux-x64`(例如使用 `npm pack --ignore-scripts` 和 `tar -xzf`),然后执行: ```sh @@ -69,7 +71,7 @@ make build-codex-runtime docker build --platform linux/amd64 -t oac-runtime:codex "${OAC_DEV_HOME:-$HOME/.oac}/build/codex-runtime" ``` -该脚本会检查软件包版本,为 Linux amd64 构建 `oac-daemon`,并准备一个仅包含守护进程、未修改的原生可执行文件、相关资源和 `services/core/deploy/codex/Dockerfile` 的上下文。 +该脚本会检查软件包版本,为 Linux amd64 构建 `oac-daemon`,并准备一个仅包含守护进程、未修改的原生可执行文件、相关资源、共用的登录 profile 构建步骤和 `services/core/deploy/codex/Dockerfile` 的上下文。 **Claude Code Runtime 镜像。** 必须使用 Node 20 或更高版本以及 pnpm。 diff --git a/scripts/build-claude-runtime.sh b/scripts/build-claude-runtime.sh index af5b3b587..d5b9c60a8 100755 --- a/scripts/build-claude-runtime.sh +++ b/scripts/build-claude-runtime.sh @@ -22,6 +22,7 @@ node "$repo_root/scripts/check-claude-sdk-runtime.mjs" "$context/claude-sdk" -o "$context/oac-daemon" ./apps/daemon/cmd/oac-daemon ) cp "$repo_root/services/core/deploy/claude/Dockerfile" "$context/Dockerfile" +cp "$repo_root/services/core/deploy/runtime_profile.py" "$context/runtime_profile.py" mkdir -p "$output_dir" cp -R "$context/." "$output_dir/" printf 'Claude Runtime image context: %s\n' "$output_dir" diff --git a/scripts/build-codex-runtime.sh b/scripts/build-codex-runtime.sh index fcae07a81..8269cd07c 100755 --- a/scripts/build-codex-runtime.sh +++ b/scripts/build-codex-runtime.sh @@ -33,6 +33,7 @@ trap 'rm -rf "$context"' EXIT cp "$native_dir/bin/codex" "$context/codex" cp -R "$native_dir/codex-resources" "$context/codex-resources" cp "$repo_root/services/core/deploy/codex/Dockerfile" "$context/Dockerfile" +cp "$repo_root/services/core/deploy/runtime_profile.py" "$context/runtime_profile.py" # Preserve the previous bundle if compilation or validation failed. mkdir -p "$output_dir" cp -R "$context/." "$output_dir/" diff --git a/scripts/build-mcode-runtime.sh b/scripts/build-mcode-runtime.sh index 04a507698..8698a2212 100644 --- a/scripts/build-mcode-runtime.sh +++ b/scripts/build-mcode-runtime.sh @@ -23,6 +23,7 @@ cp -RL "$companion/." "$context/mcode-harness/" -o "$context/oac-daemon" ./apps/daemon/cmd/oac-daemon ) cp "$repo_root/services/core/deploy/mcode/Dockerfile" "$context/Dockerfile" +cp "$repo_root/services/core/deploy/runtime_profile.py" "$context/runtime_profile.py" mkdir -p "$output" cp -R "$context/." "$output/" printf 'MiniMax Code Runtime image context: %s\n' "$output" diff --git a/services/core/deploy/claude/Dockerfile b/services/core/deploy/claude/Dockerfile index b4ce63d03..9862c8582 100644 --- a/services/core/deploy/claude/Dockerfile +++ b/services/core/deploy/claude/Dockerfile @@ -5,6 +5,8 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ ca-certificates bash git python3 python3-pip ripgrep \ && rm -rf /var/lib/apt/lists/* \ && mkdir -p /environment/workspace /workspace /home/runtime +COPY runtime_profile.py /tmp/oac-runtime-profile.py +RUN python3 /tmp/oac-runtime-profile.py && rm /tmp/oac-runtime-profile.py COPY --chmod=0555 oac-daemon /usr/local/bin/ COPY claude-sdk /opt/claude-sdk ENV HOME=/home/runtime OAC_RUNTIME_HOME=/home/runtime/.oac \ diff --git a/services/core/deploy/codex/Dockerfile b/services/core/deploy/codex/Dockerfile index 4dc401212..53887748d 100644 --- a/services/core/deploy/codex/Dockerfile +++ b/services/core/deploy/codex/Dockerfile @@ -8,6 +8,8 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ ca-certificates bash git python3 python3-pip ripgrep \ && rm -rf /var/lib/apt/lists/* \ && mkdir -p /environment/workspace /workspace /home/runtime +COPY runtime_profile.py /tmp/oac-runtime-profile.py +RUN python3 /tmp/oac-runtime-profile.py && rm /tmp/oac-runtime-profile.py COPY --chmod=0555 oac-daemon codex /usr/local/bin/ COPY codex-resources /usr/local/codex-resources ENV HOME=/home/runtime OAC_RUNTIME_HOME=/home/runtime/.oac \ diff --git a/services/core/deploy/mcode/Dockerfile b/services/core/deploy/mcode/Dockerfile index 15aa27e74..aa43dbbb4 100644 --- a/services/core/deploy/mcode/Dockerfile +++ b/services/core/deploy/mcode/Dockerfile @@ -5,6 +5,8 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ ca-certificates bash git python3 python3-pip ripgrep \ && rm -rf /var/lib/apt/lists/* \ && mkdir -p /environment/workspace /workspace /home/runtime +COPY runtime_profile.py /tmp/oac-runtime-profile.py +RUN python3 /tmp/oac-runtime-profile.py && rm /tmp/oac-runtime-profile.py COPY --chmod=0555 oac-daemon /usr/local/bin/ COPY mcode-harness /opt/mcode-harness ENV HOME=/home/runtime OAC_RUNTIME_HOME=/home/runtime/.oac \ diff --git a/services/core/deploy/runtime_profile.py b/services/core/deploy/runtime_profile.py new file mode 100644 index 000000000..cd4558dd7 --- /dev/null +++ b/services/core/deploy/runtime_profile.py @@ -0,0 +1,27 @@ +"""Preserve the Runtime's exported PATH in the pinned Debian login profile.""" +from pathlib import Path + +DEBIAN_PATH = '''if [ "$(id -u)" -eq 0 ]; then + PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" +else + PATH="/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games" +fi +export PATH +''' + + +def preserve_runtime_path(profile: str) -> str: + if profile.count(DEBIAN_PATH) != 1: + raise ValueError("Pinned Debian profile PATH block changed") + # Bash creates an unexported PATH when its caller supplied none. Check the + # exported environment so that case still receives Debian's UID defaults. + replacement = ('# Preserve the Runtime tool environment across login shells.\n' + 'if ! /usr/bin/printenv PATH >/dev/null; then\n' + + ''.join(' ' + line for line in DEBIAN_PATH.splitlines(keepends=True)) + + 'fi\n') + return profile.replace(DEBIAN_PATH, replacement) + + +if __name__ == '__main__': + path = Path('/etc/profile') + path.write_text(preserve_runtime_path(path.read_text())) diff --git a/services/core/deploy/runtime_profile_test.py b/services/core/deploy/runtime_profile_test.py new file mode 100644 index 000000000..079f5936d --- /dev/null +++ b/services/core/deploy/runtime_profile_test.py @@ -0,0 +1,44 @@ +import os +from pathlib import Path +import subprocess +import tempfile +import unittest + +from runtime_profile import DEBIAN_PATH, preserve_runtime_path + + +class RuntimeProfileTest(unittest.TestCase): + def test_rejects_changed_or_duplicate_upstream_block(self): + for value in ('', DEBIAN_PATH.replace('/usr/games', '/changed'), DEBIAN_PATH * 2, + preserve_runtime_path(DEBIAN_PATH)): + with self.subTest(profile=value): + self.assertRaises(ValueError, preserve_runtime_path, value) + + def test_preserves_other_profile_content(self): + result = preserve_runtime_path('# before\n' + DEBIAN_PATH + '# after\n') + self.assertTrue(result.startswith('# before\n')) + self.assertTrue(result.endswith('# after\n')) + + @unittest.skipUnless(os.name == 'posix' and Path('/bin/bash').exists(), 'Bash profile') + def test_exported_unset_and_empty_path(self): + with tempfile.TemporaryDirectory() as directory: + original, fixed = Path(directory) / 'original', Path(directory) / 'fixed' + original.write_text(DEBIAN_PATH) + fixed.write_text(preserve_runtime_path(DEBIAN_PATH)) + def run(profile, path): + env = dict(os.environ) + env.pop('BASH_ENV', None) + if path is None: + env.pop('PATH', None) + else: + env['PATH'] = path + return subprocess.check_output(['/bin/bash', '--noprofile', '--norc', '-c', + '. "$1"; printf %s "$PATH"', 'profile-test', str(profile)], env=env, text=True) + custom = '/custom package/bin:/usr/bin:/bin' + self.assertEqual(run(fixed, custom), custom) + self.assertEqual(run(fixed, ''), '') + self.assertEqual(run(fixed, None), run(original, None)) + + +if __name__ == '__main__': + unittest.main() From a82ce0d0621c5c8d1e4ee8287f40cb36cef2d00c Mon Sep 17 00:00:00 2001 From: SaladDay <1203511142@qq.com> Date: Sat, 10 Oct 2026 17:03:11 +0000 Subject: [PATCH 2/2] Keep PATH literals aligned in translated image guide --- docs/zh/maintainers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/zh/maintainers.md b/docs/zh/maintainers.md index c9be4699a..e61ec78da 100644 --- a/docs/zh/maintainers.md +++ b/docs/zh/maintainers.md @@ -61,7 +61,7 @@ export OAC_NATIVE_INSTALLER_BUILD_DIR=OUTPUT_DIR `make build-core-distribution` 会构建以下全部内容。也可以单独构建其中一项,以测试某个 Harness 镜像或辅助程序。所有命令都必须从仓库根目录运行;默认输出位于 `${OAC_DEV_HOME:-$HOME/.oac}/build` 下。 -三个维护的 Runtime 镜像共用一个构建期调整:固定 Debian 登录 profile 会保留继承且已导出的 `PATH`,未设置时仍使用 Debian 默认值。这样,登录和非登录工具都能使用 Runtime 初始化的包路径与用户路径,无需另一份包路径设置。组合镜像继承同一 profile。上游 profile 结构变化会使构建失败以便审查;自定义 shell 启动文件仍可以显式更改 `PATH`。 +三个维护的 Runtime 镜像共用一个构建期调整:固定 Debian 登录 profile 会保留继承且已导出的 `PATH`,未设置 `PATH` 时仍使用 Debian 默认值。这样,登录和非登录工具都能使用 Runtime 初始化的包路径与用户路径,无需另一份包路径设置。组合镜像继承同一 profile。上游 profile 结构变化会使构建失败以便审查;自定义 shell 启动文件仍可以显式更改 `PATH`。 **Codex Runtime 镜像。** 在 `~/.oac` 下解压官方 npm 包 `@openai/codex@0.153.4-linux-x64`(例如使用 `npm pack --ignore-scripts` 和 `tar -xzf`),然后执行: