diff --git a/agentkit/toolkit/cli/sandbox/cli_init.py b/agentkit/toolkit/cli/sandbox/cli_init.py index a8a8daa..db98aff 100644 --- a/agentkit/toolkit/cli/sandbox/cli_init.py +++ b/agentkit/toolkit/cli/sandbox/cli_init.py @@ -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.", ), } @@ -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}") @@ -75,6 +120,17 @@ 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", @@ -82,7 +138,9 @@ def init_command( "-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( @@ -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") diff --git a/agentkit/toolkit/resources/templates/sandbox/Dockerfile.aio-sandbox b/agentkit/toolkit/resources/templates/sandbox/Dockerfile.aio-sandbox new file mode 100644 index 0000000..12036f8 --- /dev/null +++ b/agentkit/toolkit/resources/templates/sandbox/Dockerfile.aio-sandbox @@ -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 diff --git a/agentkit/toolkit/resources/templates/sandbox/Dockerfile.install-package b/agentkit/toolkit/resources/templates/sandbox/Dockerfile.code-sandbox.install-package similarity index 100% rename from agentkit/toolkit/resources/templates/sandbox/Dockerfile.install-package rename to agentkit/toolkit/resources/templates/sandbox/Dockerfile.code-sandbox.install-package diff --git a/agentkit/toolkit/resources/templates/sandbox/Dockerfile.install-skills b/agentkit/toolkit/resources/templates/sandbox/Dockerfile.code-sandbox.install-skills similarity index 100% rename from agentkit/toolkit/resources/templates/sandbox/Dockerfile.install-skills rename to agentkit/toolkit/resources/templates/sandbox/Dockerfile.code-sandbox.install-skills diff --git a/agentkit/toolkit/resources/templates/sandbox/Dockerfile.web-server b/agentkit/toolkit/resources/templates/sandbox/Dockerfile.code-sandbox.web-server similarity index 100% rename from agentkit/toolkit/resources/templates/sandbox/Dockerfile.web-server rename to agentkit/toolkit/resources/templates/sandbox/Dockerfile.code-sandbox.web-server diff --git a/agentkit/toolkit/resources/templates/sandbox/Dockerfile.skill-sandbox b/agentkit/toolkit/resources/templates/sandbox/Dockerfile.skill-sandbox new file mode 100644 index 0000000..145ed5f --- /dev/null +++ b/agentkit/toolkit/resources/templates/sandbox/Dockerfile.skill-sandbox @@ -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 diff --git a/tests/toolkit/cli/test_cli_sandbox_init.py b/tests/toolkit/cli/test_cli_sandbox_init.py index dce3ea4..3d21fd5 100644 --- a/tests/toolkit/cli/test_cli_sandbox_init.py +++ b/tests/toolkit/cli/test_cli_sandbox_init.py @@ -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): @@ -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 --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 @@ -121,6 +186,11 @@ 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 @@ -128,6 +198,27 @@ def test_init_web_server_writes_default_template(monkeypatch, tmp_path): 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, @@ -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 )