Skip to content
Open
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
86 changes: 73 additions & 13 deletions agentkit/toolkit/cli/sandbox/cli_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,24 +38,69 @@ class DockerfileTemplate:
Path(__file__).resolve().parents[2] / "resources" / "templates" / "sandbox"
)

_PRIMARY_TEMPLATE_NAMES = (
"aio",
"skill",
"code",
"code-install-package",
"code-install-skills",
"code-web-server",
)

_AVAILABLE_TEMPLATES: dict[str, DockerfileTemplate] = {
"package": DockerfileTemplate(
name="package",
resource_path="Dockerfile.install-package",
default_output="Dockerfile.install-package",
description="Base code-cli image with additional npm packages installed.",
"aio": DockerfileTemplate(
name="aio",
resource_path="Dockerfile.aio-sandbox",
default_output="Dockerfile.aio-sandbox",
description="All-in-one sandbox image.",
),
"skill": DockerfileTemplate(
name="skill",
resource_path="Dockerfile.install-skills",
default_output="Dockerfile.install-skills",
description="Base code-cli image with local Codex skills copied in.",
resource_path="Dockerfile.skill-sandbox",
default_output="Dockerfile.skill-sandbox",
description="Skills sandbox image.",
),
"skills": DockerfileTemplate(
name="skills",
resource_path="Dockerfile.skill-sandbox",
default_output="Dockerfile.skill-sandbox",
description="Skills sandbox image.",
),
"code": DockerfileTemplate(
name="code",
resource_path="Dockerfile.code-sandbox.install-package",
default_output="Dockerfile.code-sandbox",
description="Code sandbox image with additional npm packages installed.",
),
"code-install-package": DockerfileTemplate(
name="code-install-package",
resource_path="Dockerfile.code-sandbox.install-package",
default_output="Dockerfile.code-sandbox.install-package",
description="Code sandbox image with additional npm packages installed.",
),
"code-install-skills": DockerfileTemplate(
name="code-install-skills",
resource_path="Dockerfile.code-sandbox.install-skills",
default_output="Dockerfile.code-sandbox.install-skills",
description="Code sandbox image with local Codex skills copied in.",
),
"code-web-server": DockerfileTemplate(
name="code-web-server",
resource_path="Dockerfile.code-sandbox.web-server",
default_output="Dockerfile.code-sandbox.web-server",
description="Code sandbox image with nginx routing to a local server.",
),
"package": DockerfileTemplate(
name="package",
resource_path="Dockerfile.code-sandbox.install-package",
default_output="Dockerfile.install-package",
description="Legacy alias for code-install-package.",
),
"web-server": DockerfileTemplate(
name="web-server",
resource_path="Dockerfile.web-server",
resource_path="Dockerfile.code-sandbox.web-server",
default_output="Dockerfile.web-server",
description="Base code-cli image with nginx routing to a local server.",
description="Legacy alias for code-web-server.",
),
}

Expand All @@ -64,7 +109,7 @@ def _resolve_template(template: str) -> DockerfileTemplate:
normalized = (template or "").strip()
if normalized in _AVAILABLE_TEMPLATES:
return _AVAILABLE_TEMPLATES[normalized]
valid = ", ".join(_AVAILABLE_TEMPLATES.keys())
valid = ", ".join(_PRIMARY_TEMPLATE_NAMES)
error(f"Unknown Dockerfile template '{template}'. Valid templates: {valid}")


Expand All @@ -75,14 +120,27 @@ def _read_template_file(template: DockerfileTemplate) -> str:
return path.read_text(encoding="utf-8")


def _render_template_content(content: str, output_name: str) -> str:
for dockerfile_name in (
"__SANDBOX_DOCKERFILE_NAME__",
"Dockerfile.install-package",
"Dockerfile.install-skills",
"Dockerfile.web-server",
):
content = content.replace(dockerfile_name, output_name)
return content


def init_command(
template: str = typer.Option(
"package",
"--template",
"-t",
help=(
"Dockerfile template to generate. Available: "
f"{', '.join(_AVAILABLE_TEMPLATES.keys())}."
f"{', '.join(_PRIMARY_TEMPLATE_NAMES)}. "
"Legacy aliases: package, web-server. "
"Also accepts skills as an alias for skill."
),
),
output: Optional[Path] = typer.Option(
Expand All @@ -107,7 +165,9 @@ def init_command(
error(f"{output_path} already exists. Use --force to overwrite it.")

try:
content = _read_template_file(selected)
content = _render_template_content(
_read_template_file(selected), output_path.name
)
output_path.parent.mkdir(parents=True, exist_ok=True)
output_path.write_text(content, encoding="utf-8")

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# All-in-one sandbox image
# Usage:
# 1. Add system, Python, Node.js, or browser dependencies below as needed.
# 2. agentkit sandbox build --dockerfile __SANDBOX_DOCKERFILE_NAME__

FROM enterprise-cn-shanghai-cn-shanghai.cr.volces.com/vefaas-public/all-in-one-sandbox:1.0.0.159

# Example:
# RUN python -m pip install --no-cache-dir pandas==2.2.3
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# All-in-one sandbox image
# Usage:
# 1. Add system, Python, Node.js, or browser dependencies below as needed.
# 2. agentkit sandbox build --dockerfile __SANDBOX_DOCKERFILE_NAME__

FROM enterprise-cn-shanghai-cn-shanghai.cr.volces.com/vefaas-public/agentkit-skills:1.0.5

# Example:
# RUN python -m pip install --no-cache-dir pandas==2.2.3
106 changes: 99 additions & 7 deletions tests/toolkit/cli/test_cli_sandbox_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,22 +36,42 @@ def test_init_package_writes_default_template(monkeypatch, tmp_path):
assert "Base image + npm package installation" in content
assert "agentkit sandbox build --dockerfile Dockerfile.install-package" in content
assert (
"FROM enterprise-public-cn-beijing.cr.volces.com/vefaas-public/code-cli:0.0.7"
"enterprise-public-cn-beijing.cr.volces.com/vefaas-public/code-cli:0.0.7"
in content
)
assert "is-even@1.0.0" in content


def test_init_code_writes_default_template(monkeypatch, tmp_path):
monkeypatch.chdir(tmp_path)
result = runner.invoke(
sandbox_app,
["init", "--template", "code"],
)

assert result.exit_code == 0
assert "Dockerfile template 'code' written to Dockerfile.code-sandbox" in (
result.output
)
content = open("Dockerfile.code-sandbox", encoding="utf-8").read()
assert "agentkit sandbox build --dockerfile Dockerfile.code-sandbox" in content
assert (
"enterprise-public-cn-beijing.cr.volces.com/vefaas-public/code-cli:0.0.7"
in content
)


def test_init_package_writes_custom_output_path(monkeypatch, tmp_path):
monkeypatch.chdir(tmp_path)
result = runner.invoke(
sandbox_app,
["init", "--template", "package", "-o", "./Dockerfile"],
["init", "--template", "code", "-o", "./Dockerfile"],
)

assert result.exit_code == 0
content = open("Dockerfile", encoding="utf-8").read()
assert 'ENV PATH="/opt/nodejs/22/bin:${PATH}"' in content
assert "agentkit sandbox build --dockerfile Dockerfile" in content


def test_init_refuses_to_overwrite_without_force(monkeypatch, tmp_path):
Expand Down Expand Up @@ -98,12 +118,57 @@ def test_init_skill_writes_default_template(monkeypatch, tmp_path):
)

assert result.exit_code == 0
assert "Dockerfile template 'skill' written to Dockerfile.install-skills" in (
assert "Dockerfile template 'skill' written to Dockerfile.skill-sandbox" in (
result.output
)
content = open("Dockerfile.skill-sandbox", encoding="utf-8").read()
assert "All-in-one sandbox image" in content
assert "agentkit sandbox build --dockerfile Dockerfile.skill-sandbox" in content
assert (
"enterprise-cn-shanghai-cn-shanghai.cr.volces.com/vefaas-public/"
"agentkit-skills:1.0.5"
in content
)


def test_init_skills_writes_default_template(monkeypatch, tmp_path):
monkeypatch.chdir(tmp_path)
result = runner.invoke(
sandbox_app,
["init", "--template", "skills"],
)

assert result.exit_code == 0
assert "Dockerfile template 'skills' written to Dockerfile.skill-sandbox" in (
result.output
)
content = open("Dockerfile.install-skills", encoding="utf-8").read()
assert "Base image + local Codex skills" in content
assert "agentkit skills init <skill-name> --path ./skills" in content
content = open("Dockerfile.skill-sandbox", encoding="utf-8").read()
assert "agentkit sandbox build --dockerfile Dockerfile.skill-sandbox" in content
assert (
"enterprise-cn-shanghai-cn-shanghai.cr.volces.com/vefaas-public/"
"agentkit-skills:1.0.5"
in content
)


def test_init_code_install_skills_writes_default_template(monkeypatch, tmp_path):
monkeypatch.chdir(tmp_path)
result = runner.invoke(
sandbox_app,
["init", "--template", "code-install-skills"],
)

assert result.exit_code == 0
assert (
"Dockerfile template 'code-install-skills' written to "
"Dockerfile.code-sandbox.install-skills"
in result.output
)
content = open("Dockerfile.code-sandbox.install-skills", encoding="utf-8").read()
assert (
"agentkit sandbox build --dockerfile Dockerfile.code-sandbox.install-skills"
in content
)
assert 'ENV CODEX_HOME="/home/gem/.codex"' in content
assert "COPY skills/ /home/gem/.codex/skills/" in content

Expand All @@ -121,13 +186,39 @@ def test_init_web_server_writes_default_template(monkeypatch, tmp_path):
)
content = open("Dockerfile.web-server", encoding="utf-8").read()
assert "Base image + nginx route + local server" in content
assert (
"enterprise-public-cn-beijing.cr.volces.com/vefaas-public/code-cli:0.0.7"
in content
)
assert "agentkit sandbox build --dockerfile Dockerfile.web-server" in content
assert 'ENV PUBLIC_PORT="8080"' in content
assert 'ENV APP_PORT="8000"' in content
assert "location /app/" in content
assert "proxy_pass http://127.0.0.1:8000/" in content
assert "nginx -g 'daemon off;'" in content


def test_init_aio_writes_default_template(monkeypatch, tmp_path):
monkeypatch.chdir(tmp_path)
result = runner.invoke(
sandbox_app,
["init", "--template", "aio"],
)

assert result.exit_code == 0
assert "Dockerfile template 'aio' written to Dockerfile.aio-sandbox" in (
result.output
)
content = open("Dockerfile.aio-sandbox", encoding="utf-8").read()
assert "All-in-one sandbox image" in content
assert "agentkit sandbox build --dockerfile Dockerfile.aio-sandbox" in content
assert (
"enterprise-cn-shanghai-cn-shanghai.cr.volces.com/vefaas-public/"
"all-in-one-sandbox:1.0.0.159"
in content
)


def test_init_unknown_template_exits_with_clear_error():
result = runner.invoke(
sandbox_app,
Expand All @@ -136,6 +227,7 @@ def test_init_unknown_template_exits_with_clear_error():

assert result.exit_code == 1
assert (
"Unknown Dockerfile template 'missing'. Valid templates: package, skill, web-server"
"Unknown Dockerfile template 'missing'. Valid templates: aio, skill, code, "
"code-install-package, code-install-skills, code-web-server"
in result.output
)