modal 1.5.6.dev17__tar.gz → 1.5.6.dev19__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.
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/PKG-INFO +1 -1
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_workspace.py +8 -3
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/app.py +76 -19
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/entry_point.py +1 -3
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/workspace.py +52 -8
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/client.pyi +2 -2
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/config.py +1 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/sandbox.py +57 -16
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/sandbox.pyi +8 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/types.py +2 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/workspace.pyi +15 -5
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal.egg-info/PKG-INFO +1 -1
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal.egg-info/SOURCES.txt +0 -1
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal_proto/api_pb2.py +1403 -1384
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal_proto/api_pb2.pyi +57 -4
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal_version/__init__.py +1 -1
- modal-1.5.6.dev17/modal/cli/bootstrap.py +0 -139
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/LICENSE +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/README.md +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/__init__.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/__main__.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_billing.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_clustered_functions.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_clustered_functions.pyi +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_container_entrypoint.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_environments.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_function_variants.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_functions.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_grpc_client.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_image.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_ipython.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_load_context.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_location.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_logs.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_logs_manager.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_logs_manager.pyi +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_object.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_output/__init__.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_output/manager.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_output/pty.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_output/rich.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_output/status.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_partial_function.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_resolver.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_resources.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_runtime/__init__.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_runtime/asgi.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_runtime/container_io_manager.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_runtime/container_io_manager.pyi +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_runtime/execution_context.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_runtime/execution_context.pyi +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_runtime/gpu_memory_snapshot.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_runtime/task_lifecycle_manager.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_runtime/task_lifecycle_manager.pyi +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_runtime/telemetry.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_runtime/user_code_event_loop.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_runtime/user_code_imports.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_serialization.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_server.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_supports_logs.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_traceback.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_tunnel.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_tunnel.pyi +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_type_manager.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/__init__.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/app_utils.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/async_utils.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/auth_token_manager.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/blob_utils.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/browser_utils.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/bytes_io_segment_payload.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/curl_utils.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/deprecation.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/display_utils.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/docker_utils.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/function_utils.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/git_utils.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/grpc_testing.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/grpc_utils.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/hash_utils.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/http_utils.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/idle_countdown.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/jwt_utils.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/logger.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/mount_utils.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/name_utils.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/package_utils.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/pattern_utils.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/proxy_support.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/rand_pb_testing.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/sandbox_fs_utils.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/shell_utils.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/task_command_router_client.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/time_utils.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_vendor/__init__.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_vendor/a2wsgi_wsgi.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_vendor/cloudpickle.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_vendor/tblib.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_vendor/version.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_watcher.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/app.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/app.pyi +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/billing.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/billing.pyi +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/builder/2023.12.312.txt +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/builder/2023.12.txt +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/builder/2024.04.txt +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/builder/2024.10.txt +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/builder/2025.06.txt +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/builder/PREVIEW.txt +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/builder/README.md +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/builder/base-images.json +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/call_graph.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/__init__.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/_download.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/_help.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/_logs.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/_stats.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/_traceback.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/billing.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/changelog.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/cluster.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/config.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/container.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/curl.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/dashboard.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/dict.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/endpoint.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/environment.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/function.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/image.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/import_refs.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/launch.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/logo.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/network_file_system.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/profile.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/programs/__init__.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/programs/run_jupyter.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/programs/vscode.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/queues.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/run.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/secret.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/selector.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/server.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/shell.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/skills.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/token.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/utils.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/volume.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/client.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cloud_bucket_mount.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cloud_bucket_mount.pyi +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cls.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cls.pyi +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/container_process.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/container_process.pyi +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/dict.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/dict.pyi +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/environments.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/environments.pyi +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/exception.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/experimental/__init__.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/experimental/flash.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/experimental/flash.pyi +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/experimental/ipython.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/file_pattern_matcher.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/functions.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/functions.pyi +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/image.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/image.pyi +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/io_streams.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/io_streams.pyi +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/mount.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/mount.pyi +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/network_file_system.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/network_file_system.pyi +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/object.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/object.pyi +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/output.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/parallel_map.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/parallel_map.pyi +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/partial_function.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/partial_function.pyi +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/proxy.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/proxy.pyi +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/py.typed +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/queue.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/queue.pyi +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/retries.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/runner.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/runner.pyi +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/running_app.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/sandbox_fs.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/sandbox_fs.pyi +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/schedule.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/scheduler_placement.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/secret.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/secret.pyi +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/server.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/server.pyi +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/serving.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/serving.pyi +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/skills/modal/SKILL.md +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/snapshot.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/snapshot.pyi +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/stream_type.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/token_flow.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/token_flow.pyi +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/volume.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/volume.pyi +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/workspace.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal.egg-info/dependency_links.txt +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal.egg-info/entry_points.txt +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal.egg-info/requires.txt +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal.egg-info/top_level.txt +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal_docs/__init__.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal_docs/gen_cli_docs.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal_docs/gen_cli_docs_main.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal_docs/gen_reference_docs.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal_docs/gen_reference_docs_main.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal_docs/mdmd/__init__.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal_docs/mdmd/mdmd.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal_docs/mdmd/signatures.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal_docs/mdmd/types.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal_docs/reference_intro.md +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal_proto/__init__.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal_proto/api_grpc.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal_proto/api_pb2_grpc.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal_proto/api_pb2_grpc.pyi +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal_proto/modal_api_grpc.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal_proto/py.typed +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal_proto/task_command_router_grpc.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal_proto/task_command_router_pb2.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal_proto/task_command_router_pb2.pyi +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal_proto/task_command_router_pb2_grpc.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal_version/__main__.py +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/pyproject.toml +0 -0
- {modal-1.5.6.dev17 → modal-1.5.6.dev19}/setup.cfg +0 -0
|
@@ -137,17 +137,20 @@ class _WorkspaceProxyTokenManager:
|
|
|
137
137
|
"""mdmd:hidden"""
|
|
138
138
|
self._workspace = workspace
|
|
139
139
|
|
|
140
|
-
async def create(self) -> TokenData:
|
|
140
|
+
async def create(self, name: str = "") -> TokenData:
|
|
141
141
|
"""Create a new proxy token for the Workspace.
|
|
142
142
|
|
|
143
|
+
Args:
|
|
144
|
+
name: An optional name to help identify the token.
|
|
145
|
+
|
|
143
146
|
Examples:
|
|
144
147
|
```python notest
|
|
145
|
-
token = modal.Workspace.from_context().proxy_tokens.create()
|
|
148
|
+
token = modal.Workspace.from_context().proxy_tokens.create(name="production-webhooks")
|
|
146
149
|
print(token.token_id, token.token_secret)
|
|
147
150
|
```
|
|
148
151
|
"""
|
|
149
152
|
await self._workspace.hydrate()
|
|
150
|
-
resp = await self._workspace.client.stub.WebhookTokenCreate(api_pb2.WebhookTokenCreateRequest())
|
|
153
|
+
resp = await self._workspace.client.stub.WebhookTokenCreate(api_pb2.WebhookTokenCreateRequest(name=name))
|
|
151
154
|
return TokenData(token_id=resp.token_id, token_secret=resp.token_secret)
|
|
152
155
|
|
|
153
156
|
async def list(self, environment_name: Optional[str] = None) -> builtins.list[ProxyTokenInfo]:
|
|
@@ -180,6 +183,8 @@ class _WorkspaceProxyTokenManager:
|
|
|
180
183
|
token_id=token.token_id,
|
|
181
184
|
created_at=timestamp_to_localized_dt(token.created_at),
|
|
182
185
|
scoped=token.scoped,
|
|
186
|
+
name=token.name,
|
|
187
|
+
created_by=token.created_by.username,
|
|
183
188
|
)
|
|
184
189
|
for token in resp.tokens
|
|
185
190
|
]
|
|
@@ -3,13 +3,15 @@ import re
|
|
|
3
3
|
import sys
|
|
4
4
|
import time
|
|
5
5
|
import warnings
|
|
6
|
+
from collections.abc import Mapping
|
|
6
7
|
from json import dumps
|
|
7
8
|
from typing import Literal, get_args
|
|
8
9
|
|
|
9
10
|
import click
|
|
10
11
|
import rich
|
|
11
12
|
from click import UsageError
|
|
12
|
-
from
|
|
13
|
+
from google.protobuf.json_format import MessageToDict
|
|
14
|
+
from rich.table import Column, Table
|
|
13
15
|
from rich.text import Text
|
|
14
16
|
|
|
15
17
|
from modal._environments import ensure_env
|
|
@@ -620,8 +622,9 @@ async def dashboard(
|
|
|
620
622
|
@click.argument("app_identifier")
|
|
621
623
|
@env_option
|
|
622
624
|
@click.option("--json", is_flag=True, default=False)
|
|
625
|
+
@click.option("--no-color", is_flag=True, default=False, help="Disable colors in the output.")
|
|
623
626
|
@synchronizer.create_blocking
|
|
624
|
-
async def info(app_identifier: str, *, env: str | None = None, json: bool = False):
|
|
627
|
+
async def info(app_identifier: str, *, env: str | None = None, json: bool = False, no_color: bool = False):
|
|
625
628
|
"""Show an App's lifecycle, Functions, and Servers.
|
|
626
629
|
|
|
627
630
|
Examples:
|
|
@@ -650,6 +653,17 @@ async def info(app_identifier: str, *, env: str | None = None, json: bool = Fals
|
|
|
650
653
|
state = APP_STATE_TO_MESSAGE.get(lifecycle.app_state, Text("unknown", style="gray"))
|
|
651
654
|
|
|
652
655
|
if json:
|
|
656
|
+
summaries = {
|
|
657
|
+
function_id: MessageToDict(summary, preserving_proto_field_name=True)
|
|
658
|
+
for function_id, summary in resp.function_info_summaries.items()
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
def entries_with_info(entries: Mapping[str, str]) -> dict[str, dict]:
|
|
662
|
+
return {
|
|
663
|
+
name: {"id": function_id, "summary": summaries.get(function_id, {})}
|
|
664
|
+
for name, function_id in sorted(entries.items())
|
|
665
|
+
}
|
|
666
|
+
|
|
653
667
|
output.print_json(
|
|
654
668
|
dumps(
|
|
655
669
|
{
|
|
@@ -665,28 +679,71 @@ async def info(app_identifier: str, *, env: str | None = None, json: bool = Fals
|
|
|
665
679
|
"stopped_at": timestamp_to_localized_str(lifecycle.stopped_at, json),
|
|
666
680
|
"stopped_by": lifecycle.stopped_by or None,
|
|
667
681
|
},
|
|
668
|
-
"functions":
|
|
669
|
-
"servers":
|
|
682
|
+
"functions": entries_with_info(app_info.functions),
|
|
683
|
+
"servers": entries_with_info(app_info.servers),
|
|
670
684
|
}
|
|
671
685
|
)
|
|
672
686
|
)
|
|
673
687
|
return
|
|
674
688
|
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
)
|
|
689
|
+
header = Table(show_header=False, box=None, pad_edge=False, padding=(0, 1))
|
|
690
|
+
header.add_row(Text("App:"), Text(app_info.description))
|
|
691
|
+
header.add_row(Text("App ID:"), Text(app_info.app_id))
|
|
692
|
+
header.add_row(Text("State:"), Text(state.plain))
|
|
678
693
|
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
694
|
+
def event(label: str, timestamp: float, actor: str, version: int = 0) -> None:
|
|
695
|
+
parts = [timestamp_to_localized_str(timestamp, isotz=False), actor]
|
|
696
|
+
if version:
|
|
697
|
+
parts.insert(0, f"v{version}")
|
|
698
|
+
header.add_row(Text(label + ":"), Text(" · ".join(p for p in parts if p)))
|
|
683
699
|
|
|
684
|
-
events: list[list[Text | str | None]] = [
|
|
685
|
-
["Created", timestamp_to_localized_str(lifecycle.created_at, json), lifecycle.created_by]
|
|
686
|
-
]
|
|
687
|
-
if lifecycle.deployed_at:
|
|
688
|
-
label = f"Deployed (v{lifecycle.version})" if lifecycle.version else "Deployed"
|
|
689
|
-
events.append([label, timestamp_to_localized_str(lifecycle.deployed_at, json), lifecycle.deployed_by])
|
|
690
700
|
if lifecycle.stopped_at:
|
|
691
|
-
|
|
692
|
-
|
|
701
|
+
event("Stopped", lifecycle.stopped_at, lifecycle.stopped_by)
|
|
702
|
+
if lifecycle.deployed_at:
|
|
703
|
+
event("Deployment", lifecycle.deployed_at, lifecycle.deployed_by, lifecycle.version)
|
|
704
|
+
event("Created", lifecycle.created_at, lifecycle.created_by)
|
|
705
|
+
output.print(header)
|
|
706
|
+
|
|
707
|
+
name_width = max((len(name) for name in [*app_info.functions, *app_info.servers]), default=0)
|
|
708
|
+
for title, entries in (("Functions", app_info.functions), ("Servers", app_info.servers)):
|
|
709
|
+
if not entries:
|
|
710
|
+
continue
|
|
711
|
+
output.print("")
|
|
712
|
+
output.print(Text(f"{title} ({len(entries)}):"))
|
|
713
|
+
for index, (name, function_id) in enumerate(sorted(entries.items())):
|
|
714
|
+
if index:
|
|
715
|
+
output.print("")
|
|
716
|
+
identity = Table(show_header=False, box=None, pad_edge=False, padding=(0, 1))
|
|
717
|
+
identity.add_column(width=name_width + 2)
|
|
718
|
+
identity.add_column(no_wrap=True)
|
|
719
|
+
identity.add_row(Text(f" {name}"), Text(function_id))
|
|
720
|
+
output.print(identity)
|
|
721
|
+
if function_id not in resp.function_info_summaries:
|
|
722
|
+
continue
|
|
723
|
+
summary = resp.function_info_summaries[function_id]
|
|
724
|
+
hardware = [f"{gpu.count} × {gpu.gpu_type} GPU" for gpu in summary.gpu_config]
|
|
725
|
+
metadata = Text(f" {hardware[0] if hardware else 'CPU'}")
|
|
726
|
+
if len(hardware) > 1:
|
|
727
|
+
metadata.append(f" ({', '.join(hardware[1:])})", style=None if no_color else "bright_black")
|
|
728
|
+
if summary.web_function:
|
|
729
|
+
metadata.append(" · Web function")
|
|
730
|
+
if schedule := _app_function_schedule(summary.schedule):
|
|
731
|
+
metadata.append(" · ")
|
|
732
|
+
metadata.append(schedule)
|
|
733
|
+
if summary.HasField("requires_proxy_auth") and not summary.requires_proxy_auth:
|
|
734
|
+
metadata.append(" · ")
|
|
735
|
+
metadata.append("Unauthenticated", style=None if no_color else "yellow")
|
|
736
|
+
output.print(metadata)
|
|
737
|
+
|
|
738
|
+
|
|
739
|
+
def _app_function_schedule(schedule: api_pb2.Schedule) -> str | None:
|
|
740
|
+
if schedule.HasField("cron"):
|
|
741
|
+
return f"{schedule.cron.cron_string} ({schedule.cron.timezone or 'UTC'})"
|
|
742
|
+
if schedule.HasField("period"):
|
|
743
|
+
parts = [
|
|
744
|
+
f"{value:g} {field.name.removesuffix('s') if value == 1 else field.name}"
|
|
745
|
+
for field, value in schedule.period.ListFields()
|
|
746
|
+
if value
|
|
747
|
+
]
|
|
748
|
+
return "Every " + ", ".join(parts)
|
|
749
|
+
return None
|
|
@@ -11,7 +11,6 @@ from . import run, shell as shell_module
|
|
|
11
11
|
from ._help import ModalCommand, ModalGroup, ModalProfileOption
|
|
12
12
|
from .app import app_cli
|
|
13
13
|
from .billing import billing_cli
|
|
14
|
-
from .bootstrap import bootstrap
|
|
15
14
|
from .changelog import changelog
|
|
16
15
|
from .cluster import cluster_cli
|
|
17
16
|
from .config import config_cli
|
|
@@ -131,8 +130,6 @@ entrypoint_cli.add_command(secret_cli, panel="Storage")
|
|
|
131
130
|
entrypoint_cli.add_command(queue_cli, panel="Storage")
|
|
132
131
|
entrypoint_cli.add_command(volume_cli, panel="Storage")
|
|
133
132
|
|
|
134
|
-
entrypoint_cli.add_command(setup, panel="Onboarding")
|
|
135
|
-
entrypoint_cli.add_command(bootstrap, panel="Onboarding")
|
|
136
133
|
|
|
137
134
|
entrypoint_cli.add_command(workspace_cli, panel="Configuration")
|
|
138
135
|
entrypoint_cli.add_command(environment_cli, panel="Configuration")
|
|
@@ -140,6 +137,7 @@ entrypoint_cli.add_command(profile_cli, panel="Configuration")
|
|
|
140
137
|
entrypoint_cli.add_command(config_cli, panel="Configuration")
|
|
141
138
|
entrypoint_cli.add_command(token_cli, panel="Configuration")
|
|
142
139
|
entrypoint_cli.add_command(skills_cli, panel="Configuration")
|
|
140
|
+
entrypoint_cli.add_command(setup, panel="Configuration")
|
|
143
141
|
|
|
144
142
|
entrypoint_cli.add_command(billing_cli, panel="Observability")
|
|
145
143
|
entrypoint_cli.add_command(changelog, panel="Observability")
|
|
@@ -76,15 +76,23 @@ proxy_tokens_cli = ModalGroup(name="proxy-tokens", help=PROXY_TOKENS_HELP_TEXT)
|
|
|
76
76
|
workspace_cli.add_command(proxy_tokens_cli)
|
|
77
77
|
|
|
78
78
|
|
|
79
|
-
@proxy_tokens_cli.command("create"
|
|
79
|
+
@proxy_tokens_cli.command("create")
|
|
80
|
+
@click.option("--name", default="", help="Name to help identify the token.")
|
|
80
81
|
@click.option("--json", is_flag=True, default=False)
|
|
81
|
-
def proxy_tokens_create(*, json: bool = False):
|
|
82
|
+
def proxy_tokens_create(*, name: str = "", json: bool = False):
|
|
82
83
|
"""Create a proxy token in the current Workspace.
|
|
83
84
|
|
|
84
85
|
The new token's ID and secret will be printed to stdout. The secret is only
|
|
85
86
|
shown at creation time and cannot be retrieved later.
|
|
87
|
+
|
|
88
|
+
Examples:
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
modal workspace proxy-tokens create --name production-webhooks
|
|
92
|
+
modal workspace proxy-tokens create --json
|
|
93
|
+
```
|
|
86
94
|
"""
|
|
87
|
-
token = Workspace.from_context().proxy_tokens.create()
|
|
95
|
+
token = Workspace.from_context().proxy_tokens.create(name=name)
|
|
88
96
|
output_manager = OutputManager.get()
|
|
89
97
|
bearer = f"Bearer {token.token_id}.{token.token_secret}"
|
|
90
98
|
if json:
|
|
@@ -104,7 +112,7 @@ def proxy_tokens_create(*, json: bool = False):
|
|
|
104
112
|
output_manager.print(table)
|
|
105
113
|
|
|
106
114
|
|
|
107
|
-
@proxy_tokens_cli.command("list"
|
|
115
|
+
@proxy_tokens_cli.command("list")
|
|
108
116
|
@click.option(
|
|
109
117
|
"-e",
|
|
110
118
|
"--environment",
|
|
@@ -113,39 +121,75 @@ def proxy_tokens_create(*, json: bool = False):
|
|
|
113
121
|
)
|
|
114
122
|
@click.option("--json", is_flag=True, default=False)
|
|
115
123
|
def proxy_tokens_list(environment: str | None = None, json: bool = False):
|
|
124
|
+
"""List the proxy tokens of the current Workspace.
|
|
125
|
+
|
|
126
|
+
Examples:
|
|
127
|
+
|
|
128
|
+
```
|
|
129
|
+
modal workspace proxy-tokens list
|
|
130
|
+
modal workspace proxy-tokens list --environment prod
|
|
131
|
+
modal workspace proxy-tokens list --json
|
|
132
|
+
```
|
|
133
|
+
"""
|
|
116
134
|
tokens = Workspace.from_context().proxy_tokens.list(environment_name=environment)
|
|
117
135
|
# Emit a real boolean for JSON output, but a string for the rich table (which can't render a bare bool).
|
|
118
136
|
rows = [
|
|
119
137
|
[
|
|
138
|
+
token.name,
|
|
120
139
|
token.token_id,
|
|
121
140
|
timestamp_to_localized_str(token.created_at.timestamp(), json),
|
|
141
|
+
token.created_by,
|
|
122
142
|
token.scoped if json else str(token.scoped),
|
|
123
143
|
]
|
|
124
144
|
for token in tokens
|
|
125
145
|
]
|
|
126
|
-
display_table(["Token ID", "Created at", "Scoped"], rows, json=json)
|
|
146
|
+
display_table(["Name", "Token ID", "Created at", "Created by", "Scoped"], rows, json=json)
|
|
127
147
|
|
|
128
148
|
|
|
129
|
-
@proxy_tokens_cli.command("allow",
|
|
149
|
+
@proxy_tokens_cli.command("allow", no_args_is_help=True)
|
|
130
150
|
@click.argument("token_id")
|
|
131
151
|
@click.argument("environment_name")
|
|
132
152
|
def proxy_tokens_allow(token_id: str, environment_name: str):
|
|
153
|
+
"""Allow a proxy token to authenticate to an environment.
|
|
154
|
+
|
|
155
|
+
Example:
|
|
156
|
+
|
|
157
|
+
```
|
|
158
|
+
modal workspace proxy-tokens allow wk-123 prod
|
|
159
|
+
```
|
|
160
|
+
"""
|
|
133
161
|
Workspace.from_context().proxy_tokens.allow(token_id, environment_name)
|
|
134
162
|
rich.print(f"[green]✓[/green] Allowed proxy token {token_id!r} to authenticate to environment {environment_name!r}")
|
|
135
163
|
|
|
136
164
|
|
|
137
|
-
@proxy_tokens_cli.command("revoke",
|
|
165
|
+
@proxy_tokens_cli.command("revoke", no_args_is_help=True)
|
|
138
166
|
@click.argument("token_id")
|
|
139
167
|
@click.argument("environment_name")
|
|
140
168
|
def proxy_tokens_revoke(token_id: str, environment_name: str):
|
|
169
|
+
"""Revoke a proxy token's access to an environment.
|
|
170
|
+
|
|
171
|
+
Example:
|
|
172
|
+
|
|
173
|
+
```
|
|
174
|
+
modal workspace proxy-tokens revoke wk-123 prod
|
|
175
|
+
```
|
|
176
|
+
"""
|
|
141
177
|
Workspace.from_context().proxy_tokens.revoke(token_id, environment_name)
|
|
142
178
|
rich.print(f"[green]✓[/green] Revoked proxy token {token_id!r} access to environment {environment_name!r}")
|
|
143
179
|
|
|
144
180
|
|
|
145
|
-
@proxy_tokens_cli.command("delete",
|
|
181
|
+
@proxy_tokens_cli.command("delete", no_args_is_help=True)
|
|
146
182
|
@click.argument("token_id")
|
|
147
183
|
@yes_option
|
|
148
184
|
def proxy_tokens_delete(token_id: str, yes: bool = False):
|
|
185
|
+
"""Delete a proxy token from the current Workspace.
|
|
186
|
+
|
|
187
|
+
Example:
|
|
188
|
+
|
|
189
|
+
```
|
|
190
|
+
modal workspace proxy-tokens delete wk-123
|
|
191
|
+
```
|
|
192
|
+
"""
|
|
149
193
|
if not yes:
|
|
150
194
|
message = (
|
|
151
195
|
f"Are you sure you want to delete proxy token {token_id!r}? Requests using this token will be rejected."
|
|
@@ -77,7 +77,7 @@ class _Client:
|
|
|
77
77
|
server_url: str,
|
|
78
78
|
client_type: int,
|
|
79
79
|
credentials: typing.Optional[tuple[str, str]],
|
|
80
|
-
version: str = "1.5.6.
|
|
80
|
+
version: str = "1.5.6.dev19",
|
|
81
81
|
*,
|
|
82
82
|
oauth_credentials: typing.Optional[_OAuthCredentials] = None,
|
|
83
83
|
):
|
|
@@ -277,7 +277,7 @@ class Client:
|
|
|
277
277
|
server_url: str,
|
|
278
278
|
client_type: int,
|
|
279
279
|
credentials: typing.Optional[tuple[str, str]],
|
|
280
|
-
version: str = "1.5.6.
|
|
280
|
+
version: str = "1.5.6.dev19",
|
|
281
281
|
*,
|
|
282
282
|
oauth_credentials: typing.Optional[_OAuthCredentials] = None,
|
|
283
283
|
):
|
|
@@ -377,6 +377,7 @@ _SETTINGS = {
|
|
|
377
377
|
"image_builder_version": _Setting(),
|
|
378
378
|
"strict_parameters": _Setting(False, transform=_to_boolean), # For internal/experimental use
|
|
379
379
|
"sandbox_v2": _Setting(None, transform=_to_boolean),
|
|
380
|
+
"use_control_plane_sidecar_create": _Setting(False, transform=_to_boolean), # For internal/experimental use
|
|
380
381
|
"snapshot_debug": _Setting(False, transform=_to_boolean),
|
|
381
382
|
"cuda_checkpoint_path": _Setting("/__modal/.bin/cuda-checkpoint"), # Used for snapshotting GPU memory.
|
|
382
383
|
# Used to indicate gVisor cuda-checkpoint automation.
|
|
@@ -2979,6 +2979,15 @@ class _SidecarContainer:
|
|
|
2979
2979
|
_MAIN_CONTAINER_NAME: str = "main"
|
|
2980
2980
|
|
|
2981
2981
|
|
|
2982
|
+
def _use_control_plane_sidecar_create(is_v2: bool) -> bool:
|
|
2983
|
+
"""Whether a sidecar create request goes to the Modal server rather than over the Sandbox connection.
|
|
2984
|
+
|
|
2985
|
+
Opt-in via the `use_control_plane_sidecar_create` config setting. Only V2
|
|
2986
|
+
Sandboxes can; V1 Sandboxes always create sidecars over the Sandbox connection.
|
|
2987
|
+
"""
|
|
2988
|
+
return is_v2 and config.get("use_control_plane_sidecar_create")
|
|
2989
|
+
|
|
2990
|
+
|
|
2982
2991
|
class _SidecarManager:
|
|
2983
2992
|
"""Creates and manages sidecar containers in a Sandbox."""
|
|
2984
2993
|
|
|
@@ -3060,8 +3069,13 @@ class _SidecarManager:
|
|
|
3060
3069
|
"or `.snapshot_filesystem()`\n"
|
|
3061
3070
|
)
|
|
3062
3071
|
|
|
3063
|
-
|
|
3064
|
-
|
|
3072
|
+
env_dict, resolvable_secrets = _split_env_dict_and_resolvable_secrets(list(secrets or []))
|
|
3073
|
+
if env:
|
|
3074
|
+
# `env` takes precedence over environment variables from secrets
|
|
3075
|
+
env_dict |= env
|
|
3076
|
+
_validate_sandbox_env(env_dict)
|
|
3077
|
+
|
|
3078
|
+
hydrate_coros = [secret.hydrate(client=self._sandbox._client) for secret in resolvable_secrets] + [
|
|
3065
3079
|
volume.hydrate(client=self._sandbox._client) for _, volume in validated_volumes
|
|
3066
3080
|
]
|
|
3067
3081
|
await TaskContext.gather(*hydrate_coros)
|
|
@@ -3073,21 +3087,48 @@ class _SidecarManager:
|
|
|
3073
3087
|
# Relies on dicts being ordered (true as of Python 3.6).
|
|
3074
3088
|
volume_mounts = [_volume_to_mount_proto(path, volume) for path, volume in validated_volumes]
|
|
3075
3089
|
|
|
3076
|
-
|
|
3090
|
+
network_access = _build_outbound_network_access(False, outbound_cidr_allowlist, outbound_domain_allowlist)
|
|
3091
|
+
pty_info = _Sandbox._default_pty_info() if pty else None
|
|
3092
|
+
|
|
3093
|
+
if _use_control_plane_sidecar_create(self._sandbox._is_v2):
|
|
3094
|
+
definition = api_pb2.Sandbox(
|
|
3095
|
+
entrypoint_args=list(args),
|
|
3096
|
+
image_id=image.object_id,
|
|
3097
|
+
secret_ids=[secret.object_id for secret in resolvable_secrets],
|
|
3098
|
+
workdir=workdir,
|
|
3099
|
+
volume_mounts=volume_mounts,
|
|
3100
|
+
network_access=network_access,
|
|
3101
|
+
pty_info=pty_info,
|
|
3102
|
+
)
|
|
3103
|
+
create_req = api_pb2.SandboxContainerCreateV2Request(
|
|
3104
|
+
sandbox_id=self._sandbox.object_id,
|
|
3105
|
+
container_name=name,
|
|
3106
|
+
definition=definition,
|
|
3107
|
+
ephemeral_secrets=api_pb2.StringMap(contents=env_dict) if env_dict else None,
|
|
3108
|
+
)
|
|
3109
|
+
client = self._sandbox._client
|
|
3110
|
+
assert client._auth_token_manager
|
|
3111
|
+
auth_token = await client._auth_token_manager.get_token()
|
|
3112
|
+
create_resp = await client.stub.SandboxContainerCreateV2(
|
|
3113
|
+
create_req, metadata=[("x-modal-auth-token", auth_token)]
|
|
3114
|
+
)
|
|
3115
|
+
else:
|
|
3116
|
+
task_id, command_router_client = await self._get_command_router()
|
|
3117
|
+
create_resp = await command_router_client.container_create(
|
|
3118
|
+
sr_pb2.TaskContainerCreateRequest(
|
|
3119
|
+
task_id=task_id,
|
|
3120
|
+
container_name=name,
|
|
3121
|
+
image_id=image.object_id,
|
|
3122
|
+
args=list(args),
|
|
3123
|
+
env=env_dict,
|
|
3124
|
+
workdir=workdir or "",
|
|
3125
|
+
secret_ids=[secret.object_id for secret in resolvable_secrets],
|
|
3126
|
+
volume_mounts=volume_mounts,
|
|
3127
|
+
network_access=network_access,
|
|
3128
|
+
pty_info=pty_info,
|
|
3129
|
+
)
|
|
3130
|
+
)
|
|
3077
3131
|
|
|
3078
|
-
create_req = sr_pb2.TaskContainerCreateRequest(
|
|
3079
|
-
task_id=task_id,
|
|
3080
|
-
container_name=name,
|
|
3081
|
-
image_id=image.object_id,
|
|
3082
|
-
args=list(args),
|
|
3083
|
-
env=env or {},
|
|
3084
|
-
workdir=workdir or "",
|
|
3085
|
-
secret_ids=[secret.object_id for secret in secrets],
|
|
3086
|
-
volume_mounts=volume_mounts,
|
|
3087
|
-
network_access=_build_outbound_network_access(False, outbound_cidr_allowlist, outbound_domain_allowlist),
|
|
3088
|
-
pty_info=_Sandbox._default_pty_info() if pty else None,
|
|
3089
|
-
)
|
|
3090
|
-
create_resp = await command_router_client.container_create(create_req)
|
|
3091
3132
|
container_id = create_resp.container_id
|
|
3092
3133
|
container_name = create_resp.container_name or name
|
|
3093
3134
|
return _SidecarContainer(self._sandbox, container_id, container_name)
|
|
@@ -1222,6 +1222,14 @@ class _SidecarContainer:
|
|
|
1222
1222
|
"""
|
|
1223
1223
|
...
|
|
1224
1224
|
|
|
1225
|
+
def _use_control_plane_sidecar_create(is_v2: bool) -> bool:
|
|
1226
|
+
"""Whether a sidecar create request goes to the Modal server rather than over the Sandbox connection.
|
|
1227
|
+
|
|
1228
|
+
Opt-in via the `use_control_plane_sidecar_create` config setting. Only V2
|
|
1229
|
+
Sandboxes can; V1 Sandboxes always create sidecars over the Sandbox connection.
|
|
1230
|
+
"""
|
|
1231
|
+
...
|
|
1232
|
+
|
|
1225
1233
|
class _SidecarManager:
|
|
1226
1234
|
"""Creates and manages sidecar containers in a Sandbox."""
|
|
1227
1235
|
def __init__(self, sandbox: _Sandbox) -> None:
|
|
@@ -12,8 +12,12 @@ class ProxyTokenInfo:
|
|
|
12
12
|
token_id: str
|
|
13
13
|
created_at: datetime.datetime
|
|
14
14
|
scoped: bool
|
|
15
|
+
name: str
|
|
16
|
+
created_by: str
|
|
15
17
|
|
|
16
|
-
def __init__(
|
|
18
|
+
def __init__(
|
|
19
|
+
self, token_id: str, created_at: datetime.datetime, scoped: bool, name: str = "", created_by: str = ""
|
|
20
|
+
) -> None:
|
|
17
21
|
"""Initialize self. See help(type(self)) for accurate signature."""
|
|
18
22
|
...
|
|
19
23
|
|
|
@@ -395,23 +399,29 @@ class WorkspaceProxyTokenManager:
|
|
|
395
399
|
...
|
|
396
400
|
|
|
397
401
|
class __create_spec(typing_extensions.Protocol):
|
|
398
|
-
def __call__(self,
|
|
402
|
+
def __call__(self, /, name: str = "") -> modal.types.TokenData:
|
|
399
403
|
"""Create a new proxy token for the Workspace.
|
|
400
404
|
|
|
405
|
+
Args:
|
|
406
|
+
name: An optional name to help identify the token.
|
|
407
|
+
|
|
401
408
|
Examples:
|
|
402
409
|
```python notest
|
|
403
|
-
token = modal.Workspace.from_context().proxy_tokens.create()
|
|
410
|
+
token = modal.Workspace.from_context().proxy_tokens.create(name="production-webhooks")
|
|
404
411
|
print(token.token_id, token.token_secret)
|
|
405
412
|
```
|
|
406
413
|
"""
|
|
407
414
|
...
|
|
408
415
|
|
|
409
|
-
async def aio(self,
|
|
416
|
+
async def aio(self, /, name: str = "") -> modal.types.TokenData:
|
|
410
417
|
"""Create a new proxy token for the Workspace.
|
|
411
418
|
|
|
419
|
+
Args:
|
|
420
|
+
name: An optional name to help identify the token.
|
|
421
|
+
|
|
412
422
|
Examples:
|
|
413
423
|
```python notest
|
|
414
|
-
token = modal.Workspace.from_context().proxy_tokens.create()
|
|
424
|
+
token = modal.Workspace.from_context().proxy_tokens.create(name="production-webhooks")
|
|
415
425
|
print(token.token_id, token.token_secret)
|
|
416
426
|
```
|
|
417
427
|
"""
|