Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
4 changes: 3 additions & 1 deletion docs/maintainers.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.

Expand Down
6 changes: 4 additions & 2 deletions docs/zh/maintainers.md
Original file line number Diff line number Diff line change
@@ -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)。
Expand Down Expand Up @@ -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`,未设置 `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
Expand All @@ -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。

Expand Down
1 change: 1 addition & 0 deletions scripts/build-claude-runtime.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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"
1 change: 1 addition & 0 deletions scripts/build-codex-runtime.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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/"
Expand Down
1 change: 1 addition & 0 deletions scripts/build-mcode-runtime.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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"
2 changes: 2 additions & 0 deletions services/core/deploy/claude/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down
2 changes: 2 additions & 0 deletions services/core/deploy/codex/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down
2 changes: 2 additions & 0 deletions services/core/deploy/mcode/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down
27 changes: 27 additions & 0 deletions services/core/deploy/runtime_profile.py
Original file line number Diff line number Diff line change
@@ -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()))
44 changes: 44 additions & 0 deletions services/core/deploy/runtime_profile_test.py
Original file line number Diff line number Diff line change
@@ -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()
Loading