code-sandboxes 1.8.2__tar.gz → 1.9.2__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.
- code_sandboxes-1.9.2/.github/workflows/environments-live.yml +181 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/CHANGELOG.md +87 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/Makefile +5 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/PKG-INFO +2 -2
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/README.md +1 -1
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/__version__.py +1 -1
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/daytona_sandbox.py +14 -10
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/e2b_sandbox.py +11 -7
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/adapters/datalayer.py +64 -5
- code_sandboxes-1.9.2/code_sandboxes/environments/adapters/daytona.py +532 -0
- code_sandboxes-1.9.2/code_sandboxes/environments/adapters/e2b.py +533 -0
- code_sandboxes-1.9.2/code_sandboxes/environments/adapters/modal.py +565 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/resolve.py +27 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/modal_sandbox.py +76 -13
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/index.mdx +1 -1
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/providers/daytona.mdx +32 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/providers/e2b.mdx +31 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/providers/modal.mdx +30 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docusaurus.config.js +1 -1
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/examples/README.md +1 -1
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_daytona.py +20 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_e2b.py +20 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_environment_datalayer_builder.py +52 -1
- code_sandboxes-1.9.2/tests/test_environment_daytona_builder.py +786 -0
- code_sandboxes-1.9.2/tests/test_environment_e2b_builder.py +498 -0
- code_sandboxes-1.9.2/tests/test_environment_live_matrix.py +312 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_environment_managed_builders.py +59 -25
- code_sandboxes-1.9.2/tests/test_environment_modal_builder.py +746 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_environment_resolve.py +63 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_jupyter_server_streaming.py +14 -1
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_modal_google_colab_sandbox.py +88 -0
- code_sandboxes-1.8.2/code_sandboxes/environments/adapters/daytona.py +0 -90
- code_sandboxes-1.8.2/code_sandboxes/environments/adapters/e2b.py +0 -74
- code_sandboxes-1.8.2/code_sandboxes/environments/adapters/modal.py +0 -75
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/.github/workflows/build.yml +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/.github/workflows/py-code-style.yml +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/.github/workflows/py-tests.yml +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/.github/workflows/py-typing.yml +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/.github/workflows/reusable-python.yml +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/.gitignore +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/.licenserc.yaml +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/.pre-commit-config.yaml +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/.vscode/settings.json +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/Dockerfile +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/LICENSE +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/__init__.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/base.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/bridge_mount.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/builds.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/cli.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/client.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/cloudflare_sandbox.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/commands.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/console.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/contents.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/coreweave_sandbox.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/datalayer_sandbox.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/docker_sandbox.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/__init__.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/__main__.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/accounts.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/adapters/__init__.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/adapters/managed.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/attest.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/bases.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/breaker.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/build_secrets.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/builders.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/canonical.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/conformance.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/constraints/sandbox-contract-v1.txt +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/constraints/wheelhouse/README.md +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/constraints/wheelhouse/jupyter_server-2.21.0+datalayer.1-py3-none-any.whl +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/contract.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/doctor/__init__.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/doctor/build.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/doctor/datalayer_sandbox.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/doctor/report.schema.json +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/errors.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/files.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/image_import.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/lifecycle.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/policy.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/redact.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/schema.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/spec.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/eval_sandbox.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/exceptions.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/filesystem.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/google_colab.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/google_colab_sandbox.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/interfaces.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/jupyter_ingress.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/jupyter_server_sandbox.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/kaggle.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/kaggle_execute.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/kaggle_live.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/kaggle_sandbox.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/lifecycle.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/manage.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/models.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/monty_sandbox.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/provider_ingress.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/providers.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docker/Dockerfile +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/.gitignore +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/LICENSE +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/Makefile +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/README.md +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/babel.config.js +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/api-reference/_category_.yml +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/api-reference/index.mdx +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/cli/_category_.yml +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/cli/index.mdx +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/contribute/_category_.yml +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/contribute/index.mdx +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/environments/_category_.yml +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/environments/contract.mdx +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/environments/index.mdx +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/environments/specification.mdx +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/environments/spike-2026-09.mdx +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/examples/_category_.yml +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/examples/index.mdx +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/guide/_category_.yml +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/guide/index.mdx +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/install/_category_.yml +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/install/index.mdx +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/provider-ingress.mdx +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/providers/_category_.yml +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/providers/cloudflare.mdx +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/providers/coreweave.mdx +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/providers/datalayer.mdx +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/providers/docker.mdx +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/providers/eval.mdx +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/providers/google-colab.mdx +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/providers/index.mdx +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/providers/jupyter-server.mdx +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/providers/kaggle.mdx +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/providers/monty.mdx +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/package.json +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/sidebars.js +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/src/components/HomepageRow1.js +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/src/components/HomepageRow1.module.css +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/src/components/HomepageRow2.js +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/src/components/HomepageRow2.module.css +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/src/components/HomepageRow3.js +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/src/components/HomepageRow3.module.css +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/src/css/custom.css +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/src/pages/index.module.css +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/src/pages/markdown-page.md +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/src/theme/CustomDocItem.tsx +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/src/theme/IconsTitle.tsx +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/static/img/bricks.svg +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/static/img/building-construction.svg +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/static/img/cloud-sun.svg +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/static/img/cloud.svg +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/static/img/collaboration.svg +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/static/img/copy-left.svg +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/static/img/datalayer/logo.png +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/static/img/datalayer/logo.svg +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/static/img/datalayer/personas/business.svg +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/static/img/datalayer/personas/data-scientist.svg +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/static/img/datalayer/personas/devops.svg +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/static/img/datalayer/project/project-line.svg +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/static/img/favicon.ico +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/static/img/jupyter.svg +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/static/img/open-source.svg +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/static/img/react-js.svg +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/static/img/react-js2.svg +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/static/img/ringed-planet.svg +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/static/img/robot.svg +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/static/img/rocket.svg +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/static/img/shield.svg +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/static/img/sparkles.svg +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/static/img/streamlit.gif +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/static/img/super-hero.svg +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/static/img/target.svg +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/static/img/thumbs-up.svg +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/examples/exec/Makefile +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/examples/exec/cloudflare_sandbox_example.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/examples/exec/coreweave_sandbox_example.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/examples/exec/datalayer_sandbox_example.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/examples/exec/daytona_sandbox_example.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/examples/exec/docker_sandbox_example.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/examples/exec/e2b_sandbox_example.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/examples/exec/eval_sandbox_example.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/examples/exec/google_colab_sandbox_example.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/examples/exec/jupyter_server_sandbox_example.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/examples/exec/kaggle_sandbox_example.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/examples/exec/modal_sandbox_example.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/examples/exec/monty_sandbox_example.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/examples/repl/Makefile +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/examples/repl/cloudflare_sandbox_example.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/examples/repl/coreweave_sandbox_example.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/examples/repl/datalayer_sandbox_example.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/examples/repl/daytona_sandbox_example.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/examples/repl/docker_sandbox_example.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/examples/repl/e2b_sandbox_example.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/examples/repl/eval_sandbox_example.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/examples/repl/google_colab_sandbox_example.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/examples/repl/jupyter_server_sandbox_example.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/examples/repl/kaggle_sandbox_example.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/examples/repl/modal_sandbox_example.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/examples/repl/monty_sandbox_example.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/pyproject.toml +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/schemas/environment-v1alpha1.json +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/__init__.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/conftest.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_a_datalayer_sandbox_can_be_interrupted.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_bridge_mount.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_builds.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_cli_exec.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_cli_repl.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_client.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_client_contents.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_client_files.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_client_outcome_error_output.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_cloudflare.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_commands_envelope.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_console.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_contents_contract.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_contents_live_matrix.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_coreweave.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_datalayer_sandbox.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_environment_accounts.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_environment_attest.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_environment_bases.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_environment_breaker.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_environment_build_secrets.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_environment_builders.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_environment_canonical.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_environment_conformance.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_environment_constraints.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_environment_contract.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_environment_cross_variant_report.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_environment_doctor.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_environment_errors.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_environment_image_import.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_environment_lifecycle.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_environment_redact.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_environment_spec.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_eval.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_every_variant_has_a_real_interrupt.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_factory.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_google_colab.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_integration.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_jupyter_ingress.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_jupyter_server.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_kaggle.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_kaggle_execute.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_kaggle_live.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_kaggle_session.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_kernel_client_compatibility.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_lifecycle_vocabulary.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_manage.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_manage_datalayer.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_modal_session.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_models.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_output_line_fidelity.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_provider_catalog_names.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_provider_ingress.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_providers.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_sandboxes.py +0 -0
- {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_the_kill_relaunch_drill.py +0 -0
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
name: Environments Live Matrix
|
|
2
|
+
|
|
3
|
+
# The managed Environment builders (E2-03, E2-04, E2-05) against the real
|
|
4
|
+
# providers, nightly (PLAN_ENV.md E2-11): a real, hash-verified lock, a real
|
|
5
|
+
# build, a real launch, and the formal core tier — tests/test_environment_live_matrix.py.
|
|
6
|
+
#
|
|
7
|
+
# E2B and Modal carry their own already-documented, unticked gaps there
|
|
8
|
+
# (E2-03's uid/gid mismatch; modal_sandbox.py's missing setpriv) and are
|
|
9
|
+
# `xfail(strict=False)` in that file, so an ordinary failure there is
|
|
10
|
+
# expected and does not fail this workflow. Daytona carries no such marker:
|
|
11
|
+
# a failure there is a real regression, and this workflow opens (or
|
|
12
|
+
# comments on) an issue naming it.
|
|
13
|
+
#
|
|
14
|
+
# Needs, as repository secrets: E2B_API_KEY, DAYTONA_API_KEY, MODAL_TOKEN_ID,
|
|
15
|
+
# MODAL_TOKEN_SECRET, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY — the last two
|
|
16
|
+
# scoped to read the approved base from ECR only (D-18). A secret that is
|
|
17
|
+
# not set is not a workflow failure: the test each secret belongs to skips,
|
|
18
|
+
# by name, and says so in the job summary.
|
|
19
|
+
|
|
20
|
+
on:
|
|
21
|
+
schedule:
|
|
22
|
+
# 04:17 UTC: off-peak for every provider this reaches, and away from the
|
|
23
|
+
# hour boundary every other scheduled workflow in this org tends to pick.
|
|
24
|
+
- cron: '17 4 * * *'
|
|
25
|
+
workflow_dispatch: {}
|
|
26
|
+
|
|
27
|
+
concurrency:
|
|
28
|
+
group: ${{ github.workflow }}
|
|
29
|
+
cancel-in-progress: false
|
|
30
|
+
|
|
31
|
+
# The default token on this repository is read-only: without this, a genuine
|
|
32
|
+
# matrix failure would still 403 trying to label and open the issue that is
|
|
33
|
+
# meant to report it (found in review).
|
|
34
|
+
permissions:
|
|
35
|
+
contents: read
|
|
36
|
+
issues: write
|
|
37
|
+
|
|
38
|
+
jobs:
|
|
39
|
+
live-matrix:
|
|
40
|
+
runs-on: ubuntu-latest
|
|
41
|
+
timeout-minutes: 45
|
|
42
|
+
steps:
|
|
43
|
+
- name: Checkout
|
|
44
|
+
uses: actions/checkout@v6
|
|
45
|
+
|
|
46
|
+
- name: Setup UV
|
|
47
|
+
uses: astral-sh/setup-uv@v7
|
|
48
|
+
with:
|
|
49
|
+
version: latest
|
|
50
|
+
python-version: '3.13'
|
|
51
|
+
activate-environment: true
|
|
52
|
+
|
|
53
|
+
- name: Install dependencies
|
|
54
|
+
run: |
|
|
55
|
+
uv pip install ".[e2b,daytona,modal,test]"
|
|
56
|
+
uv pip install boto3
|
|
57
|
+
|
|
58
|
+
- name: Configure AWS credentials
|
|
59
|
+
run: |
|
|
60
|
+
mkdir -p ~/.aws
|
|
61
|
+
{
|
|
62
|
+
echo "[default]"
|
|
63
|
+
echo "aws_access_key_id=${AWS_ACCESS_KEY_ID}"
|
|
64
|
+
echo "aws_secret_access_key=${AWS_SECRET_ACCESS_KEY}"
|
|
65
|
+
} > ~/.aws/credentials
|
|
66
|
+
env:
|
|
67
|
+
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
|
68
|
+
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
|
69
|
+
|
|
70
|
+
- name: Run the live matrix
|
|
71
|
+
id: live
|
|
72
|
+
continue-on-error: true
|
|
73
|
+
run: |
|
|
74
|
+
CODE_SANDBOXES_LIVE=1 pytest -q -m live \
|
|
75
|
+
tests/test_environment_live_matrix.py \
|
|
76
|
+
--junitxml=live-matrix-results.xml
|
|
77
|
+
env:
|
|
78
|
+
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
|
79
|
+
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
|
80
|
+
AWS_REGION: us-east-1
|
|
81
|
+
E2B_API_KEY: ${{ secrets.E2B_API_KEY }}
|
|
82
|
+
DAYTONA_API_KEY: ${{ secrets.DAYTONA_API_KEY }}
|
|
83
|
+
MODAL_TOKEN_ID: ${{ secrets.MODAL_TOKEN_ID }}
|
|
84
|
+
MODAL_TOKEN_SECRET: ${{ secrets.MODAL_TOKEN_SECRET }}
|
|
85
|
+
|
|
86
|
+
- name: Upload the JUnit report
|
|
87
|
+
if: always()
|
|
88
|
+
uses: actions/upload-artifact@v4
|
|
89
|
+
with:
|
|
90
|
+
name: environments-live-results
|
|
91
|
+
path: live-matrix-results.xml
|
|
92
|
+
retention-days: 14
|
|
93
|
+
|
|
94
|
+
- name: Summarize genuine failures (not xfail) naming provider and check
|
|
95
|
+
if: steps.live.outcome == 'failure'
|
|
96
|
+
run: |
|
|
97
|
+
python - <<'PY'
|
|
98
|
+
import xml.etree.ElementTree as ET
|
|
99
|
+
|
|
100
|
+
tree = ET.parse("live-matrix-results.xml")
|
|
101
|
+
lines = []
|
|
102
|
+
for testcase in tree.getroot().iter("testcase"):
|
|
103
|
+
# An expected-fail (E2B/Modal's own documented gaps) is reported
|
|
104
|
+
# by pytest's junit writer as its own case with neither
|
|
105
|
+
# <failure> nor <error> — only a genuine, un-marked failure
|
|
106
|
+
# (Daytona, or a real regression anywhere else) has one.
|
|
107
|
+
node = testcase.find("failure")
|
|
108
|
+
if node is None:
|
|
109
|
+
node = testcase.find("error")
|
|
110
|
+
if node is None:
|
|
111
|
+
continue
|
|
112
|
+
name = f"{testcase.get('classname')}::{testcase.get('name')}"
|
|
113
|
+
detail = (node.get("message") or node.text or "").strip()
|
|
114
|
+
short = "\n".join(detail.splitlines()[:20])
|
|
115
|
+
lines.append(f"### `{name}`\n\n```\n{short}\n```")
|
|
116
|
+
|
|
117
|
+
with open("failures.md", "w") as handle:
|
|
118
|
+
handle.write("\n\n".join(lines))
|
|
119
|
+
print(f"{len(lines)} genuine failure(s) found")
|
|
120
|
+
PY
|
|
121
|
+
|
|
122
|
+
- name: Ensure the environments-live label exists
|
|
123
|
+
if: steps.live.outcome == 'failure'
|
|
124
|
+
continue-on-error: true # already existing is not an error
|
|
125
|
+
uses: actions/github-script@v7
|
|
126
|
+
with:
|
|
127
|
+
script: |
|
|
128
|
+
await github.rest.issues.createLabel({
|
|
129
|
+
owner: context.repo.owner,
|
|
130
|
+
repo: context.repo.repo,
|
|
131
|
+
name: 'environments-live',
|
|
132
|
+
color: 'b60205',
|
|
133
|
+
description: 'The nightly Environments live matrix (E2-11)',
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
- name: Open or update an issue naming the provider and the check
|
|
137
|
+
if: steps.live.outcome == 'failure'
|
|
138
|
+
uses: actions/github-script@v7
|
|
139
|
+
with:
|
|
140
|
+
script: |
|
|
141
|
+
const fs = require('fs');
|
|
142
|
+
const detail = fs.readFileSync('failures.md', 'utf8').trim();
|
|
143
|
+
if (!detail) {
|
|
144
|
+
// Every failure was inside an `xfail` — nothing genuinely
|
|
145
|
+
// broke (see the module docstring's own reasoning).
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
const runUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`;
|
|
149
|
+
const body = [
|
|
150
|
+
`The nightly [environments live matrix](${runUrl}) failed for real — not the two already-documented \`xfail\` gaps (E2-03's uid/gid, Modal's missing \`setpriv\`).`,
|
|
151
|
+
'',
|
|
152
|
+
detail,
|
|
153
|
+
].join('\n');
|
|
154
|
+
const title = 'environments-live: nightly failure';
|
|
155
|
+
const existing = await github.rest.issues.listForRepo({
|
|
156
|
+
owner: context.repo.owner,
|
|
157
|
+
repo: context.repo.repo,
|
|
158
|
+
state: 'open',
|
|
159
|
+
labels: 'environments-live',
|
|
160
|
+
});
|
|
161
|
+
const found = existing.data.find((issue) => issue.title === title);
|
|
162
|
+
if (found) {
|
|
163
|
+
await github.rest.issues.createComment({
|
|
164
|
+
owner: context.repo.owner,
|
|
165
|
+
repo: context.repo.repo,
|
|
166
|
+
issue_number: found.number,
|
|
167
|
+
body,
|
|
168
|
+
});
|
|
169
|
+
} else {
|
|
170
|
+
await github.rest.issues.create({
|
|
171
|
+
owner: context.repo.owner,
|
|
172
|
+
repo: context.repo.repo,
|
|
173
|
+
title,
|
|
174
|
+
body,
|
|
175
|
+
labels: ['environments-live'],
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
- name: Fail the job if something genuinely broke
|
|
180
|
+
if: steps.live.outcome == 'failure'
|
|
181
|
+
run: exit 1
|
|
@@ -8,6 +8,93 @@
|
|
|
8
8
|
|
|
9
9
|
## Unreleased
|
|
10
10
|
|
|
11
|
+
## 1.9.2
|
|
12
|
+
|
|
13
|
+
- **A real ECR repository name is lowercase; a real owner uid is not**
|
|
14
|
+
(`environments.adapters.datalayer.owner_repository`, new
|
|
15
|
+
`owner_cache_repository`; PLAN_ENV.md E1-07). Found live, 2026-09-14, the
|
|
16
|
+
first real Datalayer build ever run for a real account's own uid rather
|
|
17
|
+
than a lowercase test fixture: `DescribeRepositories` refused outright,
|
|
18
|
+
"Invalid parameter at 'repositoryName'" — this project's own uids are
|
|
19
|
+
ULIDs, conventionally uppercase, and ECR repository names match
|
|
20
|
+
`[a-z0-9]+((\.|_|__|-+)[a-z0-9]+)*` per path segment. Both the owner's own
|
|
21
|
+
repository and the owner's cache repository are lowered at the one place
|
|
22
|
+
each is built, so every caller (create, inspect, exists, delete, the
|
|
23
|
+
cache import/export) stays consistent with itself.
|
|
24
|
+
|
|
25
|
+
## 1.9.1
|
|
26
|
+
|
|
27
|
+
- **mTLS to the build pool's `buildkitd`** (`environments.adapters.datalayer.Builder`,
|
|
28
|
+
`environments.resolve.BuildkitResolveRunner`; PLAN_ENV.md E1-06/E1-07). Both
|
|
29
|
+
drivers of `buildctl` took only `--addr`, which is what every prior live
|
|
30
|
+
drill needed against a plain-socket, ephemeral `buildkitd` — never a real
|
|
31
|
+
one. The build pool's real daemon on r1 takes mTLS connections only, so
|
|
32
|
+
reaching it needed three more flags neither driver had: `--tlscert`,
|
|
33
|
+
`--tlskey` and `--tlscacert`, each read from `DATALAYER_BUILDKIT_TLSCERT`,
|
|
34
|
+
`DATALAYER_BUILDKIT_TLSKEY` and `DATALAYER_BUILDKIT_TLSCACERT` when not
|
|
35
|
+
passed explicitly (`Builder`, matching how it already reads
|
|
36
|
+
`DATALAYER_BUILDKIT_ADDR`) or passed in by the caller (`BuildkitResolveRunner`,
|
|
37
|
+
constructed by durable's own `activities_environments.py`, which now reads
|
|
38
|
+
and forwards the same three). All three or none — a partial set is treated
|
|
39
|
+
as none, since a `buildkitd` requiring mTLS refuses a client carrying only
|
|
40
|
+
some of them at the daemon, with a less useful error than refusing here.
|
|
41
|
+
|
|
42
|
+
## 1.9.0
|
|
43
|
+
|
|
44
|
+
- **The Daytona builder** (`environments.adapters.daytona`, PLAN_ENV.md
|
|
45
|
+
E2-04). A snapshot built from the approved Datalayer base — `uv pip sync --require-hashes` against the resolved lock, your files, `postInstall`
|
|
46
|
+
commands, and an explicit `tini -- sleep infinity` entrypoint — with the
|
|
47
|
+
owner's own Daytona organization, never this package's ambient
|
|
48
|
+
credentials. Confirmed live against a real, hash-verified lock: a real
|
|
49
|
+
snapshot reaches `Active` and a sandbox launched from it passes the full
|
|
50
|
+
sandbox contract, doctor included — the one managed variant, of the
|
|
51
|
+
three landing here, whose build-time state and launched state fully
|
|
52
|
+
agree.
|
|
53
|
+
- **The E2B builder** (`environments.adapters.e2b`, PLAN_ENV.md E2-03). A
|
|
54
|
+
template built from `code-interpreter-v1` — E2B's own proprietary
|
|
55
|
+
code-interpreter server ships baked into it only, so the build starts
|
|
56
|
+
there rather than from the Datalayer base and reconciles whatever it
|
|
57
|
+
shipped to exactly what the lock pins. Two live-found identity bugs are
|
|
58
|
+
fixed: `code-interpreter-v1` already holds an account at uid 1000 and a
|
|
59
|
+
group at gid 100 of its own, so the old `useradd` was silently landing
|
|
60
|
+
elsewhere — the build now renames the existing account instead — and the
|
|
61
|
+
base sets no locale at all, now set explicitly. A real build passes the
|
|
62
|
+
contract's doctor check in full. **Still open:** a launched sandbox
|
|
63
|
+
currently runs as root, not the contract's `1000:100` — the two
|
|
64
|
+
`systemd` services that actually execute a launched sandbox's code have
|
|
65
|
+
no `User=` of their own, and E2B's private server hardcodes `/home/user`
|
|
66
|
+
as the working directory, independent of anything the build sets.
|
|
67
|
+
- **The Modal builder** (`environments.adapters.modal`, PLAN_ENV.md E2-05).
|
|
68
|
+
An image built `from_aws_ecr` off the approved base, with a per-build
|
|
69
|
+
Secret made and torn down around it. The contract's own identity is put
|
|
70
|
+
back at launch: `modal_sandbox.py`'s session driver now drops itself to
|
|
71
|
+
`1000:100` for a sandbox launched from a built Environments artifact
|
|
72
|
+
specifically (gated on `image_id`, so general Modal sandbox usage
|
|
73
|
+
elsewhere is unaffected) — Modal ignores the image's own `USER`, so this
|
|
74
|
+
is the only place it can be restored. A separate, previously
|
|
75
|
+
silently-fatal bug is also fixed: the real launcher creates the sandbox
|
|
76
|
+
with no command arguments at all, so an entrypoint relying on `exec "$@"` alone was a no-op and every launched environment exited within
|
|
77
|
+
seconds; the entrypoint now falls back to `sleep infinity` when given
|
|
78
|
+
none. **Still open:** two of the core tier's checks (imports, filesystem)
|
|
79
|
+
still fail on a launched sandbox, for a reason not yet root-caused —
|
|
80
|
+
confirmed specific to a contract-built image under repeated `exec`, not
|
|
81
|
+
the identity fix.
|
|
82
|
+
- **A real, shared bug in all three managed launchers, found in review**:
|
|
83
|
+
`start()` creates the remote sandbox well before it marks itself
|
|
84
|
+
started, while `stop()` was guarded on that flag rather than on the
|
|
85
|
+
resource itself — a failure in between left a real, running sandbox
|
|
86
|
+
that `stop()` skipped entirely, orphaned for good. Fixed identically in
|
|
87
|
+
`daytona_sandbox.py`, `modal_sandbox.py` and `e2b_sandbox.py`.
|
|
88
|
+
- **A nightly live matrix for the three managed builders**
|
|
89
|
+
(`.github/workflows/environments-live.yml`, PLAN_ENV.md E2-11). Builds a
|
|
90
|
+
real artifact on each real provider from a real, hash-verified lock,
|
|
91
|
+
launches a sandbox from it, and runs the formal core tier — opening (or
|
|
92
|
+
commenting on) an issue naming the provider and the check on a genuine
|
|
93
|
+
failure. E2B and Modal run `xfail(strict=False)` for their own
|
|
94
|
+
already-documented gaps above; Daytona carries no such marker. Needs
|
|
95
|
+
provider secrets added to the repository before it does anything for
|
|
96
|
+
real.
|
|
97
|
+
|
|
11
98
|
## 1.8.2
|
|
12
99
|
|
|
13
100
|
- **A sandbox can be asked whether it is still alive, and the answer no longer
|
|
@@ -54,6 +54,11 @@ live-matrix:
|
|
|
54
54
|
kill-relaunch:
|
|
55
55
|
CODE_SANDBOXES_LIVE=1 pytest -q -m live tests/test_the_kill_relaunch_drill.py
|
|
56
56
|
|
|
57
|
+
## The managed Environment builders against the real providers: a real
|
|
58
|
+
## resolved lock, build, launch, and the formal core tier (E2-11).
|
|
59
|
+
environments-live:
|
|
60
|
+
CODE_SANDBOXES_LIVE=1 pytest -q -m live tests/test_environment_live_matrix.py
|
|
61
|
+
|
|
57
62
|
clean: ## clean
|
|
58
63
|
git clean -fdx
|
|
59
64
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: code_sandboxes
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.9.2
|
|
4
4
|
Project-URL: Home, https://github.com/datalayer/code-sandboxes
|
|
5
5
|
Author-email: Datalayer <info@datalayer.io>
|
|
6
6
|
License: BSD 3-Clause License
|
|
@@ -109,7 +109,7 @@ Description-Content-Type: text/markdown
|
|
|
109
109
|
[](https://github.com/sponsors/datalayer)
|
|
110
110
|
[](https://pypi.org/project/code-sandboxes)
|
|
111
111
|
|
|
112
|
-
#
|
|
112
|
+
# 📦 Code Sandboxes
|
|
113
113
|
|
|
114
114
|
Code Sandboxes (`code_sandboxes`) is a Python package for running code in isolated sandbox variants through a unified API.
|
|
115
115
|
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
[](https://github.com/sponsors/datalayer)
|
|
10
10
|
[](https://pypi.org/project/code-sandboxes)
|
|
11
11
|
|
|
12
|
-
#
|
|
12
|
+
# 📦 Code Sandboxes
|
|
13
13
|
|
|
14
14
|
Code Sandboxes (`code_sandboxes`) is a Python package for running code in isolated sandbox variants through a unified API.
|
|
15
15
|
|
|
@@ -655,17 +655,21 @@ class DaytonaSandbox(Sandbox):
|
|
|
655
655
|
)
|
|
656
656
|
|
|
657
657
|
def stop(self) -> None:
|
|
658
|
-
|
|
658
|
+
# Guarded on the resource itself, not `_started` (found in review):
|
|
659
|
+
# `start()` creates the remote sandbox well before it marks itself
|
|
660
|
+
# started, so a failure in between — `create_context`, building
|
|
661
|
+
# `SandboxInfo` — would otherwise leave a real, running sandbox that
|
|
662
|
+
# `stop()` skips entirely and this object can never clean up again.
|
|
663
|
+
if self._sandbox is None:
|
|
659
664
|
return
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
self._sandbox = None
|
|
665
|
+
try:
|
|
666
|
+
if self._delete_on_stop:
|
|
667
|
+
self._sandbox.delete()
|
|
668
|
+
else:
|
|
669
|
+
self._sandbox.stop()
|
|
670
|
+
except Exception:
|
|
671
|
+
logger.debug("Ignoring error while stopping the Daytona sandbox", exc_info=True)
|
|
672
|
+
self._sandbox = None
|
|
669
673
|
self._daytona = None
|
|
670
674
|
self._jupyter_endpoint = None
|
|
671
675
|
self._contexts.clear()
|
|
@@ -419,14 +419,18 @@ class E2BSandbox(Sandbox):
|
|
|
419
419
|
return self._allow_internet_access
|
|
420
420
|
|
|
421
421
|
def stop(self) -> None:
|
|
422
|
-
|
|
422
|
+
# Guarded on the resource itself, not `_started` (found in review):
|
|
423
|
+
# `start()` creates the remote sandbox well before it marks itself
|
|
424
|
+
# started, so a failure in between — `create_context`, building
|
|
425
|
+
# `SandboxInfo` — would otherwise leave a real, running sandbox that
|
|
426
|
+
# `stop()` skips entirely and this object can never clean up again.
|
|
427
|
+
if self._sandbox is None:
|
|
423
428
|
return
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
self._sandbox = None
|
|
429
|
+
try:
|
|
430
|
+
self._sandbox.kill()
|
|
431
|
+
except Exception:
|
|
432
|
+
logger.debug("Ignoring error while killing the E2B sandbox", exc_info=True)
|
|
433
|
+
self._sandbox = None
|
|
430
434
|
self._contexts.clear()
|
|
431
435
|
self._jupyter_endpoint = None
|
|
432
436
|
self._volume_mounts.stopped()
|
{code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/adapters/datalayer.py
RENAMED
|
@@ -71,7 +71,12 @@ from ..files import files_step
|
|
|
71
71
|
from ..resolve import WHEELHOUSE_IMAGE_PATH, WHEELHOUSE_PATH, apt_pins_in, locked_versions
|
|
72
72
|
from ..spec import BuildSecret, Environment
|
|
73
73
|
|
|
74
|
-
__all__ = [
|
|
74
|
+
__all__ = [
|
|
75
|
+
"ECR_ENVIRONMENT_PREFIX",
|
|
76
|
+
"Builder",
|
|
77
|
+
"owner_cache_repository",
|
|
78
|
+
"owner_repository",
|
|
79
|
+
]
|
|
75
80
|
|
|
76
81
|
#: Where an owner's environments live in ECR (D-10).
|
|
77
82
|
ECR_ENVIRONMENT_PREFIX = "environments/u/"
|
|
@@ -89,10 +94,32 @@ DEFAULT_MAX_ARTIFACT_BYTES = 20 * 1024**3
|
|
|
89
94
|
|
|
90
95
|
|
|
91
96
|
def owner_repository(owner_uid: str, environment_name: str) -> str:
|
|
92
|
-
"""The owner's repository for one environment: ``environments/u/<owner>/<name>``.
|
|
97
|
+
"""The owner's repository for one environment: ``environments/u/<owner>/<name>``.
|
|
98
|
+
|
|
99
|
+
Lowercased: an ECR repository name matches
|
|
100
|
+
``[a-z0-9]+((\\.|_|__|-+)[a-z0-9]+)*`` per path segment, and this
|
|
101
|
+
project's own uids are ULIDs, conventionally uppercase — found live,
|
|
102
|
+
2026-09-14, the first real build ever run for a real account's own
|
|
103
|
+
uid rather than a lowercase test fixture: ``DescribeRepositories``
|
|
104
|
+
refused it outright, "Invalid parameter at 'repositoryName'". Lowering
|
|
105
|
+
both segments here, the one place this reference is built, keeps every
|
|
106
|
+
caller (create, inspect, exists, delete) consistent with itself without
|
|
107
|
+
each needing to remember to.
|
|
108
|
+
"""
|
|
93
109
|
if not owner_uid or not environment_name:
|
|
94
110
|
raise ValueError("an owner and an environment name make the repository")
|
|
95
|
-
return f"{ECR_ENVIRONMENT_PREFIX}{owner_uid}/{environment_name}"
|
|
111
|
+
return f"{ECR_ENVIRONMENT_PREFIX}{owner_uid}/{environment_name}".lower()
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def owner_cache_repository(owner_uid: str) -> str:
|
|
115
|
+
"""The owner's own BuildKit cache repository: ``environments/cache/u/<owner>``.
|
|
116
|
+
|
|
117
|
+
Lowercased for the same reason ``owner_repository`` is (a ULID, not a
|
|
118
|
+
lowercase test fixture, is what a real build actually names).
|
|
119
|
+
"""
|
|
120
|
+
if not owner_uid:
|
|
121
|
+
raise ValueError("an owner makes the cache repository")
|
|
122
|
+
return f"environments/cache/u/{owner_uid}".lower()
|
|
96
123
|
|
|
97
124
|
|
|
98
125
|
def _secret_mount(secret: BuildSecret) -> str:
|
|
@@ -127,6 +154,15 @@ class Builder:
|
|
|
127
154
|
looks like (E1-06).
|
|
128
155
|
address
|
|
129
156
|
``buildkitd``'s address, when it is not the default socket.
|
|
157
|
+
tlscert, tlskey, tlscacert
|
|
158
|
+
Paths to the client certificate, its key, and the CA that signed
|
|
159
|
+
``buildkitd``'s own server certificate — the build pool's daemon
|
|
160
|
+
takes mTLS connections only (PLAN_ENV.md E1-06), so a ``tcp://``
|
|
161
|
+
``address`` with none of these set fails the handshake, not the
|
|
162
|
+
build. Unset (the default) is right for a plain-socket
|
|
163
|
+
``buildkitd``, such as ``plane local``'s own ephemeral one, which
|
|
164
|
+
was every ``buildkitd`` this adapter had ever actually built
|
|
165
|
+
against before the pool on r1 existed.
|
|
130
166
|
region
|
|
131
167
|
The registry's region, for the ECR API calls.
|
|
132
168
|
ecr
|
|
@@ -145,6 +181,9 @@ class Builder:
|
|
|
145
181
|
credential: Any = None,
|
|
146
182
|
buildctl: str | None = None,
|
|
147
183
|
address: str | None = None,
|
|
184
|
+
tlscert: str | None = None,
|
|
185
|
+
tlskey: str | None = None,
|
|
186
|
+
tlscacert: str | None = None,
|
|
148
187
|
region: str | None = None,
|
|
149
188
|
ecr: Any = None,
|
|
150
189
|
run: Callable[..., subprocess.CompletedProcess[str]] | None = None,
|
|
@@ -155,6 +194,9 @@ class Builder:
|
|
|
155
194
|
self._credential = credential
|
|
156
195
|
self._buildctl = (shutil.which("buildctl") or "") if buildctl is None else buildctl
|
|
157
196
|
self._address = address or os.environ.get("DATALAYER_BUILDKIT_ADDR", "").strip()
|
|
197
|
+
self._tlscert = tlscert or os.environ.get("DATALAYER_BUILDKIT_TLSCERT", "").strip()
|
|
198
|
+
self._tlskey = tlskey or os.environ.get("DATALAYER_BUILDKIT_TLSKEY", "").strip()
|
|
199
|
+
self._tlscacert = tlscacert or os.environ.get("DATALAYER_BUILDKIT_TLSCACERT", "").strip()
|
|
158
200
|
self._region = region or os.environ.get("AWS_REGION", "us-east-1")
|
|
159
201
|
self._ecr = ecr
|
|
160
202
|
self._run = run or subprocess.run
|
|
@@ -359,7 +401,7 @@ class Builder:
|
|
|
359
401
|
repository = owner_repository(request.owner_uid, request.environment.metadata.name)
|
|
360
402
|
registry = self._registry_host()
|
|
361
403
|
self._ensure_repository(repository)
|
|
362
|
-
self._ensure_cache_repository(
|
|
404
|
+
self._ensure_cache_repository(owner_cache_repository(request.owner_uid))
|
|
363
405
|
tag = f"v{request.version}-{request.build_uid}"
|
|
364
406
|
reference = f"{registry}/{repository}:{tag}"
|
|
365
407
|
with tempfile.TemporaryDirectory(prefix="dl-build-") as directory:
|
|
@@ -384,6 +426,7 @@ class Builder:
|
|
|
384
426
|
command = [
|
|
385
427
|
self._buildctl,
|
|
386
428
|
*(["--addr", self._address] if self._address else []),
|
|
429
|
+
*self._tls_options(),
|
|
387
430
|
"build",
|
|
388
431
|
"--frontend",
|
|
389
432
|
"dockerfile.v0",
|
|
@@ -534,6 +577,22 @@ class Builder:
|
|
|
534
577
|
self._ecr = boto3.client("ecr", region_name=self._region)
|
|
535
578
|
return self._ecr
|
|
536
579
|
|
|
580
|
+
def _tls_options(self) -> list[str]:
|
|
581
|
+
"""The ``buildctl`` global flags for the build pool's mTLS (E1-06).
|
|
582
|
+
|
|
583
|
+
All three or none: a `buildkitd` that takes mTLS refuses a client
|
|
584
|
+
with only some of them, so a partial set here would fail at the
|
|
585
|
+
daemon with a confusing error rather than a clear one about the
|
|
586
|
+
deployment's own configuration.
|
|
587
|
+
"""
|
|
588
|
+
if not (self._tlscert and self._tlskey and self._tlscacert):
|
|
589
|
+
return []
|
|
590
|
+
return [
|
|
591
|
+
f"--tlscert={self._tlscert}",
|
|
592
|
+
f"--tlskey={self._tlskey}",
|
|
593
|
+
f"--tlscacert={self._tlscacert}",
|
|
594
|
+
]
|
|
595
|
+
|
|
537
596
|
def _registry_host(self) -> str:
|
|
538
597
|
registry = str(getattr(self._credential, "registry", "") or "")
|
|
539
598
|
if not registry:
|
|
@@ -605,7 +664,7 @@ class Builder:
|
|
|
605
664
|
def _cache_options(self, request: BuildRequest) -> list[str]:
|
|
606
665
|
"""The owner's cache, imported and exported: never another owner's (D-12)."""
|
|
607
666
|
registry = self._registry_host()
|
|
608
|
-
cache = f"{registry}/
|
|
667
|
+
cache = f"{registry}/{owner_cache_repository(request.owner_uid)}:{self.variant}"
|
|
609
668
|
return [
|
|
610
669
|
"--import-cache",
|
|
611
670
|
f"type=registry,ref={cache}",
|