agent-sandbox-harbor 0.0.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agent_sandbox_harbor-0.0.3/PKG-INFO +110 -0
- agent_sandbox_harbor-0.0.3/README.md +92 -0
- agent_sandbox_harbor-0.0.3/agent_sandbox_harbor/__init__.py +22 -0
- agent_sandbox_harbor-0.0.3/agent_sandbox_harbor/environment.py +172 -0
- agent_sandbox_harbor-0.0.3/agent_sandbox_harbor.egg-info/PKG-INFO +110 -0
- agent_sandbox_harbor-0.0.3/agent_sandbox_harbor.egg-info/SOURCES.txt +9 -0
- agent_sandbox_harbor-0.0.3/agent_sandbox_harbor.egg-info/dependency_links.txt +1 -0
- agent_sandbox_harbor-0.0.3/agent_sandbox_harbor.egg-info/requires.txt +10 -0
- agent_sandbox_harbor-0.0.3/agent_sandbox_harbor.egg-info/top_level.txt +1 -0
- agent_sandbox_harbor-0.0.3/pyproject.toml +39 -0
- agent_sandbox_harbor-0.0.3/setup.cfg +4 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: agent-sandbox-harbor
|
|
3
|
+
Version: 0.0.3
|
|
4
|
+
Summary: Agent Sandbox plugin for Harbor — run Terminal-Bench / SWE-bench / Harbor benchmarks on Agent Sandbox pools
|
|
5
|
+
License: Apache-2.0
|
|
6
|
+
Project-URL: Homepage, https://github.com/scitix/agent-sandbox
|
|
7
|
+
Project-URL: Source, https://github.com/scitix/agent-sandbox/tree/main/sdk/python/harbor
|
|
8
|
+
Requires-Python: >=3.12
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
Requires-Dist: agent-sandbox-e2b>=0.0.2
|
|
11
|
+
Provides-Extra: harbor
|
|
12
|
+
Requires-Dist: harbor<=0.7.1,>=0.7.0; extra == "harbor"
|
|
13
|
+
Requires-Dist: e2b>=2.0.0; extra == "harbor"
|
|
14
|
+
Provides-Extra: dev
|
|
15
|
+
Requires-Dist: harbor>=0.7.0; extra == "dev"
|
|
16
|
+
Requires-Dist: e2b>=2.0.0; extra == "dev"
|
|
17
|
+
Requires-Dist: ruff>=0.4.0; extra == "dev"
|
|
18
|
+
|
|
19
|
+
# agent-sandbox-harbor
|
|
20
|
+
|
|
21
|
+
A [Harbor](https://github.com/harbor-framework/harbor) environment plugin that runs Harbor
|
|
22
|
+
benchmarks (Terminal-Bench, SWE-bench, custom datasets) on
|
|
23
|
+
[Agent Sandbox](https://github.com/scitix/agent-sandbox) pre-warmed pools — no fork of Harbor
|
|
24
|
+
required.
|
|
25
|
+
|
|
26
|
+
Highlights:
|
|
27
|
+
|
|
28
|
+
- **Zero Harbor source changes.** Plugs into Harbor via the official
|
|
29
|
+
`--environment-import-path` extension point.
|
|
30
|
+
- **Skips Template Build.** Agent Sandbox uses a pre-warmed Pod pool with in-place image swap,
|
|
31
|
+
so the per-task Template Build step that E2B / Novita require is replaced by a single
|
|
32
|
+
`POST /v1/sandboxes` call.
|
|
33
|
+
- **Internal-mirror friendly.** A configurable image-prefix rewrites `docker.io/...` to your
|
|
34
|
+
private Distribution / Harbor registry.
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pip install 'harbor[e2b]' agent-sandbox-harbor
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The plugin pulls [`agent-sandbox-e2b`](https://pypi.org/project/agent-sandbox-e2b/) as a hard
|
|
43
|
+
dependency (it calls `patch_e2b()` at import). `harbor` is an optional peer dependency, so the
|
|
44
|
+
package can be inspected / unit-tested without it; in real usage you install `harbor[e2b]`
|
|
45
|
+
yourself.
|
|
46
|
+
|
|
47
|
+
## Quick start
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# 1. Set credentials (one-off)
|
|
51
|
+
cat > agentbox.env <<'EOF'
|
|
52
|
+
E2B_API_KEY=agbx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
|
53
|
+
E2B_DOMAIN=agent-sandbox-data-plane.example.com/agent-sandbox/api/data
|
|
54
|
+
E2B_API_URL=https://agent-sandbox-data-plane.example.com/agent-sandbox/api/e2b
|
|
55
|
+
AGBX_CLUSTER_ID=cluster-a
|
|
56
|
+
AGBX_POOL_NAME=terminal-bench-pool
|
|
57
|
+
AGBX_IMAGE_PREFIX=registry.internal/agent-sandbox
|
|
58
|
+
EOF
|
|
59
|
+
|
|
60
|
+
# 2. Run Harbor (use the plugin via the official --environment-import-path flag)
|
|
61
|
+
harbor run \
|
|
62
|
+
-d terminal-bench@2.0 \
|
|
63
|
+
-a oracle \
|
|
64
|
+
--environment-import-path agent_sandbox_harbor:AgentSandboxEnvironment \
|
|
65
|
+
-n 16 -y \
|
|
66
|
+
--env-file agentbox.env
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Configuration
|
|
70
|
+
|
|
71
|
+
| Variable | Required | Description |
|
|
72
|
+
|----------|----------|-------------|
|
|
73
|
+
| `E2B_API_KEY` | yes | Agent Sandbox API key (`agbx_...`). |
|
|
74
|
+
| `AGBX_POOL_NAME` | yes | Pre-warmed pool name. |
|
|
75
|
+
| `E2B_DOMAIN` | no | Data-plane gateway, host[:port][/path]. Default is the in-cluster service. |
|
|
76
|
+
| `E2B_API_URL` | no | E2B-compatible control-plane URL, including scheme. |
|
|
77
|
+
| `AGBX_CLUSTER_ID` | no | Cluster id prefix (e.g. `cluster-a`). Omit for single-cluster setups. |
|
|
78
|
+
| `AGBX_IMAGE_PREFIX` | no | Mirror prefix (e.g. `registry.internal/agent-sandbox`). `docker.io/` is stripped before the prefix is applied. |
|
|
79
|
+
| `AGBX_HTTPS` | no | `true`/`false` for the data-plane scheme (default `true`). |
|
|
80
|
+
| `AGBX_STARTUP_TIMEOUT` | no | Sandbox startup timeout, seconds (default `300`). |
|
|
81
|
+
| `AGBX_READY_TIMEOUT` | no | Cold-image readiness ceiling, seconds (default `600`). |
|
|
82
|
+
|
|
83
|
+
## How it works
|
|
84
|
+
|
|
85
|
+
`AgentSandboxEnvironment` subclasses Harbor's `E2BEnvironment` and overrides three methods:
|
|
86
|
+
|
|
87
|
+
- `_does_template_exist` → always returns `True`
|
|
88
|
+
- `_create_template` → no-op
|
|
89
|
+
- `_create_sandbox` → calls `AsyncSandbox.create(template="cluster::pool//image", secure=False, ...)`
|
|
90
|
+
|
|
91
|
+
`__init__` calls `super().__init__()` first, so Harbor's stock Dockerfile parsing still runs
|
|
92
|
+
(and sets `self._workdir` from the image's `WORKDIR`). The constructor then overrides
|
|
93
|
+
`self._template_name` with the Agent Sandbox pool shorthand.
|
|
94
|
+
|
|
95
|
+
At module import, `patch_e2b()` from
|
|
96
|
+
[`agent-sandbox-e2b`](https://pypi.org/project/agent-sandbox-e2b/) redirects the e2b SDK to
|
|
97
|
+
your Agent Sandbox endpoints.
|
|
98
|
+
|
|
99
|
+
See [INTEGRATION.md](INTEGRATION.md) for full design notes, the `--environment-import-path`
|
|
100
|
+
mechanism explanation, and operational guidance.
|
|
101
|
+
|
|
102
|
+
## Compatibility
|
|
103
|
+
|
|
104
|
+
Each release build is tested against the latest published versions of
|
|
105
|
+
`harbor` and `e2b`. The pinned upper bound in `[project.optional-dependencies]` is updated
|
|
106
|
+
automatically by the release CI to reflect the highest verified `harbor` version.
|
|
107
|
+
|
|
108
|
+
## License
|
|
109
|
+
|
|
110
|
+
Apache 2.0.
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# agent-sandbox-harbor
|
|
2
|
+
|
|
3
|
+
A [Harbor](https://github.com/harbor-framework/harbor) environment plugin that runs Harbor
|
|
4
|
+
benchmarks (Terminal-Bench, SWE-bench, custom datasets) on
|
|
5
|
+
[Agent Sandbox](https://github.com/scitix/agent-sandbox) pre-warmed pools — no fork of Harbor
|
|
6
|
+
required.
|
|
7
|
+
|
|
8
|
+
Highlights:
|
|
9
|
+
|
|
10
|
+
- **Zero Harbor source changes.** Plugs into Harbor via the official
|
|
11
|
+
`--environment-import-path` extension point.
|
|
12
|
+
- **Skips Template Build.** Agent Sandbox uses a pre-warmed Pod pool with in-place image swap,
|
|
13
|
+
so the per-task Template Build step that E2B / Novita require is replaced by a single
|
|
14
|
+
`POST /v1/sandboxes` call.
|
|
15
|
+
- **Internal-mirror friendly.** A configurable image-prefix rewrites `docker.io/...` to your
|
|
16
|
+
private Distribution / Harbor registry.
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pip install 'harbor[e2b]' agent-sandbox-harbor
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
The plugin pulls [`agent-sandbox-e2b`](https://pypi.org/project/agent-sandbox-e2b/) as a hard
|
|
25
|
+
dependency (it calls `patch_e2b()` at import). `harbor` is an optional peer dependency, so the
|
|
26
|
+
package can be inspected / unit-tested without it; in real usage you install `harbor[e2b]`
|
|
27
|
+
yourself.
|
|
28
|
+
|
|
29
|
+
## Quick start
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# 1. Set credentials (one-off)
|
|
33
|
+
cat > agentbox.env <<'EOF'
|
|
34
|
+
E2B_API_KEY=agbx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
|
35
|
+
E2B_DOMAIN=agent-sandbox-data-plane.example.com/agent-sandbox/api/data
|
|
36
|
+
E2B_API_URL=https://agent-sandbox-data-plane.example.com/agent-sandbox/api/e2b
|
|
37
|
+
AGBX_CLUSTER_ID=cluster-a
|
|
38
|
+
AGBX_POOL_NAME=terminal-bench-pool
|
|
39
|
+
AGBX_IMAGE_PREFIX=registry.internal/agent-sandbox
|
|
40
|
+
EOF
|
|
41
|
+
|
|
42
|
+
# 2. Run Harbor (use the plugin via the official --environment-import-path flag)
|
|
43
|
+
harbor run \
|
|
44
|
+
-d terminal-bench@2.0 \
|
|
45
|
+
-a oracle \
|
|
46
|
+
--environment-import-path agent_sandbox_harbor:AgentSandboxEnvironment \
|
|
47
|
+
-n 16 -y \
|
|
48
|
+
--env-file agentbox.env
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Configuration
|
|
52
|
+
|
|
53
|
+
| Variable | Required | Description |
|
|
54
|
+
|----------|----------|-------------|
|
|
55
|
+
| `E2B_API_KEY` | yes | Agent Sandbox API key (`agbx_...`). |
|
|
56
|
+
| `AGBX_POOL_NAME` | yes | Pre-warmed pool name. |
|
|
57
|
+
| `E2B_DOMAIN` | no | Data-plane gateway, host[:port][/path]. Default is the in-cluster service. |
|
|
58
|
+
| `E2B_API_URL` | no | E2B-compatible control-plane URL, including scheme. |
|
|
59
|
+
| `AGBX_CLUSTER_ID` | no | Cluster id prefix (e.g. `cluster-a`). Omit for single-cluster setups. |
|
|
60
|
+
| `AGBX_IMAGE_PREFIX` | no | Mirror prefix (e.g. `registry.internal/agent-sandbox`). `docker.io/` is stripped before the prefix is applied. |
|
|
61
|
+
| `AGBX_HTTPS` | no | `true`/`false` for the data-plane scheme (default `true`). |
|
|
62
|
+
| `AGBX_STARTUP_TIMEOUT` | no | Sandbox startup timeout, seconds (default `300`). |
|
|
63
|
+
| `AGBX_READY_TIMEOUT` | no | Cold-image readiness ceiling, seconds (default `600`). |
|
|
64
|
+
|
|
65
|
+
## How it works
|
|
66
|
+
|
|
67
|
+
`AgentSandboxEnvironment` subclasses Harbor's `E2BEnvironment` and overrides three methods:
|
|
68
|
+
|
|
69
|
+
- `_does_template_exist` → always returns `True`
|
|
70
|
+
- `_create_template` → no-op
|
|
71
|
+
- `_create_sandbox` → calls `AsyncSandbox.create(template="cluster::pool//image", secure=False, ...)`
|
|
72
|
+
|
|
73
|
+
`__init__` calls `super().__init__()` first, so Harbor's stock Dockerfile parsing still runs
|
|
74
|
+
(and sets `self._workdir` from the image's `WORKDIR`). The constructor then overrides
|
|
75
|
+
`self._template_name` with the Agent Sandbox pool shorthand.
|
|
76
|
+
|
|
77
|
+
At module import, `patch_e2b()` from
|
|
78
|
+
[`agent-sandbox-e2b`](https://pypi.org/project/agent-sandbox-e2b/) redirects the e2b SDK to
|
|
79
|
+
your Agent Sandbox endpoints.
|
|
80
|
+
|
|
81
|
+
See [INTEGRATION.md](INTEGRATION.md) for full design notes, the `--environment-import-path`
|
|
82
|
+
mechanism explanation, and operational guidance.
|
|
83
|
+
|
|
84
|
+
## Compatibility
|
|
85
|
+
|
|
86
|
+
Each release build is tested against the latest published versions of
|
|
87
|
+
`harbor` and `e2b`. The pinned upper bound in `[project.optional-dependencies]` is updated
|
|
88
|
+
automatically by the release CI to reflect the highest verified `harbor` version.
|
|
89
|
+
|
|
90
|
+
## License
|
|
91
|
+
|
|
92
|
+
Apache 2.0.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Copyright 2026 ScitiX
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
"""Agent Sandbox Harbor plugin — run Harbor benchmarks on Agent Sandbox pools."""
|
|
16
|
+
|
|
17
|
+
from .environment import AgentSandboxEnvironment
|
|
18
|
+
|
|
19
|
+
__version__ = "0.0.3"
|
|
20
|
+
SUPPORTED_HARBOR_MAX_VERSION = "0.7.1"
|
|
21
|
+
|
|
22
|
+
__all__ = ["SUPPORTED_HARBOR_MAX_VERSION", "AgentSandboxEnvironment"]
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# Copyright 2026 ScitiX
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
"""
|
|
16
|
+
Agent Sandbox Harbor environment plugin.
|
|
17
|
+
|
|
18
|
+
Subclasses Harbor's stock ``E2BEnvironment`` so that:
|
|
19
|
+
|
|
20
|
+
* ``patch_e2b()`` runs at module import (before any AsyncSandbox call),
|
|
21
|
+
redirecting traffic to the Agent Sandbox E2B-compatible endpoint.
|
|
22
|
+
* The "template build" step is skipped (``alias_exists -> True``,
|
|
23
|
+
``_create_template -> no-op``). Agent Sandbox uses a pre-warmed pool with
|
|
24
|
+
in-place image swap, so no template build is needed.
|
|
25
|
+
* The "template" string is rewritten to Agent Sandbox's pool+image shorthand
|
|
26
|
+
``"<CLUSTER_ID>::<POOL_NAME>//<rewritten_image>"`` so that PostSandboxes on
|
|
27
|
+
the compat layer creates a sandbox from the pre-warmed pool.
|
|
28
|
+
|
|
29
|
+
Harbor's stock ``E2BEnvironment.__init__`` runs unchanged (we only subclass
|
|
30
|
+
and call ``super().__init__()``), so ``environment/Dockerfile`` is parsed for
|
|
31
|
+
``WORKDIR`` and ``self._workdir`` is set automatically. Terminal-Bench 2.0
|
|
32
|
+
ships a Dockerfile for every task.
|
|
33
|
+
|
|
34
|
+
Required environment variables (typically via ``harbor run --env-file ...``):
|
|
35
|
+
E2B_API_KEY Agent Sandbox API key (``agbx_...``).
|
|
36
|
+
AGBX_POOL_NAME Pre-warmed pool name (e.g. ``terminal2``).
|
|
37
|
+
|
|
38
|
+
Optional environment variables:
|
|
39
|
+
E2B_DOMAIN Data-plane gateway host[:port][/path].
|
|
40
|
+
E2B_API_URL E2B-compatible control-plane URL (scheme://host).
|
|
41
|
+
AGBX_CLUSTER_ID Cluster id prefix (e.g. ``cetus``); omit for
|
|
42
|
+
single-cluster setups.
|
|
43
|
+
AGBX_IMAGE_PREFIX Internal mirror prefix
|
|
44
|
+
(e.g. ``registry.internal/agent-sandbox``).
|
|
45
|
+
``docker.io/`` is stripped before the prefix is
|
|
46
|
+
applied.
|
|
47
|
+
AGBX_HTTPS ``true``/``false`` for the data-plane scheme
|
|
48
|
+
(default ``true``).
|
|
49
|
+
AGBX_STARTUP_TIMEOUT Sandbox startup timeout, seconds (default ``300``).
|
|
50
|
+
AGBX_READY_TIMEOUT Cold-image readiness ceiling, seconds
|
|
51
|
+
(default ``600``).
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
from __future__ import annotations
|
|
55
|
+
|
|
56
|
+
import asyncio
|
|
57
|
+
import os
|
|
58
|
+
import time
|
|
59
|
+
|
|
60
|
+
from agent_sandbox_e2b import patch_e2b
|
|
61
|
+
|
|
62
|
+
# CRITICAL: ``patch_e2b()`` must execute before ``AsyncSandbox.create`` is
|
|
63
|
+
# invoked. Doing it at module import means all later ``from e2b import ...``
|
|
64
|
+
# lines see the already-patched class methods. Harbor's lazy import of the e2b
|
|
65
|
+
# backend means this module is imported (and ``patch_e2b()`` runs) before
|
|
66
|
+
# Harbor's own ``harbor.environments.e2b`` module is loaded.
|
|
67
|
+
patch_e2b(
|
|
68
|
+
https=os.environ.get("AGBX_HTTPS", "true").lower() == "true",
|
|
69
|
+
domain=os.environ.get("E2B_DOMAIN") or None,
|
|
70
|
+
api_url=os.environ.get("E2B_API_URL") or None,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
from e2b import AsyncSandbox # noqa: E402
|
|
74
|
+
from harbor.environments.e2b import E2BEnvironment # noqa: E402
|
|
75
|
+
from harbor.models.environment_type import EnvironmentType # noqa: E402
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _rewrite_image(raw_image: str) -> str:
|
|
79
|
+
"""Strip ``docker.io/`` and apply the internal mirror prefix."""
|
|
80
|
+
if not raw_image:
|
|
81
|
+
return raw_image
|
|
82
|
+
if raw_image.startswith("docker.io/"):
|
|
83
|
+
raw_image = raw_image[len("docker.io/") :]
|
|
84
|
+
prefix = os.environ.get("AGBX_IMAGE_PREFIX", "").rstrip("/")
|
|
85
|
+
if prefix and not raw_image.startswith(prefix + "/"):
|
|
86
|
+
return f"{prefix}/{raw_image}"
|
|
87
|
+
return raw_image
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class AgentSandboxEnvironment(E2BEnvironment):
|
|
91
|
+
"""E2B-compatible Harbor environment that targets Agent Sandbox pools."""
|
|
92
|
+
|
|
93
|
+
@staticmethod
|
|
94
|
+
def type() -> EnvironmentType:
|
|
95
|
+
return EnvironmentType.E2B
|
|
96
|
+
|
|
97
|
+
@classmethod
|
|
98
|
+
def preflight(cls) -> None:
|
|
99
|
+
missing = [
|
|
100
|
+
v for v in ("E2B_API_KEY", "AGBX_POOL_NAME") if not os.environ.get(v)
|
|
101
|
+
]
|
|
102
|
+
if missing:
|
|
103
|
+
raise SystemExit(
|
|
104
|
+
f"AgentSandboxEnvironment requires env vars: {', '.join(missing)}"
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
def __init__(self, *args, **kwargs):
|
|
108
|
+
# Let E2BEnvironment.__init__ parse environment/Dockerfile for WORKDIR
|
|
109
|
+
# and run its own validation. Terminal-Bench 2.0 ships a Dockerfile for
|
|
110
|
+
# every task, so the validation passes.
|
|
111
|
+
super().__init__(*args, **kwargs)
|
|
112
|
+
|
|
113
|
+
docker_image = self.task_env_config.docker_image
|
|
114
|
+
if not docker_image:
|
|
115
|
+
raise ValueError(
|
|
116
|
+
f"task '{self.environment_name}' has no environment.docker_image; "
|
|
117
|
+
"AgentSandboxEnvironment requires it to be set."
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
cluster = os.environ.get("AGBX_CLUSTER_ID", "").strip()
|
|
121
|
+
pool = os.environ["AGBX_POOL_NAME"]
|
|
122
|
+
image = _rewrite_image(docker_image)
|
|
123
|
+
prefix = f"{cluster}::" if cluster else ""
|
|
124
|
+
# PostSandboxes on the compat layer parses "<cluster>::<pool>//<image>".
|
|
125
|
+
# Replace the hash-based template name that the parent computed.
|
|
126
|
+
self._template_name = f"{prefix}{pool}//{image}"
|
|
127
|
+
|
|
128
|
+
# --- skip template build entirely ----------------------------------------
|
|
129
|
+
|
|
130
|
+
async def _does_template_exist(self) -> bool: # type: ignore[override]
|
|
131
|
+
return True
|
|
132
|
+
|
|
133
|
+
async def _create_template(self) -> None: # type: ignore[override]
|
|
134
|
+
return
|
|
135
|
+
|
|
136
|
+
# --- sandbox create: Agent Sandbox metadata + secure=False + ready poll ---
|
|
137
|
+
|
|
138
|
+
async def _create_sandbox(self) -> None: # type: ignore[override]
|
|
139
|
+
startup_timeout = os.environ.get("AGBX_STARTUP_TIMEOUT", "300")
|
|
140
|
+
metadata = {
|
|
141
|
+
"environment_name": self.environment_name,
|
|
142
|
+
"session_id": self.session_id,
|
|
143
|
+
"agentbox.scitix.ai/startup-timeout": startup_timeout,
|
|
144
|
+
}
|
|
145
|
+
self._sandbox = await AsyncSandbox.create(
|
|
146
|
+
self._template_name,
|
|
147
|
+
timeout=86_400,
|
|
148
|
+
secure=False,
|
|
149
|
+
metadata=metadata,
|
|
150
|
+
allow_internet_access=self.task_env_config.allow_internet,
|
|
151
|
+
)
|
|
152
|
+
# Cold-image pulls can leave ``AsyncSandbox.create`` returning before
|
|
153
|
+
# the envd daemon is reachable; the first ``commands.run()`` then fails
|
|
154
|
+
# with ``Code.unknown``. Block on a trivial exec until it succeeds.
|
|
155
|
+
ready_deadline = float(os.environ.get("AGBX_READY_TIMEOUT", "600"))
|
|
156
|
+
t0 = time.time()
|
|
157
|
+
last_err: Exception | None = None
|
|
158
|
+
while time.time() - t0 < ready_deadline:
|
|
159
|
+
try:
|
|
160
|
+
h = await self._sandbox.commands.run(
|
|
161
|
+
"true", background=True, timeout=0, user="root"
|
|
162
|
+
)
|
|
163
|
+
await h.wait()
|
|
164
|
+
return
|
|
165
|
+
except Exception as e: # noqa: BLE001
|
|
166
|
+
last_err = e
|
|
167
|
+
await asyncio.sleep(3)
|
|
168
|
+
raise TimeoutError(
|
|
169
|
+
f"Sandbox {self._sandbox.sandbox_id} not ready after "
|
|
170
|
+
f"{ready_deadline}s (template={self._template_name}); "
|
|
171
|
+
f"last error: {type(last_err).__name__}: {last_err}"
|
|
172
|
+
)
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: agent-sandbox-harbor
|
|
3
|
+
Version: 0.0.3
|
|
4
|
+
Summary: Agent Sandbox plugin for Harbor — run Terminal-Bench / SWE-bench / Harbor benchmarks on Agent Sandbox pools
|
|
5
|
+
License: Apache-2.0
|
|
6
|
+
Project-URL: Homepage, https://github.com/scitix/agent-sandbox
|
|
7
|
+
Project-URL: Source, https://github.com/scitix/agent-sandbox/tree/main/sdk/python/harbor
|
|
8
|
+
Requires-Python: >=3.12
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
Requires-Dist: agent-sandbox-e2b>=0.0.2
|
|
11
|
+
Provides-Extra: harbor
|
|
12
|
+
Requires-Dist: harbor<=0.7.1,>=0.7.0; extra == "harbor"
|
|
13
|
+
Requires-Dist: e2b>=2.0.0; extra == "harbor"
|
|
14
|
+
Provides-Extra: dev
|
|
15
|
+
Requires-Dist: harbor>=0.7.0; extra == "dev"
|
|
16
|
+
Requires-Dist: e2b>=2.0.0; extra == "dev"
|
|
17
|
+
Requires-Dist: ruff>=0.4.0; extra == "dev"
|
|
18
|
+
|
|
19
|
+
# agent-sandbox-harbor
|
|
20
|
+
|
|
21
|
+
A [Harbor](https://github.com/harbor-framework/harbor) environment plugin that runs Harbor
|
|
22
|
+
benchmarks (Terminal-Bench, SWE-bench, custom datasets) on
|
|
23
|
+
[Agent Sandbox](https://github.com/scitix/agent-sandbox) pre-warmed pools — no fork of Harbor
|
|
24
|
+
required.
|
|
25
|
+
|
|
26
|
+
Highlights:
|
|
27
|
+
|
|
28
|
+
- **Zero Harbor source changes.** Plugs into Harbor via the official
|
|
29
|
+
`--environment-import-path` extension point.
|
|
30
|
+
- **Skips Template Build.** Agent Sandbox uses a pre-warmed Pod pool with in-place image swap,
|
|
31
|
+
so the per-task Template Build step that E2B / Novita require is replaced by a single
|
|
32
|
+
`POST /v1/sandboxes` call.
|
|
33
|
+
- **Internal-mirror friendly.** A configurable image-prefix rewrites `docker.io/...` to your
|
|
34
|
+
private Distribution / Harbor registry.
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pip install 'harbor[e2b]' agent-sandbox-harbor
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The plugin pulls [`agent-sandbox-e2b`](https://pypi.org/project/agent-sandbox-e2b/) as a hard
|
|
43
|
+
dependency (it calls `patch_e2b()` at import). `harbor` is an optional peer dependency, so the
|
|
44
|
+
package can be inspected / unit-tested without it; in real usage you install `harbor[e2b]`
|
|
45
|
+
yourself.
|
|
46
|
+
|
|
47
|
+
## Quick start
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# 1. Set credentials (one-off)
|
|
51
|
+
cat > agentbox.env <<'EOF'
|
|
52
|
+
E2B_API_KEY=agbx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
|
53
|
+
E2B_DOMAIN=agent-sandbox-data-plane.example.com/agent-sandbox/api/data
|
|
54
|
+
E2B_API_URL=https://agent-sandbox-data-plane.example.com/agent-sandbox/api/e2b
|
|
55
|
+
AGBX_CLUSTER_ID=cluster-a
|
|
56
|
+
AGBX_POOL_NAME=terminal-bench-pool
|
|
57
|
+
AGBX_IMAGE_PREFIX=registry.internal/agent-sandbox
|
|
58
|
+
EOF
|
|
59
|
+
|
|
60
|
+
# 2. Run Harbor (use the plugin via the official --environment-import-path flag)
|
|
61
|
+
harbor run \
|
|
62
|
+
-d terminal-bench@2.0 \
|
|
63
|
+
-a oracle \
|
|
64
|
+
--environment-import-path agent_sandbox_harbor:AgentSandboxEnvironment \
|
|
65
|
+
-n 16 -y \
|
|
66
|
+
--env-file agentbox.env
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Configuration
|
|
70
|
+
|
|
71
|
+
| Variable | Required | Description |
|
|
72
|
+
|----------|----------|-------------|
|
|
73
|
+
| `E2B_API_KEY` | yes | Agent Sandbox API key (`agbx_...`). |
|
|
74
|
+
| `AGBX_POOL_NAME` | yes | Pre-warmed pool name. |
|
|
75
|
+
| `E2B_DOMAIN` | no | Data-plane gateway, host[:port][/path]. Default is the in-cluster service. |
|
|
76
|
+
| `E2B_API_URL` | no | E2B-compatible control-plane URL, including scheme. |
|
|
77
|
+
| `AGBX_CLUSTER_ID` | no | Cluster id prefix (e.g. `cluster-a`). Omit for single-cluster setups. |
|
|
78
|
+
| `AGBX_IMAGE_PREFIX` | no | Mirror prefix (e.g. `registry.internal/agent-sandbox`). `docker.io/` is stripped before the prefix is applied. |
|
|
79
|
+
| `AGBX_HTTPS` | no | `true`/`false` for the data-plane scheme (default `true`). |
|
|
80
|
+
| `AGBX_STARTUP_TIMEOUT` | no | Sandbox startup timeout, seconds (default `300`). |
|
|
81
|
+
| `AGBX_READY_TIMEOUT` | no | Cold-image readiness ceiling, seconds (default `600`). |
|
|
82
|
+
|
|
83
|
+
## How it works
|
|
84
|
+
|
|
85
|
+
`AgentSandboxEnvironment` subclasses Harbor's `E2BEnvironment` and overrides three methods:
|
|
86
|
+
|
|
87
|
+
- `_does_template_exist` → always returns `True`
|
|
88
|
+
- `_create_template` → no-op
|
|
89
|
+
- `_create_sandbox` → calls `AsyncSandbox.create(template="cluster::pool//image", secure=False, ...)`
|
|
90
|
+
|
|
91
|
+
`__init__` calls `super().__init__()` first, so Harbor's stock Dockerfile parsing still runs
|
|
92
|
+
(and sets `self._workdir` from the image's `WORKDIR`). The constructor then overrides
|
|
93
|
+
`self._template_name` with the Agent Sandbox pool shorthand.
|
|
94
|
+
|
|
95
|
+
At module import, `patch_e2b()` from
|
|
96
|
+
[`agent-sandbox-e2b`](https://pypi.org/project/agent-sandbox-e2b/) redirects the e2b SDK to
|
|
97
|
+
your Agent Sandbox endpoints.
|
|
98
|
+
|
|
99
|
+
See [INTEGRATION.md](INTEGRATION.md) for full design notes, the `--environment-import-path`
|
|
100
|
+
mechanism explanation, and operational guidance.
|
|
101
|
+
|
|
102
|
+
## Compatibility
|
|
103
|
+
|
|
104
|
+
Each release build is tested against the latest published versions of
|
|
105
|
+
`harbor` and `e2b`. The pinned upper bound in `[project.optional-dependencies]` is updated
|
|
106
|
+
automatically by the release CI to reflect the highest verified `harbor` version.
|
|
107
|
+
|
|
108
|
+
## License
|
|
109
|
+
|
|
110
|
+
Apache 2.0.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
agent_sandbox_harbor/__init__.py
|
|
4
|
+
agent_sandbox_harbor/environment.py
|
|
5
|
+
agent_sandbox_harbor.egg-info/PKG-INFO
|
|
6
|
+
agent_sandbox_harbor.egg-info/SOURCES.txt
|
|
7
|
+
agent_sandbox_harbor.egg-info/dependency_links.txt
|
|
8
|
+
agent_sandbox_harbor.egg-info/requires.txt
|
|
9
|
+
agent_sandbox_harbor.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
agent_sandbox_harbor
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "agent-sandbox-harbor"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "Agent Sandbox plugin for Harbor — run Terminal-Bench / SWE-bench / Harbor benchmarks on Agent Sandbox pools"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "Apache-2.0" }
|
|
11
|
+
requires-python = ">=3.12"
|
|
12
|
+
dependencies = [
|
|
13
|
+
"agent-sandbox-e2b>=0.0.2",
|
|
14
|
+
]
|
|
15
|
+
# harbor is an optional peer dependency installed by users via `harbor[e2b]`;
|
|
16
|
+
# we don't pin it as a hard dependency to avoid version conflicts with users
|
|
17
|
+
# who pin a specific harbor version themselves.
|
|
18
|
+
|
|
19
|
+
[project.optional-dependencies]
|
|
20
|
+
harbor = ["harbor>=0.7.0,<=0.7.1", "e2b>=2.0.0"]
|
|
21
|
+
dev = ["harbor>=0.7.0", "e2b>=2.0.0", "ruff>=0.4.0"]
|
|
22
|
+
|
|
23
|
+
[tool.setuptools.dynamic]
|
|
24
|
+
version = { attr = "agent_sandbox_harbor.__version__" }
|
|
25
|
+
|
|
26
|
+
[tool.setuptools.packages.find]
|
|
27
|
+
where = ["."]
|
|
28
|
+
include = ["agent_sandbox_harbor*"]
|
|
29
|
+
|
|
30
|
+
[project.urls]
|
|
31
|
+
Homepage = "https://github.com/scitix/agent-sandbox"
|
|
32
|
+
Source = "https://github.com/scitix/agent-sandbox/tree/main/sdk/python/harbor"
|
|
33
|
+
|
|
34
|
+
[tool.ruff]
|
|
35
|
+
line-length = 100
|
|
36
|
+
target-version = "py312"
|
|
37
|
+
|
|
38
|
+
[tool.ruff.lint]
|
|
39
|
+
select = ["E", "F", "I", "UP"]
|