Skip to content

Commit 94831fc

Browse files
authored
Merge branch 'alibaba:main' into main
2 parents aa974a0 + ac24abd commit 94831fc

60 files changed

Lines changed: 3254 additions & 71 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/publish-components.yml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ on:
1717
- code-interpreter
1818
- ingress
1919
- egress
20+
- controller
21+
- task-executor
2022
default: 'execd'
2123
image_tag:
2224
description: 'Docker image tag'
@@ -28,6 +30,8 @@ on:
2830
- 'docker/code-interpreter/**'
2931
- 'docker/ingress/**'
3032
- 'docker/egress/**'
33+
- 'k8s/controller/**'
34+
- 'k8s/task-executor/**'
3135

3236
jobs:
3337
publish:
@@ -65,6 +69,15 @@ jobs:
6569
COMPONENT=$(echo "$TAG_PATH" | cut -d'/' -f2)
6670
IMAGE_TAG=$(echo "$TAG_PATH" | cut -d'/' -f3)
6771
72+
echo "component=$COMPONENT" >> $GITHUB_OUTPUT
73+
echo "image_tag=$IMAGE_TAG" >> $GITHUB_OUTPUT
74+
elif [[ "${{ github.ref }}" == refs/tags/k8s/* ]]; then
75+
TAG_PATH="${{ github.ref }}"
76+
TAG_PATH="${TAG_PATH#refs/tags/}"
77+
78+
COMPONENT=$(echo "$TAG_PATH" | cut -d'/' -f2)
79+
IMAGE_TAG=$(echo "$TAG_PATH" | cut -d'/' -f3)
80+
6881
echo "component=$COMPONENT" >> $GITHUB_OUTPUT
6982
echo "image_tag=$IMAGE_TAG" >> $GITHUB_OUTPUT
7083
else
@@ -90,11 +103,16 @@ jobs:
90103
cd components/ingress
91104
elif [ "$COMPONENT" == "egress" ]; then
92105
cd components/egress
106+
elif [ "$COMPONENT" == "controller" ]; then
107+
cd kubernetes
108+
elif [ "$COMPONENT" == "task-executor" ]; then
109+
cd kubernetes
93110
else
94111
cd sandboxes/$COMPONENT
95112
fi
96113
97114
export TAG=$IMAGE_TAG
115+
export COMPONENT=$COMPONENT
98116
chmod +x build.sh
99117
./build.sh
100118
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
name: Publish Helm Chart
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
component:
7+
description: 'Component to release'
8+
required: true
9+
type: choice
10+
options:
11+
- opensandbox-controller
12+
default: 'opensandbox-controller'
13+
app_version:
14+
description: 'App version (without v prefix, e.g., 0.1.0)'
15+
required: true
16+
default: '0.1.0'
17+
push:
18+
tags:
19+
- 'helm/**' # Format: helm/<component>/<app_version>, e.g., helm/opensandbox-controller/0.1.0
20+
21+
jobs:
22+
publish:
23+
runs-on: ubuntu-latest
24+
permissions:
25+
contents: write
26+
steps:
27+
- name: Checkout code
28+
uses: actions/checkout@v4
29+
with:
30+
fetch-depth: 0
31+
32+
- name: Configure Git
33+
run: |
34+
git config user.name "$GITHUB_ACTOR"
35+
git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
36+
37+
- name: Install Helm
38+
uses: azure/setup-helm@v4
39+
with:
40+
version: 'latest'
41+
42+
- name: Parse tag and set variables
43+
id: parse_tag
44+
run: |
45+
if [[ "${{ github.ref }}" == refs/tags/helm/* ]]; then
46+
TAG_PATH="${{ github.ref }}"
47+
TAG_PATH="${TAG_PATH#refs/tags/}"
48+
49+
COMPONENT=$(echo "$TAG_PATH" | cut -d'/' -f2)
50+
VERSION=$(echo "$TAG_PATH" | cut -d'/' -f3)
51+
52+
# Remove 'v' prefix if present
53+
VERSION=${VERSION#v}
54+
55+
echo "component=$COMPONENT" >> $GITHUB_OUTPUT
56+
echo "app_version=$VERSION" >> $GITHUB_OUTPUT
57+
else
58+
echo "component=${{ inputs.component }}" >> $GITHUB_OUTPUT
59+
echo "app_version=${{ inputs.app_version }}" >> $GITHUB_OUTPUT
60+
fi
61+
62+
- name: Set chart path
63+
id: chart_path
64+
run: |
65+
COMPONENT="${{ steps.parse_tag.outputs.component }}"
66+
67+
if [ "$COMPONENT" == "opensandbox-controller" ]; then
68+
CHART_PATH="kubernetes/charts/opensandbox-controller"
69+
else
70+
echo "Error: Unknown component: $COMPONENT"
71+
exit 1
72+
fi
73+
74+
echo "path=$CHART_PATH" >> $GITHUB_OUTPUT
75+
76+
- name: Get chart version from Chart.yaml
77+
id: chart_version
78+
run: |
79+
CHART_PATH="${{ steps.chart_path.outputs.path }}"
80+
CHART_VERSION=$(grep '^version:' $CHART_PATH/Chart.yaml | awk '{print $2}')
81+
echo "version=$CHART_VERSION" >> $GITHUB_OUTPUT
82+
echo "Chart version: $CHART_VERSION"
83+
84+
- name: Update Chart.yaml with app version
85+
run: |
86+
APP_VERSION="${{ steps.parse_tag.outputs.app_version }}"
87+
CHART_PATH="${{ steps.chart_path.outputs.path }}"
88+
89+
# Only update appVersion, keep chart version as-is in Chart.yaml
90+
sed -i "s/^appVersion:.*/appVersion: \"$APP_VERSION\"/" $CHART_PATH/Chart.yaml
91+
92+
echo "Updated Chart.yaml:"
93+
cat $CHART_PATH/Chart.yaml
94+
95+
- name: Lint Helm chart
96+
run: |
97+
CHART_PATH="${{ steps.chart_path.outputs.path }}"
98+
helm lint $CHART_PATH
99+
100+
- name: Package Helm chart
101+
run: |
102+
CHART_PATH="${{ steps.chart_path.outputs.path }}"
103+
helm package $CHART_PATH
104+
105+
- name: Create GitHub Release
106+
uses: softprops/action-gh-release@v1
107+
with:
108+
tag_name: helm/${{ steps.parse_tag.outputs.component }}/${{ steps.parse_tag.outputs.app_version }}
109+
name: Helm Chart ${{ steps.parse_tag.outputs.component }} ${{ steps.chart_version.outputs.version }} (App v${{ steps.parse_tag.outputs.app_version }})
110+
body: |
111+
## ${{ steps.parse_tag.outputs.component }} Helm Chart
112+
113+
**Chart Version:** ${{ steps.chart_version.outputs.version }}
114+
**App Version:** ${{ steps.parse_tag.outputs.app_version }}
115+
116+
### Installation
117+
118+
直接从 GitHub Release 安装:
119+
120+
```bash
121+
helm install opensandbox \
122+
https://github.com/${{ github.repository }}/releases/download/helm/${{ steps.parse_tag.outputs.component }}/${{ steps.parse_tag.outputs.app_version }}/${{ steps.parse_tag.outputs.component }}-${{ steps.chart_version.outputs.version }}.tgz \
123+
--namespace opensandbox-system \
124+
--create-namespace
125+
```
126+
127+
或者先下载后安装:
128+
129+
```bash
130+
# 下载
131+
wget https://github.com/${{ github.repository }}/releases/download/helm/${{ steps.parse_tag.outputs.component }}/${{ steps.parse_tag.outputs.app_version }}/${{ steps.parse_tag.outputs.component }}-${{ steps.chart_version.outputs.version }}.tgz
132+
133+
# 安装
134+
helm install opensandbox ./${{ steps.parse_tag.outputs.component }}-${{ steps.chart_version.outputs.version }}.tgz \
135+
--namespace opensandbox-system \
136+
--create-namespace
137+
```
138+
139+
### What's Changed
140+
141+
- Chart version: ${{ steps.chart_version.outputs.version }}
142+
- App version: ${{ steps.parse_tag.outputs.app_version }}
143+
files: |
144+
${{ steps.parse_tag.outputs.component }}-*.tgz
145+
draft: false
146+
prerelease: false
147+
env:
148+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.github/workflows/real-e2e.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,8 @@ on:
77
pull_request:
88
branches: [ main ]
99
paths:
10-
- 'server/**'
10+
- 'server/src/**'
1111
- 'components/execd/**'
12-
- 'sandboxes/**'
1312
- 'sdks/code-interpreter/**'
1413
- 'sdks/sandbox/**'
1514
- 'tests/**'

.github/workflows/sandbox-k8s-e2e.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ jobs:
2222
strategy:
2323
fail-fast: false
2424
matrix:
25-
k8s-version: ["1.22.4", "1.24.4", "1.26.4", "1.28.6", "1.30.4", "1.32.2", "1.34.2"]
25+
k8s-version: ["1.21.1", "1.22.4", "1.24.4", "1.26.4", "1.28.6", "1.30.4", "1.32.2", "1.34.2"]
2626
runs-on: ubuntu-latest
2727
steps:
2828
- name: Checkout code

.github/workflows/server-test.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ on:
44
pull_request:
55
branches: [ main ]
66
paths:
7-
- 'server/**'
7+
- 'server/src/**'
8+
- 'server/tests/**'
89

910
permissions:
1011
contents: read

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
<img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki" />
1212
</a>
1313
<a href="https://www.apache.org/licenses/LICENSE-2.0.html">
14-
<img src="https://img.shields.io/github/license/alibaba/OpenSandbox.svg" alt="license" />
14+
<img src="https://img.shields.io/badge/license-Apache%202.0-blue.svg" alt="license" />
1515
</a>
1616
<a href="https://badge.fury.io/py/opensandbox">
1717
<img src="https://badge.fury.io/py/opensandbox.svg" alt="PyPI version" />
@@ -159,6 +159,7 @@ OpenSandbox provides rich examples demonstrating sandbox usage in different scen
159159
- **[iflow-cli](examples/iflow-cli/README.md)** - Run iFLow CLI inside OpenSandbox.
160160
- **[langgraph](examples/langgraph/README.md)** - LangGraph state-machine workflow that creates/runs a sandbox job with fallback retry.
161161
- **[google-adk](examples/google-adk/README.md)** - Google ADK agent using OpenSandbox tools to write/read files and run commands.
162+
- **[nullclaw](examples/nullclaw/README.md)** - Launch a [Nullclaw](https://github.com/nullclaw/nullclaw) Gateway inside a sandbox.
162163
- **[openclaw](examples/openclaw/README.md)** - Launch an OpenClaw Gateway inside a sandbox.
163164

164165
#### 🌐 Browser and Desktop Environments
@@ -229,4 +230,3 @@ This project is open source under the [Apache 2.0 License](LICENSE).
229230
## Star History
230231

231232
[![Star History Chart](https://api.star-history.com/svg?repos=alibaba/OpenSandbox&type=date&legend=top-left)](https://www.star-history.com/#alibaba/OpenSandbox&type=date&legend=top-left)
232-

docs/README_zh.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
<img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki" />
1212
</a>
1313
<a href="https://www.apache.org/licenses/LICENSE-2.0.html">
14-
<img src="https://img.shields.io/github/license/alibaba/OpenSandbox.svg" alt="license" />
14+
<img src="https://img.shields.io/badge/license-Apache%202.0-blue.svg" alt="license" />
1515
</a>
1616
<a href="https://badge.fury.io/py/opensandbox">
1717
<img src="https://badge.fury.io/py/opensandbox.svg" alt="PyPI version" />
@@ -161,7 +161,8 @@ OpenSandbox 提供了丰富的示例来演示不同场景下的沙箱使用方
161161
- **[iflow-cli](../examples/iflow-cli/README.md)** - 在 OpenSandbox 中运行 iFlow CLI。
162162
- **[langgraph](../examples/langgraph/README.md)** - 基于 LangGraph 状态机编排沙箱任务与回退重试。
163163
- **[google-adk](../examples/google-adk/README.md)** - 使用 Google ADK 通过 OpenSandbox 工具读写文件并执行命令。
164-
- **[openclaw](../examples/openclaw/README.md)** - 在沙箱中启动 OpenClaw Gateway。
164+
- **[nullclaw](../examples/nullclaw/README.md)** - 在沙箱中启动 Nullclaw Gateway。
165+
- **[openclaw](../examples/openclaw/README_zh.md)** - 在沙箱中启动 OpenClaw Gateway。
165166

166167
#### 🌐 浏览器与桌面环境
167168

examples/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ Examples for common OpenSandbox use cases. Each subdirectory contains runnable c
1515
- <img src="https://www.kimi.com/favicon.ico" alt="Kimi" width="16" height="16" style="display:inline-block;width:16px;height:16px;vertical-align:middle;margin-right:4px;" /> [**kimi-cli**](kimi-cli): Call Kimi Code CLI (Moonshot AI) within the sandbox
1616
- <img src="https://img.shields.io/badge/-%20-1C3C3C?logo=langgraph&logoColor=white&style=flat-square" alt="LangGraph" width="16" height="16" style="display:inline-block;width:16px;height:16px;vertical-align:middle;margin-right:4px;" /> [**langgraph**](langgraph): LangGraph agent orchestrating sandbox lifecycle + tools
1717
- <img src="https://google.github.io/adk-docs/assets/agent-development-kit.png" alt="Google ADK" width="16" height="16" style="display:inline-block;width:16px;height:16px;vertical-align:middle;margin-right:4px;" /> [**google-adk**](google-adk): Google ADK agent calling OpenSandbox tools
18+
- 🦞 [**nullclaw**](nullclaw): Launch a Nullclaw Gateway inside a sandbox
1819
- 🦞 [**openclaw**](openclaw): Run an OpenClaw Gateway inside a sandbox
1920
- 🖥️ [**desktop**](desktop): Launch VNC desktop (Xvfb + x11vnc) for VNC client connections
2021
- <img src="https://playwright.dev/img/playwright-logo.svg" alt="Playwright" width="16" height="16" style="display:inline-block;width:16px;height:16px;vertical-align:middle;margin-right:4px;" /> [**playwright**](playwright): Launch headless browser (Playwright + Chromium) to scrape web content

examples/nullclaw/README.md

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# Nullclaw Gateway Example
2+
3+
Launch a [Nullclaw](https://github.com/nullclaw/nullclaw) Gateway inside an OpenSandbox instance and expose its HTTP endpoint. The script polls the gateway health check until it returns HTTP 200, then prints the reachable endpoint.
4+
5+
## Start OpenSandbox server [local]
6+
7+
You can find the latest Nullclaw container image [here](https://github.com/nullclaw/nullclaw/pkgs/container/nullclaw).
8+
9+
### Notes (Docker runtime requirement)
10+
11+
The server uses `runtime.type = "docker"` by default, so it **must** be able to reach a running Docker daemon.
12+
13+
- **Docker Desktop**: ensure Docker Desktop is running, then verify with `docker version`.
14+
- **Colima (macOS)**: start it first (`colima start`) and export the socket before starting the server:
15+
16+
```shell
17+
export DOCKER_HOST="unix://${HOME}/.colima/default/docker.sock"
18+
```
19+
20+
Pre-pull the Nullclaw image:
21+
22+
```shell
23+
docker pull ghcr.io/nullclaw/nullclaw:latest
24+
```
25+
26+
Start the OpenSandbox server (logs will stay in the terminal):
27+
28+
```shell
29+
uv pip install opensandbox-server
30+
opensandbox-server init-config ~/.sandbox.toml --example docker
31+
opensandbox-server
32+
```
33+
34+
If you see errors like `FileNotFoundError: [Errno 2] No such file or directory` from `docker/transport/unixconn.py`, it usually means the Docker unix socket is missing or Docker is not running.
35+
36+
## Create and Access the Nullclaw Sandbox
37+
38+
This example is hard-coded for a quick start:
39+
- OpenSandbox server: `http://localhost:8080`
40+
- Image: `ghcr.io/nullclaw/nullclaw:latest`
41+
- Gateway port: `3000`
42+
- Timeout: `3600s`
43+
44+
Install dependencies from the project root:
45+
46+
```shell
47+
uv pip install opensandbox requests
48+
```
49+
50+
Run the example:
51+
52+
```shell
53+
uv run python examples/nullclaw/main.py
54+
```
55+
56+
You should see output similar to:
57+
58+
```text
59+
Creating nullclaw sandbox with image=ghcr.io/nullclaw/nullclaw:latest on OpenSandbox server http://localhost:8080...
60+
[check] sandbox ready after 0.3s
61+
Nullclaw gateway started. Please refer to 127.0.0.1:56234
62+
```
63+
64+
The endpoint printed at the end (e.g., `127.0.0.1:56234`) is the Nullclaw Gateway address exposed from the sandbox.
65+
66+
By default, Nullclaw requires pairing before authenticated endpoints (for example, `/webhook`) can be used. The `/health` endpoint remains publicly accessible.
67+
68+
## References
69+
- [Nullclaw](https://github.com/nullclaw/nullclaw) — Minimal AI assistant runtime (678 KB static Zig binary)
70+
- [Nullclaw Documentation](https://nullclaw.github.io) — Full documentation
71+
- [OpenSandbox Python SDK](https://pypi.org/project/opensandbox/)

0 commit comments

Comments
 (0)