code-sandboxes 1.4.5__tar.gz → 1.7.0__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.
Files changed (235) hide show
  1. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/CHANGELOG.md +47 -0
  2. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/Makefile +18 -0
  3. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/PKG-INFO +1 -1
  4. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/code_sandboxes/__version__.py +1 -1
  5. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/code_sandboxes/base.py +52 -1
  6. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/code_sandboxes/cloudflare_sandbox.py +2 -1
  7. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/code_sandboxes/coreweave_sandbox.py +2 -1
  8. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/code_sandboxes/datalayer_sandbox.py +13 -13
  9. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/code_sandboxes/daytona_sandbox.py +2 -1
  10. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/code_sandboxes/docker_sandbox.py +19 -1
  11. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/code_sandboxes/e2b_sandbox.py +2 -1
  12. code_sandboxes-1.7.0/code_sandboxes/environments/__init__.py +119 -0
  13. code_sandboxes-1.7.0/code_sandboxes/environments/__main__.py +31 -0
  14. code_sandboxes-1.7.0/code_sandboxes/environments/adapters/__init__.py +11 -0
  15. code_sandboxes-1.7.0/code_sandboxes/environments/bases.py +194 -0
  16. code_sandboxes-1.7.0/code_sandboxes/environments/builders.py +272 -0
  17. code_sandboxes-1.7.0/code_sandboxes/environments/canonical.py +121 -0
  18. code_sandboxes-1.7.0/code_sandboxes/environments/conformance.py +655 -0
  19. code_sandboxes-1.7.0/code_sandboxes/environments/constraints/sandbox-contract-v1.txt +27 -0
  20. code_sandboxes-1.7.0/code_sandboxes/environments/contract.py +559 -0
  21. code_sandboxes-1.7.0/code_sandboxes/environments/doctor/__init__.py +13 -0
  22. code_sandboxes-1.7.0/code_sandboxes/environments/doctor/build.py +47 -0
  23. code_sandboxes-1.7.0/code_sandboxes/environments/doctor/datalayer_sandbox.py +441 -0
  24. code_sandboxes-1.7.0/code_sandboxes/environments/doctor/report.schema.json +112 -0
  25. code_sandboxes-1.7.0/code_sandboxes/environments/errors.py +316 -0
  26. code_sandboxes-1.7.0/code_sandboxes/environments/files.py +73 -0
  27. code_sandboxes-1.7.0/code_sandboxes/environments/lifecycle.py +237 -0
  28. code_sandboxes-1.7.0/code_sandboxes/environments/redact.py +424 -0
  29. code_sandboxes-1.7.0/code_sandboxes/environments/resolve.py +887 -0
  30. code_sandboxes-1.7.0/code_sandboxes/environments/schema.py +68 -0
  31. code_sandboxes-1.7.0/code_sandboxes/environments/spec.py +598 -0
  32. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/code_sandboxes/eval_sandbox.py +2 -1
  33. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/code_sandboxes/google_colab_sandbox.py +14 -1
  34. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/code_sandboxes/jupyter_ingress.py +30 -0
  35. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/code_sandboxes/jupyter_server_sandbox.py +13 -6
  36. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/code_sandboxes/kaggle_sandbox.py +14 -1
  37. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/code_sandboxes/manage.py +43 -6
  38. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/code_sandboxes/modal_sandbox.py +2 -1
  39. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/code_sandboxes/models.py +8 -1
  40. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/code_sandboxes/monty_sandbox.py +21 -1
  41. code_sandboxes-1.7.0/docs/docs/environments/_category_.yml +2 -0
  42. code_sandboxes-1.7.0/docs/docs/environments/contract.mdx +57 -0
  43. code_sandboxes-1.7.0/docs/docs/environments/index.mdx +167 -0
  44. code_sandboxes-1.7.0/docs/docs/environments/specification.mdx +53 -0
  45. code_sandboxes-1.7.0/docs/docs/environments/spike-2026-09.mdx +114 -0
  46. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/pyproject.toml +9 -0
  47. code_sandboxes-1.7.0/schemas/environment-v1alpha1.json +497 -0
  48. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/tests/test_a_datalayer_sandbox_can_be_interrupted.py +11 -93
  49. code_sandboxes-1.7.0/tests/test_environment_bases.py +100 -0
  50. code_sandboxes-1.7.0/tests/test_environment_builders.py +281 -0
  51. code_sandboxes-1.7.0/tests/test_environment_canonical.py +121 -0
  52. code_sandboxes-1.7.0/tests/test_environment_conformance.py +357 -0
  53. code_sandboxes-1.7.0/tests/test_environment_constraints.py +64 -0
  54. code_sandboxes-1.7.0/tests/test_environment_contract.py +159 -0
  55. code_sandboxes-1.7.0/tests/test_environment_doctor.py +259 -0
  56. code_sandboxes-1.7.0/tests/test_environment_errors.py +184 -0
  57. code_sandboxes-1.7.0/tests/test_environment_lifecycle.py +170 -0
  58. code_sandboxes-1.7.0/tests/test_environment_redact.py +269 -0
  59. code_sandboxes-1.7.0/tests/test_environment_resolve.py +495 -0
  60. code_sandboxes-1.7.0/tests/test_environment_spec.py +345 -0
  61. code_sandboxes-1.7.0/tests/test_every_variant_has_a_real_interrupt.py +262 -0
  62. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/tests/test_jupyter_server.py +24 -0
  63. code_sandboxes-1.7.0/tests/test_manage_datalayer.py +102 -0
  64. code_sandboxes-1.7.0/tests/test_the_kill_relaunch_drill.py +131 -0
  65. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/.github/workflows/build.yml +0 -0
  66. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/.github/workflows/py-code-style.yml +0 -0
  67. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/.github/workflows/py-tests.yml +0 -0
  68. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/.github/workflows/py-typing.yml +0 -0
  69. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/.github/workflows/reusable-python.yml +0 -0
  70. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/.gitignore +0 -0
  71. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/.licenserc.yaml +0 -0
  72. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/.pre-commit-config.yaml +0 -0
  73. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/.vscode/settings.json +0 -0
  74. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/Dockerfile +0 -0
  75. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/LICENSE +0 -0
  76. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/README.md +0 -0
  77. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/code_sandboxes/__init__.py +0 -0
  78. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/code_sandboxes/bridge_mount.py +0 -0
  79. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/code_sandboxes/builds.py +0 -0
  80. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/code_sandboxes/cli.py +0 -0
  81. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/code_sandboxes/client.py +0 -0
  82. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/code_sandboxes/commands.py +0 -0
  83. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/code_sandboxes/console.py +0 -0
  84. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/code_sandboxes/contents.py +0 -0
  85. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/code_sandboxes/exceptions.py +0 -0
  86. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/code_sandboxes/filesystem.py +0 -0
  87. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/code_sandboxes/google_colab.py +0 -0
  88. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/code_sandboxes/interfaces.py +0 -0
  89. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/code_sandboxes/kaggle.py +0 -0
  90. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/code_sandboxes/kaggle_execute.py +0 -0
  91. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/code_sandboxes/kaggle_live.py +0 -0
  92. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/code_sandboxes/lifecycle.py +0 -0
  93. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/code_sandboxes/provider_ingress.py +0 -0
  94. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/code_sandboxes/providers.py +0 -0
  95. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docker/Dockerfile +0 -0
  96. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/.gitignore +0 -0
  97. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/LICENSE +0 -0
  98. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/Makefile +0 -0
  99. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/README.md +0 -0
  100. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/babel.config.js +0 -0
  101. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/docs/api-reference/_category_.yml +0 -0
  102. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/docs/api-reference/index.mdx +0 -0
  103. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/docs/cli/_category_.yml +0 -0
  104. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/docs/cli/index.mdx +0 -0
  105. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/docs/contribute/_category_.yml +0 -0
  106. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/docs/contribute/index.mdx +0 -0
  107. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/docs/examples/_category_.yml +0 -0
  108. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/docs/examples/index.mdx +0 -0
  109. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/docs/guide/_category_.yml +0 -0
  110. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/docs/guide/index.mdx +0 -0
  111. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/docs/index.mdx +0 -0
  112. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/docs/install/_category_.yml +0 -0
  113. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/docs/install/index.mdx +0 -0
  114. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/docs/provider-ingress.mdx +0 -0
  115. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/docs/providers/_category_.yml +0 -0
  116. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/docs/providers/cloudflare.mdx +0 -0
  117. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/docs/providers/coreweave.mdx +0 -0
  118. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/docs/providers/datalayer.mdx +0 -0
  119. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/docs/providers/daytona.mdx +0 -0
  120. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/docs/providers/docker.mdx +0 -0
  121. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/docs/providers/e2b.mdx +0 -0
  122. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/docs/providers/eval.mdx +0 -0
  123. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/docs/providers/google-colab.mdx +0 -0
  124. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/docs/providers/index.mdx +0 -0
  125. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/docs/providers/jupyter-server.mdx +0 -0
  126. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/docs/providers/kaggle.mdx +0 -0
  127. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/docs/providers/modal.mdx +0 -0
  128. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/docs/providers/monty.mdx +0 -0
  129. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/docusaurus.config.js +0 -0
  130. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/package.json +0 -0
  131. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/sidebars.js +0 -0
  132. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/src/components/HomepageRow1.js +0 -0
  133. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/src/components/HomepageRow1.module.css +0 -0
  134. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/src/components/HomepageRow2.js +0 -0
  135. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/src/components/HomepageRow2.module.css +0 -0
  136. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/src/components/HomepageRow3.js +0 -0
  137. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/src/components/HomepageRow3.module.css +0 -0
  138. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/src/css/custom.css +0 -0
  139. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/src/pages/index.module.css +0 -0
  140. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/src/pages/markdown-page.md +0 -0
  141. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/src/theme/CustomDocItem.tsx +0 -0
  142. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/src/theme/IconsTitle.tsx +0 -0
  143. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/static/img/bricks.svg +0 -0
  144. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/static/img/building-construction.svg +0 -0
  145. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/static/img/cloud-sun.svg +0 -0
  146. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/static/img/cloud.svg +0 -0
  147. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/static/img/collaboration.svg +0 -0
  148. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/static/img/copy-left.svg +0 -0
  149. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/static/img/datalayer/logo.png +0 -0
  150. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/static/img/datalayer/logo.svg +0 -0
  151. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/static/img/datalayer/personas/business.svg +0 -0
  152. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/static/img/datalayer/personas/data-scientist.svg +0 -0
  153. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/static/img/datalayer/personas/devops.svg +0 -0
  154. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/static/img/datalayer/project/project-line.svg +0 -0
  155. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/static/img/favicon.ico +0 -0
  156. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/static/img/jupyter.svg +0 -0
  157. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/static/img/open-source.svg +0 -0
  158. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/static/img/react-js.svg +0 -0
  159. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/static/img/react-js2.svg +0 -0
  160. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/static/img/ringed-planet.svg +0 -0
  161. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/static/img/robot.svg +0 -0
  162. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/static/img/rocket.svg +0 -0
  163. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/static/img/shield.svg +0 -0
  164. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/static/img/sparkles.svg +0 -0
  165. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/static/img/streamlit.gif +0 -0
  166. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/static/img/super-hero.svg +0 -0
  167. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/static/img/target.svg +0 -0
  168. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/docs/static/img/thumbs-up.svg +0 -0
  169. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/examples/README.md +0 -0
  170. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/examples/exec/Makefile +0 -0
  171. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/examples/exec/cloudflare_sandbox_example.py +0 -0
  172. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/examples/exec/coreweave_sandbox_example.py +0 -0
  173. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/examples/exec/datalayer_sandbox_example.py +0 -0
  174. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/examples/exec/daytona_sandbox_example.py +0 -0
  175. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/examples/exec/docker_sandbox_example.py +0 -0
  176. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/examples/exec/e2b_sandbox_example.py +0 -0
  177. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/examples/exec/eval_sandbox_example.py +0 -0
  178. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/examples/exec/google_colab_sandbox_example.py +0 -0
  179. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/examples/exec/jupyter_server_sandbox_example.py +0 -0
  180. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/examples/exec/kaggle_sandbox_example.py +0 -0
  181. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/examples/exec/modal_sandbox_example.py +0 -0
  182. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/examples/exec/monty_sandbox_example.py +0 -0
  183. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/examples/repl/Makefile +0 -0
  184. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/examples/repl/cloudflare_sandbox_example.py +0 -0
  185. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/examples/repl/coreweave_sandbox_example.py +0 -0
  186. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/examples/repl/datalayer_sandbox_example.py +0 -0
  187. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/examples/repl/daytona_sandbox_example.py +0 -0
  188. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/examples/repl/docker_sandbox_example.py +0 -0
  189. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/examples/repl/e2b_sandbox_example.py +0 -0
  190. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/examples/repl/eval_sandbox_example.py +0 -0
  191. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/examples/repl/google_colab_sandbox_example.py +0 -0
  192. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/examples/repl/jupyter_server_sandbox_example.py +0 -0
  193. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/examples/repl/kaggle_sandbox_example.py +0 -0
  194. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/examples/repl/modal_sandbox_example.py +0 -0
  195. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/examples/repl/monty_sandbox_example.py +0 -0
  196. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/tests/__init__.py +0 -0
  197. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/tests/conftest.py +0 -0
  198. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/tests/test_bridge_mount.py +0 -0
  199. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/tests/test_builds.py +0 -0
  200. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/tests/test_cli_exec.py +0 -0
  201. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/tests/test_cli_repl.py +0 -0
  202. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/tests/test_client.py +0 -0
  203. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/tests/test_client_contents.py +0 -0
  204. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/tests/test_client_files.py +0 -0
  205. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/tests/test_client_outcome_error_output.py +0 -0
  206. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/tests/test_cloudflare.py +0 -0
  207. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/tests/test_commands_envelope.py +0 -0
  208. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/tests/test_console.py +0 -0
  209. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/tests/test_contents_contract.py +0 -0
  210. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/tests/test_contents_live_matrix.py +0 -0
  211. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/tests/test_coreweave.py +0 -0
  212. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/tests/test_datalayer_sandbox.py +0 -0
  213. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/tests/test_daytona.py +0 -0
  214. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/tests/test_e2b.py +0 -0
  215. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/tests/test_eval.py +0 -0
  216. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/tests/test_factory.py +0 -0
  217. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/tests/test_google_colab.py +0 -0
  218. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/tests/test_integration.py +0 -0
  219. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/tests/test_jupyter_ingress.py +0 -0
  220. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/tests/test_jupyter_server_streaming.py +0 -0
  221. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/tests/test_kaggle.py +0 -0
  222. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/tests/test_kaggle_execute.py +0 -0
  223. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/tests/test_kaggle_live.py +0 -0
  224. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/tests/test_kaggle_session.py +0 -0
  225. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/tests/test_kernel_client_compatibility.py +0 -0
  226. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/tests/test_lifecycle_vocabulary.py +0 -0
  227. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/tests/test_manage.py +0 -0
  228. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/tests/test_modal_google_colab_sandbox.py +0 -0
  229. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/tests/test_modal_session.py +0 -0
  230. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/tests/test_models.py +0 -0
  231. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/tests/test_output_line_fidelity.py +0 -0
  232. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/tests/test_provider_catalog_names.py +0 -0
  233. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/tests/test_provider_ingress.py +0 -0
  234. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/tests/test_providers.py +0 -0
  235. {code_sandboxes-1.4.5 → code_sandboxes-1.7.0}/tests/test_sandboxes.py +0 -0
@@ -8,6 +8,53 @@
8
8
 
9
9
  ## Unreleased
10
10
 
11
+ ## 1.7.0
12
+
13
+ - **A version is resolved into one lock** (`code_sandboxes.environments.resolve`,
14
+ PLAN_ENV.md E1-04, D-9). Datalayer's protected constraints are merged over the
15
+ user's requirements — a requirement that agrees with a pin is dropped for it,
16
+ one that contradicts it is `DL_ENV_PROTECTED_PACKAGE` with the supported range
17
+ — the base is resolved to a digest per requested variant, and uv's refusals
18
+ are read into the error taxonomy: a conflict with the pair that cannot hold, a
19
+ package no index has, a protected pin, and `DL_ENV_PROVIDER_ERROR`, which is
20
+ retryable, for a failure that is not about the version.
21
+
22
+ The lock is uv's hashed output with the apt pins and the protected pins above
23
+ it as comments: one document that says everything a build installs, and still
24
+ a requirements file. `BuildkitResolveRunner` is D-9's solve, `FROM` the
25
+ resolved base digest; `LocalResolveRunner` runs `uv` where it is called, for
26
+ `plane local`, and refuses to pin apt rather than lock another
27
+ distribution's versions.
28
+
29
+ - **Every variant has a real interrupt now, and can be reached to give it.**
30
+ `Sandbox.interrupt` is two gates — `_executing_event` must be set, then
31
+ `_do_interrupt` must answer — and seven variants failed one of them, each in
32
+ a way that looked like success:
33
+
34
+ - `docker`, `google_colab`, `kaggle` and `monty` had no `_do_interrupt`, so
35
+ the base default ran: set a flag, return `True`, stop nothing. `True` means
36
+ "the interrupt was delivered", and nothing had been. `google_colab` and
37
+ `kaggle` did read the flag, but only *after* the run, to label a finished
38
+ result as `interrupted` — labelling a run is not stopping one.
39
+ - `cloudflare`, `coreweave`, `daytona`, `e2b` and `modal` answered honestly
40
+ (`False`: this provider takes no interrupt) but never marked their execution
41
+ window, so `interrupt()` returned at the first gate and their answer was
42
+ never reached — and `is_executing` was always False, which every status
43
+ above them believed.
44
+
45
+ Docker, Colab and Kaggle now interrupt for real, delegating to the
46
+ `JupyterKernelClient` that already knows how to authenticate to their server
47
+ rather than rebuilding the request. Monty answers `False` with its reason:
48
+ `feed_run` executes a snippet in one blocking call with no limits and no
49
+ callable hook, so there is no point at which a flag could be read.
50
+
51
+ The execution window is `@marks_execution` on `run_code` rather than a line
52
+ in each body, because it has to close on *every* path out — including the
53
+ early `return ExecutionResult(...)` each adapter uses for an infrastructure
54
+ failure — and a `finally` in the decorator cannot be forgotten in one branch
55
+ of one variant. Both invariants are asserted across the package with no
56
+ pinned exceptions left.
57
+
11
58
  - A Datalayer sandbox says when it is running code, so an interrupt can reach
12
59
  it. `Sandbox.interrupt` refuses before it delegates — `if not
13
60
  self._executing_event.is_set(): return False` — and `run_code` never set that
@@ -32,10 +32,28 @@ build:
32
32
  test:
33
33
  pytest -q
34
34
 
35
+ ## Build the datalayer-sandbox doctor as a single-file zipapp.
36
+ doctor-zipapp:
37
+ python -m code_sandboxes.environments.doctor.build dist/datalayer-sandbox
38
+
39
+ ## Regenerate the Environment JSON Schema and the sandbox contract page.
40
+ environments-generate:
41
+ python -m code_sandboxes.environments schema --write
42
+ python -m code_sandboxes.environments contract --markdown docs/docs/environments/contract.mdx
43
+
44
+ ## Fail when the Environment JSON Schema or the sandbox contract page has drifted.
45
+ environments-check:
46
+ python -m code_sandboxes.environments schema --check
47
+ python -m code_sandboxes.environments contract --check docs/docs/environments/contract.mdx
48
+
35
49
  ## The Contents provider matrix against the real providers (skips those without credentials).
36
50
  live-matrix:
37
51
  CODE_SANDBOXES_LIVE=1 pytest -q -m live tests/test_contents_live_matrix.py
38
52
 
53
+ ## Lose a sandbox on each live provider and check the harness says so.
54
+ kill-relaunch:
55
+ CODE_SANDBOXES_LIVE=1 pytest -q -m live tests/test_the_kill_relaunch_drill.py
56
+
39
57
  clean: ## clean
40
58
  git clean -fdx
41
59
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: code_sandboxes
3
- Version: 1.4.5
3
+ Version: 1.7.0
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
@@ -3,4 +3,4 @@
3
3
 
4
4
  """Code Sandboxes."""
5
5
 
6
- __version__ = "1.4.5"
6
+ __version__ = "1.7.0"
@@ -6,6 +6,7 @@
6
6
 
7
7
  from __future__ import annotations
8
8
 
9
+ import functools
9
10
  import threading
10
11
  import uuid
11
12
  from abc import ABC, abstractmethod
@@ -82,6 +83,49 @@ def generate_sandbox_name() -> str:
82
83
  DEFAULT_ENVIRONMENT = "ai-agents-env"
83
84
 
84
85
 
86
+ def marks_execution(run):
87
+ """Say, for the duration of the call, that this sandbox is running code.
88
+
89
+ `Sandbox.interrupt` refuses before it delegates::
90
+
91
+ if not self._executing_event.is_set():
92
+ return False
93
+ self._interrupt_requested.set()
94
+ return self._do_interrupt()
95
+
96
+ So a variant that never sets the event has an interrupt nothing can
97
+ reach, and an `is_executing` that is always False — every status and
98
+ every cancel decision above it silently wrong. `DatalayerSandbox` was in
99
+ exactly that state, and implementing `_do_interrupt` for it changed the
100
+ measurement on prod1 from 60.8 seconds to 60.5: a fix that reads as
101
+ complete and measures as nothing.
102
+
103
+ A decorator rather than a line in each body, because the window has to
104
+ close on *every* path out — including the several early `return
105
+ ExecutionResult(...)`s that each adapter uses for an infrastructure
106
+ failure — and a `finally` here cannot be forgotten in one branch of one
107
+ variant. It also gives the invariant something to test that is not a
108
+ grep for a line of source.
109
+
110
+ `_interrupt_requested` is cleared on the way in, so a stale request from
111
+ a previous call cannot stop this one, and deliberately *not* on the way
112
+ out: the cooperative variants read it after the run to report that the
113
+ execution was interrupted.
114
+ """
115
+
116
+ @functools.wraps(run)
117
+ def marked(self, *args, **kwargs):
118
+ self._interrupt_requested.clear()
119
+ self._executing_event.set()
120
+ try:
121
+ return run(self, *args, **kwargs)
122
+ finally:
123
+ self._executing_event.clear()
124
+
125
+ marked._marks_execution = True
126
+ return marked
127
+
128
+
85
129
  class Sandbox(ABC):
86
130
  """Abstract base class for code execution sandboxes.
87
131
 
@@ -455,6 +499,7 @@ class Sandbox(ABC):
455
499
  timeout: float | None = None,
456
500
  name: str | None = None,
457
501
  environment: str | None = None,
502
+ environment_version: str | int | None = None,
458
503
  gpu: str | None = None,
459
504
  cpu: float | None = None,
460
505
  memory: int | None = None,
@@ -481,7 +526,12 @@ class Sandbox(ABC):
481
526
  config: Optional full configuration object (overrides individual params).
482
527
  timeout: Default timeout for code execution in seconds.
483
528
  name: Optional name for the sandbox.
484
- environment: Runtime environment (e.g., "ai-agents-env").
529
+ environment: Runtime environment (e.g., "ai-agents-env"), or a user
530
+ environment as "<account-handle>/<name>" or by its uid.
531
+ environment_version: Which version of a user environment to launch:
532
+ its number, or a version uid. Without one the promoted version
533
+ launches, which is what a platform environment always does.
534
+ Only the datalayer variant has versions (PLAN_ENV.md, E1-19).
485
535
  gpu: GPU type to use (e.g., "T4", "A100", "H100"). Only for datalayer.
486
536
  cpu: CPU cores to allocate.
487
537
  memory: Memory limit in MB.
@@ -515,6 +565,7 @@ class Sandbox(ABC):
515
565
  config = SandboxConfig(
516
566
  timeout=timeout or 30.0,
517
567
  environment=environment or DEFAULT_ENVIRONMENT,
568
+ environment_version=environment_version,
518
569
  memory_limit=memory * 1024 * 1024 if memory else None,
519
570
  cpu_limit=cpu,
520
571
  env_vars=env or {},
@@ -43,7 +43,7 @@ from collections.abc import Iterator
43
43
  from typing import Any
44
44
  from urllib.parse import quote
45
45
 
46
- from .base import Sandbox
46
+ from .base import Sandbox, marks_execution
47
47
  from .exceptions import (
48
48
  SandboxConfigurationError,
49
49
  SandboxConnectionError,
@@ -370,6 +370,7 @@ class CloudflareSandbox(Sandbox):
370
370
  except Exception:
371
371
  return False
372
372
 
373
+ @marks_execution
373
374
  def run_code(
374
375
  self,
375
376
  code: str,
@@ -31,7 +31,7 @@ import math
31
31
  import time
32
32
  from typing import Any
33
33
 
34
- from .base import Sandbox
34
+ from .base import Sandbox, marks_execution
35
35
  from .exceptions import (
36
36
  SandboxConfigurationError,
37
37
  SandboxExecutionError,
@@ -521,6 +521,7 @@ class CoreWeaveSandbox(Sandbox):
521
521
  if self._info:
522
522
  self._info.status = SandboxStatus.STOPPED
523
523
 
524
+ @marks_execution
524
525
  def run_code(
525
526
  self,
526
527
  code: str,
@@ -17,7 +17,7 @@ from typing import TYPE_CHECKING, Any, Optional
17
17
  if TYPE_CHECKING:
18
18
  from .filesystem import SandboxFileHandle
19
19
 
20
- from .base import Sandbox
20
+ from .base import Sandbox, marks_execution
21
21
  from .contents import (
22
22
  FILESYSTEM_PRIMITIVES,
23
23
  LOCAL_BRIDGE_MOUNT,
@@ -475,11 +475,19 @@ class DatalayerSandbox(Sandbox):
475
475
  # Build sandbox name
476
476
  sandbox_name = self.config.name or f"sandbox-{self._sandbox_id[:8]}"
477
477
 
478
+ # Which VERSION of that environment, passed through as the
479
+ # environment is (PLAN_ENV.md, E1-19). `None` means the promoted
480
+ # version, which is what every platform environment launches — and
481
+ # what every sandbox launched before there were versions asked for.
482
+ # The GPU rewrite above is left exactly as it is until E4-11 (D-20).
483
+ environment_version = self.config.environment_version
484
+
478
485
  # Create the runtime (optionally from snapshot)
479
486
  if self._snapshot_name:
480
487
  self._runtime = self._client.create_runtime(
481
488
  name=sandbox_name,
482
489
  environment=environment,
490
+ version=environment_version,
483
491
  time_reservation=time_reservation,
484
492
  snapshot_name=self._snapshot_name,
485
493
  )
@@ -487,6 +495,7 @@ class DatalayerSandbox(Sandbox):
487
495
  self._runtime = self._client.create_runtime(
488
496
  name=sandbox_name,
489
497
  environment=environment,
498
+ version=environment_version,
490
499
  time_reservation=time_reservation,
491
500
  )
492
501
 
@@ -761,6 +770,7 @@ class DatalayerSandbox(Sandbox):
761
770
  return None
762
771
  return 0
763
772
 
773
+ @marks_execution
764
774
  def run_code(
765
775
  self,
766
776
  code: str,
@@ -801,17 +811,9 @@ class DatalayerSandbox(Sandbox):
801
811
 
802
812
  started_at = time.time()
803
813
 
804
- # **Say that code is running, because `interrupt()` will not act
805
- # unless it can see that.** `Sandbox.interrupt` returns `False`
806
- # before reaching `_do_interrupt` when `_executing_event` is clear,
807
- # and this variant never set it — so `is_executing` was always False,
808
- # every interrupt was refused at the door, and the refusal was
809
- # reported as "no code was running" to a caller watching a cell run.
810
- # Implementing `_do_interrupt` alone did not fix cancellation for
811
- # exactly this reason: nothing ever called it.
814
+ # The execution window is `@marks_execution`'s, not this body's — see
815
+ # its docstring for why the interrupt is refused without one.
812
816
  execution_timeout = timeout or self.config.timeout
813
- self._interrupt_requested.clear()
814
- self._executing_event.set()
815
817
  try:
816
818
  # Set environment variables if provided
817
819
  if envs:
@@ -831,8 +833,6 @@ class DatalayerSandbox(Sandbox):
831
833
  started_at=started_at,
832
834
  completed_at=time.time(),
833
835
  )
834
- finally:
835
- self._executing_event.clear()
836
836
 
837
837
  # Parse the response
838
838
  stdout_messages: list[OutputMessage] = []
@@ -30,7 +30,7 @@ import textwrap
30
30
  import time
31
31
  from typing import Any
32
32
 
33
- from .base import Sandbox
33
+ from .base import Sandbox, marks_execution
34
34
  from .contents import (
35
35
  FILESYSTEM_PRIMITIVES,
36
36
  ContentAttachmentSpec,
@@ -701,6 +701,7 @@ class DaytonaSandbox(Sandbox):
701
701
  self._contexts[context.id] = existing
702
702
  return existing
703
703
 
704
+ @marks_execution
704
705
  def run_code( # noqa: C901
705
706
  self,
706
707
  code: str,
@@ -15,9 +15,12 @@ import tempfile
15
15
  import time
16
16
  import uuid
17
17
 
18
+ import logging
19
+
18
20
  import requests
19
21
 
20
- from .base import Sandbox
22
+ from .base import Sandbox, marks_execution
23
+ from .jupyter_ingress import interrupt_kernel_client
21
24
  from .exceptions import SandboxConfigurationError, SandboxNotStartedError
22
25
  from .interfaces import ISandboxClient
23
26
  from .models import (
@@ -34,6 +37,8 @@ from .models import (
34
37
  SandboxStatus,
35
38
  )
36
39
 
40
+ logger = logging.getLogger(__name__)
41
+
37
42
  DEFAULT_IMAGE = "code-sandboxes-jupyter:latest"
38
43
  DEFAULT_PORT = 8888
39
44
 
@@ -194,6 +199,18 @@ class DockerSandbox(Sandbox):
194
199
  """The underlying kernel client for this sandbox, if started."""
195
200
  return self._client
196
201
 
202
+ def _do_interrupt(self) -> bool:
203
+ """Interrupt the container's kernel, through the client that runs it.
204
+
205
+ This variant runs a Jupyter Server in a container and executes
206
+ through `jupyter-kernel-client`, so it can be interrupted — and it
207
+ had no `_do_interrupt` at all, so the base class's default ran: sets
208
+ a flag, returns `True`, stops nothing. A caller that cancelled a long
209
+ cell here was told the interrupt had been delivered while the
210
+ container went on computing.
211
+ """
212
+ return interrupt_kernel_client(self._client, variant="container's")
213
+
197
214
  def stop(self) -> None:
198
215
  if not self._started:
199
216
  return
@@ -225,6 +242,7 @@ class DockerSandbox(Sandbox):
225
242
  if self._info:
226
243
  self._info.status = SandboxStatus.STOPPED
227
244
 
245
+ @marks_execution
228
246
  def run_code(
229
247
  self,
230
248
  code: str,
@@ -28,7 +28,7 @@ import logging
28
28
  import time
29
29
  from typing import Any
30
30
 
31
- from .base import Sandbox
31
+ from .base import Sandbox, marks_execution
32
32
  from .contents import (
33
33
  FILESYSTEM_PRIMITIVES,
34
34
  ContentAttachmentSpec,
@@ -471,6 +471,7 @@ class E2BSandbox(Sandbox):
471
471
  self._contexts[context.id] = existing
472
472
  return existing
473
473
 
474
+ @marks_execution
474
475
  def run_code(
475
476
  self,
476
477
  code: str,
@@ -0,0 +1,119 @@
1
+ # Copyright (c) 2025-2026 Datalayer, Inc.
2
+ #
3
+ # BSD 3-Clause License
4
+
5
+ """Environments: define once, resolve once, build an immutable artifact per variant.
6
+
7
+ An **Environment** is the stable object a person names. An **Environment
8
+ Version** is immutable once it leaves draft: its spec, the lock resolved from
9
+ it, and its build outcome. An **Environment Artifact** is the build output of
10
+ one version on one variant — an OCI digest for Datalayer, a build id for E2B,
11
+ a snapshot id for Daytona, an image id for Modal.
12
+
13
+ This package holds what every party to that life shares, and nothing
14
+ provider-specific:
15
+
16
+ - :mod:`.spec` — the canonical specification and its rules;
17
+ - :mod:`.resolve` — the lock a version is resolved into, once (D-9);
18
+ - :mod:`.contract` — ``sandbox-contract/v1`` and the Dockerfile validator;
19
+ - :mod:`.lifecycle` — the version states and their legal moves;
20
+ - :mod:`.errors` — the error taxonomy every provider error maps into;
21
+ - :mod:`.builders` — the builder interface and its neutral types;
22
+ - :mod:`.conformance` — the checks an artifact passes before it is ready;
23
+ - :mod:`.redact` — a build's log with its secrets taken out before it is stored;
24
+ - :mod:`.doctor` — the in-sandbox contract checker.
25
+
26
+ The builders themselves are in :mod:`.adapters`, loaded by variant.
27
+ """
28
+
29
+ from __future__ import annotations
30
+
31
+ from .bases import APPROVED_BASES, ApprovedBase
32
+ from .builders import (
33
+ ArtifactReference,
34
+ BuildRequest,
35
+ CapabilityReport,
36
+ CapabilitySet,
37
+ CheckResult,
38
+ EnvironmentBuilder,
39
+ ValidationResult,
40
+ get_builder,
41
+ )
42
+ from .canonical import canonical_digest, canonical_json
43
+ from .contract import (
44
+ CONTRACT_V1,
45
+ SANDBOX_CONTRACT_V1,
46
+ SUPPORTED_CONTRACTS,
47
+ SandboxContract,
48
+ check_dockerfile,
49
+ validate_dockerfile,
50
+ )
51
+ from .errors import ERROR_CODES, EnvironmentsError, ErrorCode, map_provider_error
52
+ from .lifecycle import VersionState, build_outcome, can_transition, transition
53
+ from .resolve import (
54
+ LOCK_FORMAT,
55
+ BuildkitResolveRunner,
56
+ LocalResolveRunner,
57
+ ResolveRunner,
58
+ locked_versions,
59
+ merge_requirements,
60
+ parse_resolver_failure,
61
+ protected_pins,
62
+ resolve_environment,
63
+ )
64
+ from .spec import (
65
+ API_VERSION,
66
+ VARIANTS,
67
+ Environment,
68
+ EnvironmentSpec,
69
+ parse_environment,
70
+ spec_digest,
71
+ spec_findings,
72
+ validate_environment,
73
+ )
74
+
75
+ __all__ = [
76
+ "API_VERSION",
77
+ "APPROVED_BASES",
78
+ "CONTRACT_V1",
79
+ "ERROR_CODES",
80
+ "LOCK_FORMAT",
81
+ "SANDBOX_CONTRACT_V1",
82
+ "SUPPORTED_CONTRACTS",
83
+ "VARIANTS",
84
+ "ApprovedBase",
85
+ "ArtifactReference",
86
+ "BuildkitResolveRunner",
87
+ "BuildRequest",
88
+ "CapabilityReport",
89
+ "CapabilitySet",
90
+ "CheckResult",
91
+ "Environment",
92
+ "EnvironmentBuilder",
93
+ "EnvironmentSpec",
94
+ "EnvironmentsError",
95
+ "ErrorCode",
96
+ "LocalResolveRunner",
97
+ "ResolveRunner",
98
+ "SandboxContract",
99
+ "ValidationResult",
100
+ "VersionState",
101
+ "build_outcome",
102
+ "can_transition",
103
+ "canonical_digest",
104
+ "canonical_json",
105
+ "check_dockerfile",
106
+ "get_builder",
107
+ "locked_versions",
108
+ "merge_requirements",
109
+ "map_provider_error",
110
+ "parse_environment",
111
+ "parse_resolver_failure",
112
+ "protected_pins",
113
+ "resolve_environment",
114
+ "spec_digest",
115
+ "spec_findings",
116
+ "transition",
117
+ "validate_dockerfile",
118
+ "validate_environment",
119
+ ]
@@ -0,0 +1,31 @@
1
+ # Copyright (c) 2025-2026 Datalayer, Inc.
2
+ #
3
+ # BSD 3-Clause License
4
+
5
+ """Generate what is generated from the Environment models.
6
+
7
+ python -m code_sandboxes.environments schema --write [PATH]
8
+ python -m code_sandboxes.environments schema --check [PATH]
9
+ python -m code_sandboxes.environments contract --markdown docs/docs/environments/contract.mdx
10
+ python -m code_sandboxes.environments contract --check docs/docs/environments/contract.mdx
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import sys
16
+
17
+ from . import contract, schema
18
+
19
+ _COMMANDS = {"schema": schema.main, "contract": contract.main}
20
+
21
+
22
+ def main(argv: list[str] | None = None) -> int:
23
+ arguments = list(sys.argv[1:] if argv is None else argv)
24
+ if not arguments or arguments[0] not in _COMMANDS:
25
+ print(__doc__.strip(), file=sys.stderr) # noqa: T201 - a command's own output
26
+ return 2
27
+ return _COMMANDS[arguments[0]](arguments[1:])
28
+
29
+
30
+ if __name__ == "__main__":
31
+ raise SystemExit(main())
@@ -0,0 +1,11 @@
1
+ # Copyright (c) 2025-2026 Datalayer, Inc.
2
+ #
3
+ # BSD 3-Clause License
4
+
5
+ """The provider-specific Environment builders, one module per variant.
6
+
7
+ Not neutral: each module speaks its provider's SDK. They are loaded only by
8
+ :func:`code_sandboxes.environments.builders.get_builder`, and only where the
9
+ build runs — the durable worker — never by a service that merely describes
10
+ or launches an Environment.
11
+ """