runta-sdk 0.2.2__tar.gz → 0.2.3__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 (364) hide show
  1. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/PKG-INFO +30 -1
  2. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/README.md +29 -0
  3. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/integrations/harbor/runta_harbor/environment.py +13 -21
  4. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/pyproject.toml +1 -1
  5. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/runta_sdk.egg-info/PKG-INFO +30 -1
  6. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/runta_sdk.egg-info/SOURCES.txt +2 -0
  7. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/__init__.py +72 -1
  8. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/async_http.py +24 -0
  9. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/runtime.py +21 -0
  10. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/streaming_transfer.py +228 -5
  11. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/sync_http.py +23 -0
  12. runta_sdk-0.2.3/tests/test_download_dir_contents.py +541 -0
  13. runta_sdk-0.2.3/tests/test_download_dir_contents_live.py +126 -0
  14. runta_sdk-0.2.3/tests/test_harbor_directory_download.py +76 -0
  15. runta_sdk-0.2.2/tests/test_harbor_directory_download.py +0 -193
  16. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/integrations/harbor/runta_harbor/__init__.py +0 -0
  17. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/integrations/harbor/runta_harbor/py.typed +0 -0
  18. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/integrations/openai-agents-runta/src/agents_runta/__init__.py +0 -0
  19. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/integrations/openai-agents-runta/src/agents_runta/durable/__init__.py +0 -0
  20. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/integrations/openai-agents-runta/src/agents_runta/durable/runner.py +0 -0
  21. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/integrations/openai-agents-runta/src/agents_runta/durable/store.py +0 -0
  22. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/integrations/openai-agents-runta/src/agents_runta/py.typed +0 -0
  23. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/integrations/openai-agents-runta/src/agents_runta/sandbox.py +0 -0
  24. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/runta_sdk.egg-info/dependency_links.txt +0 -0
  25. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/runta_sdk.egg-info/requires.txt +0 -0
  26. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/runta_sdk.egg-info/top_level.txt +0 -0
  27. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/setup.cfg +0 -0
  28. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/__init__.py +0 -0
  29. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/async_file_io.py +0 -0
  30. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/byte_budget.py +0 -0
  31. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/config.py +0 -0
  32. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/execution.py +0 -0
  33. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/execution.sh +0 -0
  34. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/helpers.py +0 -0
  35. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/http_limits.py +0 -0
  36. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/__init__.py +0 -0
  37. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/__init__.py +0 -0
  38. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/auth/__init__.py +0 -0
  39. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/auth/begin_device_authorization.py +0 -0
  40. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/auth/exchange_device_token.py +0 -0
  41. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/auth/revoke_current_token.py +0 -0
  42. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/checkpoints/__init__.py +0 -0
  43. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/checkpoints/create_checkpoint.py +0 -0
  44. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/checkpoints/delete_checkpoint.py +0 -0
  45. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/checkpoints/get_checkpoint.py +0 -0
  46. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/checkpoints/list_checkpoints.py +0 -0
  47. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/cloud_agents/__init__.py +0 -0
  48. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/cloud_agents/cancel_cloud_agent_run.py +0 -0
  49. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/cloud_agents/create_cloud_agent.py +0 -0
  50. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/cloud_agents/create_cloud_agent_artifact.py +0 -0
  51. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/cloud_agents/create_cloud_agent_computer_session.py +0 -0
  52. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/cloud_agents/create_cloud_agent_run.py +0 -0
  53. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/cloud_agents/delete_cloud_agent.py +0 -0
  54. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/cloud_agents/delete_cloud_agent_artifact.py +0 -0
  55. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/cloud_agents/delete_cloud_agent_workspace_file.py +0 -0
  56. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/cloud_agents/execute_cloud_agent_workspace_operation.py +0 -0
  57. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/cloud_agents/follow_up_cloud_agent_run.py +0 -0
  58. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/cloud_agents/get_cloud_agent.py +0 -0
  59. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/cloud_agents/get_cloud_agent_artifact.py +0 -0
  60. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/cloud_agents/get_cloud_agent_run.py +0 -0
  61. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/cloud_agents/list_cloud_agent_artifacts.py +0 -0
  62. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/cloud_agents/list_cloud_agent_runs.py +0 -0
  63. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/cloud_agents/list_cloud_agent_session_events.py +0 -0
  64. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/cloud_agents/list_cloud_agents.py +0 -0
  65. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/cloud_agents/resume_cloud_agent_run.py +0 -0
  66. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/cloud_agents/stream_cloud_agent_run_events.py +0 -0
  67. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/cloud_agents/update_cloud_agent.py +0 -0
  68. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/cloud_agents/write_cloud_agent_workspace_file.py +0 -0
  69. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/events/__init__.py +0 -0
  70. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/events/get_organization_event.py +0 -0
  71. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/events/get_organization_token_analysis.py +0 -0
  72. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/events/list_organization_events.py +0 -0
  73. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/events/list_organization_token_analysis.py +0 -0
  74. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/events/list_organization_token_savings.py +0 -0
  75. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/files/__init__.py +0 -0
  76. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/files/read_runtime_file.py +0 -0
  77. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/files/write_runtime_file.py +0 -0
  78. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/git_hub/__init__.py +0 -0
  79. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/git_hub/create_git_hub_authorization_request.py +0 -0
  80. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/git_hub/create_git_hub_runtime_binding.py +0 -0
  81. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/git_hub/delete_git_hub_runtime_binding.py +0 -0
  82. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/git_hub/get_git_hub_authorization_request.py +0 -0
  83. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/git_hub/get_git_hub_runtime_binding.py +0 -0
  84. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/git_hub/list_git_hub_authorization_requests.py +0 -0
  85. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/git_hub/list_git_hub_branches.py +0 -0
  86. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/git_hub/list_git_hub_repositories.py +0 -0
  87. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/git_hub/replace_git_hub_runtime_bindings.py +0 -0
  88. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/health/__init__.py +0 -0
  89. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/health/head_healthz.py +0 -0
  90. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/health/healthz.py +0 -0
  91. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/identity/__init__.py +0 -0
  92. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/identity/get_me.py +0 -0
  93. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/managed_model_providers/__init__.py +0 -0
  94. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/managed_model_providers/list_managed_model_providers.py +0 -0
  95. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/managed_model_providers/update_managed_model_provider.py +0 -0
  96. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/runtimes/__init__.py +0 -0
  97. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/runtimes/cancel_runtime_image_build.py +0 -0
  98. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/runtimes/create_runtime.py +0 -0
  99. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/runtimes/create_runtime_image_build.py +0 -0
  100. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/runtimes/create_runtime_vnc_credential.py +0 -0
  101. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/runtimes/delete_runtime.py +0 -0
  102. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/runtimes/delete_runtime_image.py +0 -0
  103. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/runtimes/disable_runtime_vnc.py +0 -0
  104. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/runtimes/dismiss_runtime_image_build.py +0 -0
  105. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/runtimes/enable_runtime_vnc.py +0 -0
  106. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/runtimes/get_runtime.py +0 -0
  107. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/runtimes/get_runtime_image_build.py +0 -0
  108. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/runtimes/list_runtime_image_builds.py +0 -0
  109. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/runtimes/list_runtime_images.py +0 -0
  110. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/runtimes/list_runtime_vnc_credentials.py +0 -0
  111. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/runtimes/list_runtimes.py +0 -0
  112. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/runtimes/patch_runtime.py +0 -0
  113. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/runtimes/pause_runtime.py +0 -0
  114. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/runtimes/resume_runtime.py +0 -0
  115. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/runtimes/revoke_runtime_vnc_credential.py +0 -0
  116. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/runtimes/start_runtime.py +0 -0
  117. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/runtimes/stop_runtime.py +0 -0
  118. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/secrets/__init__.py +0 -0
  119. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/secrets/create_secret.py +0 -0
  120. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/secrets/delete_secret.py +0 -0
  121. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/secrets/get_secret.py +0 -0
  122. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/secrets/list_secrets.py +0 -0
  123. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/secrets/set_secret.py +0 -0
  124. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/ssh_keys/__init__.py +0 -0
  125. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/ssh_keys/attach_runtime_ssh_key.py +0 -0
  126. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/ssh_keys/create_ssh_key.py +0 -0
  127. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/ssh_keys/delete_ssh_key.py +0 -0
  128. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/ssh_keys/detach_runtime_ssh_key.py +0 -0
  129. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/ssh_keys/get_ssh_host_key.py +0 -0
  130. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/ssh_keys/list_runtime_ssh_keys.py +0 -0
  131. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/api/ssh_keys/list_ssh_keys.py +0 -0
  132. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/client.py +0 -0
  133. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/errors.py +0 -0
  134. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/__init__.py +0 -0
  135. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/allowlist_egress_policy.py +0 -0
  136. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/allowlist_egress_policy_mode.py +0 -0
  137. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/approved_git_hub_authorization.py +0 -0
  138. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/approved_git_hub_authorization_status.py +0 -0
  139. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/begin_device_authorization_request.py +0 -0
  140. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/begin_device_authorization_request_client_id.py +0 -0
  141. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/checkpoint.py +0 -0
  142. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/checkpoint_pagination.py +0 -0
  143. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/checkpoint_response.py +0 -0
  144. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/checkpoint_runtime_restore_request.py +0 -0
  145. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/checkpoint_source_runtime.py +0 -0
  146. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/cloud_agent.py +0 -0
  147. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/cloud_agent_artifact.py +0 -0
  148. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/cloud_agent_computer_session.py +0 -0
  149. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/cloud_agent_computer_session_channels.py +0 -0
  150. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/cloud_agent_event.py +0 -0
  151. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/cloud_agent_harness_request.py +0 -0
  152. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/cloud_agent_latest_reply.py +0 -0
  153. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/cloud_agent_list.py +0 -0
  154. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/cloud_agent_prompt_image.py +0 -0
  155. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/cloud_agent_prompt_image_mime_type.py +0 -0
  156. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/cloud_agent_prompt_image_type.py +0 -0
  157. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/cloud_agent_run.py +0 -0
  158. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/cloud_agent_runtime_reference.py +0 -0
  159. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/cloud_agent_runtime_reference_type.py +0 -0
  160. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/cloud_agent_session_events.py +0 -0
  161. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/cloud_agent_vnc_session_channel.py +0 -0
  162. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/cloud_agent_workspace_delete_request.py +0 -0
  163. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/cloud_agent_workspace_write_request.py +0 -0
  164. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/cloud_agent_workspace_write_result.py +0 -0
  165. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/cold_restore_resource_requests.py +0 -0
  166. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/cold_restore_resources.py +0 -0
  167. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/create_checkpoint_request.py +0 -0
  168. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/create_checkpoint_request_kind.py +0 -0
  169. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/create_cloud_agent_artifact_request.py +0 -0
  170. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/create_cloud_agent_request.py +0 -0
  171. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/create_cloud_agent_run_request.py +0 -0
  172. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/create_git_hub_authorization_request_request.py +0 -0
  173. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/create_git_hub_runtime_binding_request.py +0 -0
  174. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/create_llm_token_saving_policy.py +0 -0
  175. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/create_runtime_image_build_request.py +0 -0
  176. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/create_runtime_image_build_request_architecture.py +0 -0
  177. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/create_runtime_image_selection.py +0 -0
  178. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/create_runtime_resource_requests.py +0 -0
  179. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/create_runtime_resources.py +0 -0
  180. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/create_secret_request.py +0 -0
  181. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/create_ssh_key_request.py +0 -0
  182. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/create_vnc_credential_request.py +0 -0
  183. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/create_vnc_credential_response.py +0 -0
  184. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/denied_git_hub_authorization.py +0 -0
  185. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/denied_git_hub_authorization_status.py +0 -0
  186. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/denylist_egress_policy.py +0 -0
  187. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/denylist_egress_policy_mode.py +0 -0
  188. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/device_authorization_start.py +0 -0
  189. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/device_authorization_start_response.py +0 -0
  190. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/device_token_error.py +0 -0
  191. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/device_token_response.py +0 -0
  192. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/disabled_idle_policy.py +0 -0
  193. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/disabled_idle_policy_mode.py +0 -0
  194. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/empty_object.py +0 -0
  195. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/empty_response.py +0 -0
  196. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/error_body.py +0 -0
  197. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/error_envelope.py +0 -0
  198. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/event.py +0 -0
  199. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/event_list.py +0 -0
  200. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/event_list_response.py +0 -0
  201. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/event_response.py +0 -0
  202. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/exchange_device_token_request.py +0 -0
  203. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/expired_git_hub_authorization.py +0 -0
  204. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/expired_git_hub_authorization_status.py +0 -0
  205. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/fresh_runtime_create_request.py +0 -0
  206. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/fresh_runtime_create_request_environment_variables.py +0 -0
  207. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/git_hub_authorization_decision_metadata.py +0 -0
  208. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/git_hub_authorization_request.py +0 -0
  209. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/git_hub_authorization_request_list.py +0 -0
  210. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/git_hub_authorization_request_list_response.py +0 -0
  211. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/git_hub_authorization_request_response.py +0 -0
  212. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/git_hub_branch.py +0 -0
  213. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/git_hub_branch_list.py +0 -0
  214. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/git_hub_branch_list_response.py +0 -0
  215. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/git_hub_installation.py +0 -0
  216. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/git_hub_repository.py +0 -0
  217. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/git_hub_repository_list.py +0 -0
  218. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/git_hub_repository_list_response.py +0 -0
  219. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/git_hub_runtime_binding.py +0 -0
  220. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/git_hub_runtime_binding_list.py +0 -0
  221. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/git_hub_runtime_binding_list_response.py +0 -0
  222. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/git_hub_runtime_binding_response.py +0 -0
  223. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/installed_git_hub_authorization.py +0 -0
  224. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/installed_git_hub_authorization_status.py +0 -0
  225. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/list_checkpoints_response.py +0 -0
  226. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/list_runtime_image_builds_response.py +0 -0
  227. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/list_runtime_images_response.py +0 -0
  228. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/list_runtimes_response.py +0 -0
  229. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/list_secrets_response.py +0 -0
  230. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/list_ssh_keys_response.py +0 -0
  231. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/list_vnc_credentials_response.py +0 -0
  232. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/llm_token_saving_policy.py +0 -0
  233. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/managed_model_provider.py +0 -0
  234. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/managed_model_provider_list.py +0 -0
  235. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/managed_model_provider_reference.py +0 -0
  236. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/managed_model_provider_reference_type.py +0 -0
  237. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/patch_llm_token_saving_policy.py +0 -0
  238. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/patch_runtime_request.py +0 -0
  239. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/patch_runtime_request_environment_variables.py +0 -0
  240. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/patch_runtime_resource_requests.py +0 -0
  241. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/patch_runtime_resources.py +0 -0
  242. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/pending_git_hub_authorization.py +0 -0
  243. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/pending_git_hub_authorization_status.py +0 -0
  244. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/public_checkpoint_state.py +0 -0
  245. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/public_error_code.py +0 -0
  246. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/public_ingress_protocol.py +0 -0
  247. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/public_ingress_spec.py +0 -0
  248. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/public_runtime_status.py +0 -0
  249. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/replace_git_hub_runtime_bindings_request.py +0 -0
  250. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/replace_git_hub_runtime_bindings_request_repositories_item.py +0 -0
  251. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/runtime_desired_status.py +0 -0
  252. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/runtime_environment_variables.py +0 -0
  253. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/runtime_image.py +0 -0
  254. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/runtime_image_architectures_item.py +0 -0
  255. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/runtime_image_build.py +0 -0
  256. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/runtime_image_build_architecture.py +0 -0
  257. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/runtime_image_build_attempt.py +0 -0
  258. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/runtime_image_build_attempt_outcome.py +0 -0
  259. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/runtime_image_build_response.py +0 -0
  260. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/runtime_image_build_status.py +0 -0
  261. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/runtime_image_disk_capability.py +0 -0
  262. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/runtime_image_model_provider.py +0 -0
  263. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/runtime_image_protocol_binding.py +0 -0
  264. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/runtime_image_recommended_resources.py +0 -0
  265. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/runtime_image_subscription_option.py +0 -0
  266. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/runtime_image_web_ui.py +0 -0
  267. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/runtime_pagination.py +0 -0
  268. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/runtime_repository_selection.py +0 -0
  269. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/runtime_resource_current.py +0 -0
  270. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/runtime_resource_limits.py +0 -0
  271. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/runtime_resource_requests.py +0 -0
  272. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/runtime_resources.py +0 -0
  273. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/runtime_response.py +0 -0
  274. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/runtime_secret_configuration.py +0 -0
  275. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/runtime_workspace_file_entry.py +0 -0
  276. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/runtime_workspace_file_kind.py +0 -0
  277. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/runtime_workspace_operation_kind.py +0 -0
  278. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/runtime_workspace_operation_request.py +0 -0
  279. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/runtime_workspace_operation_result.py +0 -0
  280. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/runtime_workspace_port.py +0 -0
  281. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/runtime_workspace_search_match.py +0 -0
  282. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/secret.py +0 -0
  283. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/secret_egress_injection.py +0 -0
  284. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/secret_environment_injection.py +0 -0
  285. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/secret_file_injection.py +0 -0
  286. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/secret_injection_action.py +0 -0
  287. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/secret_response.py +0 -0
  288. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/set_secret_request.py +0 -0
  289. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/ssh_host_key.py +0 -0
  290. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/ssh_host_key_response.py +0 -0
  291. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/ssh_key.py +0 -0
  292. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/ssh_key_response.py +0 -0
  293. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/suspend_and_wakeup_idle_policy.py +0 -0
  294. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/suspend_and_wakeup_idle_policy_mode.py +0 -0
  295. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/suspend_only_idle_policy.py +0 -0
  296. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/suspend_only_idle_policy_mode.py +0 -0
  297. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/token_analysis_detail.py +0 -0
  298. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/token_analysis_detail_response.py +0 -0
  299. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/token_analysis_finding.py +0 -0
  300. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/token_analysis_finding_confidence.py +0 -0
  301. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/token_analysis_finding_kind.py +0 -0
  302. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/token_analysis_finding_severity.py +0 -0
  303. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/token_analysis_issue.py +0 -0
  304. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/token_analysis_issue_confidence.py +0 -0
  305. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/token_analysis_issue_kind.py +0 -0
  306. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/token_analysis_issue_severity.py +0 -0
  307. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/token_analysis_list.py +0 -0
  308. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/token_analysis_list_response.py +0 -0
  309. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/token_analysis_summary.py +0 -0
  310. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/token_analysis_tool_call.py +0 -0
  311. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/token_saving_event.py +0 -0
  312. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/token_saving_method.py +0 -0
  313. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/token_saving_method_method.py +0 -0
  314. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/token_savings_list.py +0 -0
  315. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/token_savings_list_response.py +0 -0
  316. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/token_savings_summary.py +0 -0
  317. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/update_cloud_agent_request.py +0 -0
  318. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/update_managed_model_provider_request.py +0 -0
  319. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/user_profile.py +0 -0
  320. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/user_profile_response.py +0 -0
  321. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/vnc_credential.py +0 -0
  322. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/vnc_credential_connection.py +0 -0
  323. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/vnc_credential_connection_security_type.py +0 -0
  324. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/vnc_credential_response.py +0 -0
  325. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/vnc_gateway_connection.py +0 -0
  326. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/vnc_gateway_connection_security_type.py +0 -0
  327. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/models/vnc_gateway_connection_username.py +0 -0
  328. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/openapi/types.py +0 -0
  329. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/pagination.py +0 -0
  330. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/rest_tls.py +0 -0
  331. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/retry.py +0 -0
  332. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/tls_resumption.py +0 -0
  333. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/transport.py +0 -0
  334. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/_internal/user_agent.py +0 -0
  335. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/src/runta/py.typed +0 -0
  336. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/tests/test_api_reference_generation.py +0 -0
  337. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/tests/test_async_file_io.py +0 -0
  338. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/tests/test_async_http_transport.py +0 -0
  339. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/tests/test_checkpoint_metadata.py +0 -0
  340. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/tests/test_config_and_helpers.py +0 -0
  341. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/tests/test_connection_pool.py +0 -0
  342. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/tests/test_delete_runtime_retry.py +0 -0
  343. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/tests/test_directory_upload.py +0 -0
  344. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/tests/test_directory_upload_concurrency.py +0 -0
  345. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/tests/test_execution.py +0 -0
  346. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/tests/test_execution_result_stream.py +0 -0
  347. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/tests/test_fetch_openapi.py +0 -0
  348. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/tests/test_get_runtime_retry.py +0 -0
  349. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/tests/test_harbor_e2e.py +0 -0
  350. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/tests/test_harbor_integration.py +0 -0
  351. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/tests/test_harbor_runta_environment.py +0 -0
  352. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/tests/test_live.py +0 -0
  353. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/tests/test_openai_extra_packaging.py +0 -0
  354. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/tests/test_openapi_contract.py +0 -0
  355. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/tests/test_openapi_transport.py +0 -0
  356. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/tests/test_read_retries.py +0 -0
  357. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/tests/test_rest_deployment.py +0 -0
  358. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/tests/test_rest_tls.py +0 -0
  359. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/tests/test_retry_policy.py +0 -0
  360. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/tests/test_runtime_readiness.py +0 -0
  361. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/tests/test_streaming_transfer.py +0 -0
  362. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/tests/test_user_agent.py +0 -0
  363. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/tests/test_websocket_tls.py +0 -0
  364. {runta_sdk-0.2.2 → runta_sdk-0.2.3}/tests/test_websocket_tls_resumption.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: runta-sdk
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Summary: Python SDK for Runta runtime management
5
5
  Author: Runta
6
6
  License-Expression: MIT
@@ -134,6 +134,35 @@ runtime.files.upload("./project", "/workspace/project")
134
134
  runtime.files.download("/workspace/dist", "./artifacts/")
135
135
  ```
136
136
 
137
+ To merge a directory's **contents** into an existing local directory, use:
138
+
139
+ ```python
140
+ runtime.files.download_dir_contents("/workspace/dist", "./artifacts")
141
+ # AsyncRuntime exposes the same method:
142
+ await runtime.files.download_dir_contents("/workspace/dist", "./artifacts")
143
+ ```
144
+
145
+ This creates the destination if needed, overwrites matching files, and preserves
146
+ unrelated files without appending `dist`. The source must be a directory (a
147
+ source directory symlink is followed). File/directory conflicts raise an error;
148
+ existing directories are never recursively removed. Empty sources preserve the
149
+ existing destination and its root metadata. The merge is **not transactional**:
150
+ failure or cancellation may leave partially merged files. Existing `download()`
151
+ path and replacement behavior is unchanged.
152
+
153
+ The new method reads the downloaded seekable archive directly, without a second
154
+ archive or a complete staging copy. It always applies safe tar data filtering,
155
+ including to members returned by an optional synchronous
156
+ `filter(member, destination)` callback. Return `None` to skip a member, for
157
+ example to ignore FIFOs and device nodes; by default these are rejected.
158
+ Permissions follow `tarfile.data_filter` (including removal of unsafe mode bits
159
+ and ownership). Safe links are preserved where supported; the standard library
160
+ copies archive referents when link creation is unavailable. Unresolvable dangling
161
+ symlinks are skipped on platforms that cannot create them. Use a current Python
162
+ security patch release with safe tar link fallback; missing safety support raises
163
+ an error rather than extracting unsafely. As with standard tar extraction, do not
164
+ allow another process to modify the destination concurrently during extraction.
165
+
137
166
  Runtime, Checkpoint, and Secret queries, file reads, Runtime creation and deletion,
138
167
  resource and egress updates, and streaming transfers retry transient failures
139
168
  with exponential backoff and full jitter. The default is 16 total attempts,
@@ -106,6 +106,35 @@ runtime.files.upload("./project", "/workspace/project")
106
106
  runtime.files.download("/workspace/dist", "./artifacts/")
107
107
  ```
108
108
 
109
+ To merge a directory's **contents** into an existing local directory, use:
110
+
111
+ ```python
112
+ runtime.files.download_dir_contents("/workspace/dist", "./artifacts")
113
+ # AsyncRuntime exposes the same method:
114
+ await runtime.files.download_dir_contents("/workspace/dist", "./artifacts")
115
+ ```
116
+
117
+ This creates the destination if needed, overwrites matching files, and preserves
118
+ unrelated files without appending `dist`. The source must be a directory (a
119
+ source directory symlink is followed). File/directory conflicts raise an error;
120
+ existing directories are never recursively removed. Empty sources preserve the
121
+ existing destination and its root metadata. The merge is **not transactional**:
122
+ failure or cancellation may leave partially merged files. Existing `download()`
123
+ path and replacement behavior is unchanged.
124
+
125
+ The new method reads the downloaded seekable archive directly, without a second
126
+ archive or a complete staging copy. It always applies safe tar data filtering,
127
+ including to members returned by an optional synchronous
128
+ `filter(member, destination)` callback. Return `None` to skip a member, for
129
+ example to ignore FIFOs and device nodes; by default these are rejected.
130
+ Permissions follow `tarfile.data_filter` (including removal of unsafe mode bits
131
+ and ownership). Safe links are preserved where supported; the standard library
132
+ copies archive referents when link creation is unavailable. Unresolvable dangling
133
+ symlinks are skipped on platforms that cannot create them. Use a current Python
134
+ security patch release with safe tar link fallback; missing safety support raises
135
+ an error rather than extracting unsafely. As with standard tar extraction, do not
136
+ allow another process to modify the destination concurrently during extraction.
137
+
109
138
  Runtime, Checkpoint, and Secret queries, file reads, Runtime creation and deletion,
110
139
  resource and egress updates, and streaming transfers retry transient failures
111
140
  with exponential backoff and full jitter. The default is 16 total attempts,
@@ -6,7 +6,6 @@ import json
6
6
  import os
7
7
  import shlex
8
8
  import tarfile
9
- import tempfile
10
9
  import time
11
10
  import uuid
12
11
  from abc import ABC, abstractmethod
@@ -51,6 +50,15 @@ _RUNTA_CONTAINER_CA_ENV = {
51
50
  }
52
51
 
53
52
 
53
+ def transfer_tar_filter(
54
+ member: tarfile.TarInfo, destination: str
55
+ ) -> tarfile.TarInfo | None:
56
+ """Skip empty special nodes; supported Harbor releases may not export this."""
57
+ if member.isdev():
58
+ return None
59
+ return tarfile.data_filter(member, destination)
60
+
61
+
54
62
  def _with_runta_container_ca_env(
55
63
  env: dict[str, str] | None,
56
64
  ) -> dict[str, str]:
@@ -936,27 +944,11 @@ class RuntaEnvironment(BaseEnvironment):
936
944
  async def _runtime_download_dir(
937
945
  self, source_dir: str, target_dir: Path | str
938
946
  ) -> None:
939
- # Resolve only the source directory: tar otherwise archives a top-level
940
- # symlink rather than its contents. Keep nested-link extraction checks.
941
- result = await self._runtime_exec_raw(
942
- f"CDPATH= cd -P -- {shlex.quote(source_dir)} && pwd -P", timeout_sec=30
947
+ await self._require_runtime().files.download_dir_contents(
948
+ source_dir,
949
+ str(target_dir),
950
+ filter=transfer_tar_filter,
943
951
  )
944
- self._check(result, "resolve download source directory")
945
- source_dir = result.stdout.removesuffix("\n")
946
- target = Path(target_dir)
947
- target.mkdir(parents=True, exist_ok=True)
948
- with tempfile.TemporaryDirectory() as tmp_dir:
949
- source = Path(tmp_dir) / "contents"
950
- await self._require_runtime().files.download(source_dir, str(source))
951
- # Harbor downloads contents into an existing directory, whereas the
952
- # SDK downloads the source directory itself to a staged destination.
953
- # Retain Harbor's safe content merge, including existing destination
954
- # symlink checks. This merge archive stays local.
955
- archive_path = Path(tmp_dir) / "merge.tar"
956
- with tarfile.open(archive_path, "w") as archive:
957
- archive.add(source, arcname=".")
958
- with tarfile.open(archive_path, "r") as archive:
959
- archive.extractall(target, filter="data")
960
952
 
961
953
  async def _runtime_ensure_dirs(self, dirs: list[str]) -> None:
962
954
  if not dirs:
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "runta-sdk"
7
- version = "0.2.2"
7
+ version = "0.2.3"
8
8
  description = "Python SDK for Runta runtime management"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: runta-sdk
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Summary: Python SDK for Runta runtime management
5
5
  Author: Runta
6
6
  License-Expression: MIT
@@ -134,6 +134,35 @@ runtime.files.upload("./project", "/workspace/project")
134
134
  runtime.files.download("/workspace/dist", "./artifacts/")
135
135
  ```
136
136
 
137
+ To merge a directory's **contents** into an existing local directory, use:
138
+
139
+ ```python
140
+ runtime.files.download_dir_contents("/workspace/dist", "./artifacts")
141
+ # AsyncRuntime exposes the same method:
142
+ await runtime.files.download_dir_contents("/workspace/dist", "./artifacts")
143
+ ```
144
+
145
+ This creates the destination if needed, overwrites matching files, and preserves
146
+ unrelated files without appending `dist`. The source must be a directory (a
147
+ source directory symlink is followed). File/directory conflicts raise an error;
148
+ existing directories are never recursively removed. Empty sources preserve the
149
+ existing destination and its root metadata. The merge is **not transactional**:
150
+ failure or cancellation may leave partially merged files. Existing `download()`
151
+ path and replacement behavior is unchanged.
152
+
153
+ The new method reads the downloaded seekable archive directly, without a second
154
+ archive or a complete staging copy. It always applies safe tar data filtering,
155
+ including to members returned by an optional synchronous
156
+ `filter(member, destination)` callback. Return `None` to skip a member, for
157
+ example to ignore FIFOs and device nodes; by default these are rejected.
158
+ Permissions follow `tarfile.data_filter` (including removal of unsafe mode bits
159
+ and ownership). Safe links are preserved where supported; the standard library
160
+ copies archive referents when link creation is unavailable. Unresolvable dangling
161
+ symlinks are skipped on platforms that cannot create them. Use a current Python
162
+ security patch release with safe tar link fallback; missing safety support raises
163
+ an error rather than extracting unsafely. As with standard tar extraction, do not
164
+ allow another process to modify the destination concurrently during extraction.
165
+
137
166
  Runtime, Checkpoint, and Secret queries, file reads, Runtime creation and deletion,
138
167
  resource and egress updates, and streaming transfers retry transient failures
139
168
  with exponential backoff and full jitter. The default is 16 total attempts,
@@ -336,6 +336,8 @@ tests/test_connection_pool.py
336
336
  tests/test_delete_runtime_retry.py
337
337
  tests/test_directory_upload.py
338
338
  tests/test_directory_upload_concurrency.py
339
+ tests/test_download_dir_contents.py
340
+ tests/test_download_dir_contents_live.py
339
341
  tests/test_execution.py
340
342
  tests/test_execution_result_stream.py
341
343
  tests/test_fetch_openapi.py
@@ -4,8 +4,9 @@ from __future__ import annotations
4
4
 
5
5
  import asyncio
6
6
  import time
7
+ import tarfile
7
8
  import uuid
8
- from collections.abc import Mapping, Sequence
9
+ from collections.abc import Callable, Mapping, Sequence
9
10
  from concurrent.futures import Future, ThreadPoolExecutor
10
11
  from dataclasses import dataclass, field
11
12
  from enum import Enum
@@ -1588,6 +1589,41 @@ class FileManager(_SdkOwnedHandle):
1588
1589
  destination_is_directory=destination_is_directory,
1589
1590
  )
1590
1591
 
1592
+ def download_dir_contents(
1593
+ self,
1594
+ src: str,
1595
+ dst: str,
1596
+ *,
1597
+ filter: Callable[[tarfile.TarInfo, str], tarfile.TarInfo | None] | None = None,
1598
+ ) -> None:
1599
+ """Merge a runtime directory's contents into a local directory.
1600
+
1601
+ Existing unrelated files are preserved and matching files overwritten.
1602
+ Source directory links are followed; nested links are preserved when
1603
+ supported. Safe data filtering is always enforced, including after a
1604
+ custom tarfile-style filter. Special files are rejected unless skipped
1605
+ by that callback. Permissions follow tarfile.data_filter semantics.
1606
+
1607
+ This operation is not transactional: failures or cancellation can leave
1608
+ partially merged contents. A file/directory type conflict raises instead
1609
+ of deleting a directory. An empty source preserves existing contents.
1610
+
1611
+ Args:
1612
+ src: Source directory inside the runtime.
1613
+ dst: Local directory, created if absent; no source name is appended.
1614
+ filter: Optional synchronous (TarInfo, destination) callback. Return
1615
+ None to skip a member. Async callbacks are not supported.
1616
+
1617
+ Examples:
1618
+ >>> runtime.files.download_dir_contents("/workspace/dist", "./artifacts")
1619
+ """
1620
+ self._runtime._client._http.download_dir_contents(
1621
+ self._runtime.id,
1622
+ src,
1623
+ Path(dst),
1624
+ filter=filter,
1625
+ )
1626
+
1591
1627
 
1592
1628
  class AsyncFileManager(_SdkOwnedHandle):
1593
1629
  """Reads, writes, uploads, and downloads runtime files asynchronously."""
@@ -1662,6 +1698,41 @@ class AsyncFileManager(_SdkOwnedHandle):
1662
1698
  destination_is_directory=destination_is_directory,
1663
1699
  )
1664
1700
 
1701
+ async def download_dir_contents(
1702
+ self,
1703
+ src: str,
1704
+ dst: str,
1705
+ *,
1706
+ filter: Callable[[tarfile.TarInfo, str], tarfile.TarInfo | None] | None = None,
1707
+ ) -> None:
1708
+ """Merge a runtime directory's contents into a local directory.
1709
+
1710
+ Existing unrelated files are preserved and matching files overwritten.
1711
+ Source directory links are followed; nested links are preserved when
1712
+ supported. Safe data filtering is always enforced, including after a
1713
+ custom tarfile-style filter. Special files are rejected unless skipped
1714
+ by that callback. Permissions follow tarfile.data_filter semantics.
1715
+
1716
+ This operation is not transactional: failures or cancellation can leave
1717
+ partially merged contents. A file/directory type conflict raises instead
1718
+ of deleting a directory. An empty source preserves existing contents.
1719
+
1720
+ Args:
1721
+ src: Source directory inside the runtime.
1722
+ dst: Local directory, created if absent; no source name is appended.
1723
+ filter: Optional synchronous (TarInfo, destination) callback. Return
1724
+ None to skip a member. Async callbacks are not supported.
1725
+
1726
+ Examples:
1727
+ >>> await runtime.files.download_dir_contents("/workspace/dist", "./artifacts")
1728
+ """
1729
+ await self._runtime._client._http.download_dir_contents(
1730
+ self._runtime.id,
1731
+ src,
1732
+ Path(dst),
1733
+ filter=filter,
1734
+ )
1735
+
1665
1736
 
1666
1737
  class RuntimeCheckpointManager(_SdkOwnedHandle):
1667
1738
  """Creates, lists, and deletes checkpoints for a runtime."""
@@ -5,6 +5,7 @@ from __future__ import annotations
5
5
  from collections.abc import Awaitable, Mapping, Sequence
6
6
  from pathlib import Path
7
7
  import time
8
+ import tarfile
8
9
  from typing import Any, Callable
9
10
  from uuid import uuid4
10
11
 
@@ -517,6 +518,29 @@ class _AsyncHttpClient:
517
518
  _file_io=self._file_io,
518
519
  )
519
520
 
521
+ async def download_dir_contents(
522
+ self,
523
+ runtime_id: str,
524
+ src: str,
525
+ dst: Path,
526
+ *,
527
+ filter: Callable[[tarfile.TarInfo, str], tarfile.TarInfo | None] | None = None,
528
+ ) -> None:
529
+ from .streaming_transfer import async_download_dir_contents
530
+
531
+ await async_download_dir_contents(
532
+ self.endpoint,
533
+ self.token,
534
+ runtime_id,
535
+ src,
536
+ dst,
537
+ filter=filter,
538
+ timeout=self.timeout,
539
+ attempts=self.retry_max_attempts,
540
+ _tls=self._websocket_tls,
541
+ _file_io=self._file_io,
542
+ )
543
+
520
544
  async def update_runtime_status(
521
545
  self, runtime_id: str, action: str
522
546
  ) -> _public.RuntimeInfo:
@@ -63,6 +63,8 @@ class Runtime:
63
63
  destination, bypassing secret injection, LLM capture, token saving, and model routing.
64
64
  vnc_error_code (None | str | Unset): Bounded machine-readable VNC startup failure code.
65
65
  error_code (None | str | Unset): Bounded machine-readable provisioning failure code.
66
+ error_message (None | str | Unset): Current runtime failure reason, bounded to 4096 UTF-8 bytes. Cleared when
67
+ the failure recovers.
66
68
  """
67
69
 
68
70
  display_name: str
@@ -92,6 +94,7 @@ class Runtime:
92
94
  tls_passthrough_hosts: list[str] | Unset = UNSET
93
95
  vnc_error_code: None | str | Unset = UNSET
94
96
  error_code: None | str | Unset = UNSET
97
+ error_message: None | str | Unset = UNSET
95
98
  additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
96
99
 
97
100
  def to_dict(self) -> dict[str, Any]:
@@ -181,6 +184,12 @@ class Runtime:
181
184
  else:
182
185
  error_code = self.error_code
183
186
 
187
+ error_message: None | str | Unset
188
+ if isinstance(self.error_message, Unset):
189
+ error_message = UNSET
190
+ else:
191
+ error_message = self.error_message
192
+
184
193
  field_dict: dict[str, Any] = {}
185
194
  field_dict.update(self.additional_properties)
186
195
  field_dict.update(
@@ -218,6 +227,8 @@ class Runtime:
218
227
  field_dict["vnc_error_code"] = vnc_error_code
219
228
  if error_code is not UNSET:
220
229
  field_dict["error_code"] = error_code
230
+ if error_message is not UNSET:
231
+ field_dict["error_message"] = error_message
221
232
 
222
233
  return field_dict
223
234
 
@@ -379,6 +390,15 @@ class Runtime:
379
390
 
380
391
  error_code = _parse_error_code(d.pop("error_code", UNSET))
381
392
 
393
+ def _parse_error_message(data: object) -> None | str | Unset:
394
+ if data is None:
395
+ return data
396
+ if isinstance(data, Unset):
397
+ return data
398
+ return cast(None | str | Unset, data)
399
+
400
+ error_message = _parse_error_message(d.pop("error_message", UNSET))
401
+
382
402
  runtime = cls(
383
403
  display_name=display_name,
384
404
  egress_policy=egress_policy,
@@ -407,6 +427,7 @@ class Runtime:
407
427
  tls_passthrough_hosts=tls_passthrough_hosts,
408
428
  vnc_error_code=vnc_error_code,
409
429
  error_code=error_code,
430
+ error_message=error_message,
410
431
  )
411
432
 
412
433
  runtime.additional_properties = d
@@ -4,6 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  import asyncio
6
6
  import base64
7
+ import copy
7
8
  import errno
8
9
  from contextlib import asynccontextmanager, contextmanager
9
10
  import json
@@ -17,7 +18,7 @@ import tempfile
17
18
  import threading
18
19
  import time
19
20
  from collections.abc import Callable
20
- from pathlib import Path
21
+ from pathlib import Path, PurePosixPath, PureWindowsPath
21
22
  from typing import Any, BinaryIO
22
23
  from urllib.parse import quote, urlsplit, urlunsplit
23
24
 
@@ -337,6 +338,77 @@ async def async_download_path(
337
338
  pool.close()
338
339
 
339
340
 
341
+ def download_dir_contents(
342
+ endpoint: str,
343
+ token: str,
344
+ runtime_id: str,
345
+ src: str,
346
+ dst: Path,
347
+ *,
348
+ filter: Callable[[tarfile.TarInfo, str], tarfile.TarInfo | None] | None = None,
349
+ timeout: float,
350
+ attempts: int = _TRANSFER_ATTEMPTS,
351
+ _tls: _WebSocketTLS | None = None,
352
+ ) -> None:
353
+ """Download one seekable archive and merge its directory contents."""
354
+ with tempfile.TemporaryDirectory(prefix="runta-download-") as temp:
355
+ archive = Path(temp) / "payload.tar"
356
+ _retry_sync(
357
+ lambda: _sync_download_archive(
358
+ endpoint,
359
+ token,
360
+ runtime_id,
361
+ src,
362
+ archive,
363
+ timeout,
364
+ _tls=_tls,
365
+ directory_contents=True,
366
+ ),
367
+ attempts=attempts,
368
+ )
369
+ _extract_dir_contents(archive, dst, filter)
370
+
371
+
372
+ async def async_download_dir_contents(
373
+ endpoint: str,
374
+ token: str,
375
+ runtime_id: str,
376
+ src: str,
377
+ dst: Path,
378
+ *,
379
+ filter: Callable[[tarfile.TarInfo, str], tarfile.TarInfo | None] | None = None,
380
+ timeout: float,
381
+ attempts: int = _TRANSFER_ATTEMPTS,
382
+ _tls: _WebSocketTLS | _AsyncWebSocketTLS | None = None,
383
+ _file_io: _AsyncFileIO | None = None,
384
+ ) -> None:
385
+ """Merge on the client's bounded, cancellation-safe file workers."""
386
+ owned_pool = _file_io is None
387
+ pool = _file_io or _AsyncFileIO(None, asyncio.get_running_loop)
388
+ try:
389
+ with pool.lease():
390
+ async with _async_download_workspace(pool) as temp:
391
+ archive = temp / "payload.tar"
392
+ await _retry_async(
393
+ lambda: _async_download_archive(
394
+ endpoint,
395
+ token,
396
+ runtime_id,
397
+ src,
398
+ archive,
399
+ timeout,
400
+ _tls=_tls,
401
+ _file_io=pool,
402
+ directory_contents=True,
403
+ ),
404
+ attempts=attempts,
405
+ )
406
+ await pool.run(_extract_dir_contents, archive, dst, filter)
407
+ finally:
408
+ if owned_pool:
409
+ pool.close()
410
+
411
+
340
412
  def execute(
341
413
  endpoint: str,
342
414
  token: str,
@@ -694,10 +766,11 @@ def _sync_download_archive(
694
766
  timeout: float,
695
767
  *,
696
768
  _tls: _WebSocketTLS | None = None,
769
+ directory_contents: bool = False,
697
770
  ) -> None:
698
- parent, name = _remote_parent_and_name(src)
771
+ command, args = _download_archive_command(src, directory_contents)
699
772
  with _sync_socket(endpoint, token, runtime_id, timeout, _tls=_tls) as socket:
700
- _send_start(socket.send, "tar", ["cf", "-", "-C", parent, name])
773
+ _send_start(socket.send, command, args)
701
774
  _send_close_stdin(socket.send)
702
775
  with archive.open("wb") as handle:
703
776
  _receive_completion(socket.recv, stdout=handle)
@@ -713,10 +786,11 @@ async def _async_download_archive(
713
786
  *,
714
787
  _tls: _WebSocketTLS | _AsyncWebSocketTLS | None = None,
715
788
  _file_io: _AsyncFileIO,
789
+ directory_contents: bool = False,
716
790
  ) -> None:
717
- parent, name = _remote_parent_and_name(src)
791
+ command, args = _download_archive_command(src, directory_contents)
718
792
  async with _async_socket(endpoint, token, runtime_id, timeout, _tls=_tls) as socket:
719
- await _async_send_start(socket.send, "tar", ["cf", "-", "-C", parent, name])
793
+ await _async_send_start(socket.send, command, args)
720
794
  await _async_send_close_stdin(socket.send)
721
795
  writer = _AsyncDownloadWriter(_file_io, archive)
722
796
  try:
@@ -1041,6 +1115,155 @@ async def _retry_async(
1041
1115
  )
1042
1116
 
1043
1117
 
1118
+ def _download_archive_command(
1119
+ src: str, directory_contents: bool
1120
+ ) -> tuple[str, list[str]]:
1121
+ if directory_contents:
1122
+ # cd follows only the source directory link; tar preserves nested links.
1123
+ # Positional arguments preserve arbitrary names without shell interpolation.
1124
+ return "sh", [
1125
+ "-c",
1126
+ 'set -eu; CDPATH= cd -P -- "$1"; exec tar cf - .',
1127
+ "_",
1128
+ src,
1129
+ ]
1130
+ parent, name = _remote_parent_and_name(src)
1131
+ return "tar", ["cf", "-", "-C", parent, name]
1132
+
1133
+
1134
+ def _safe_relative_name(name: str) -> None:
1135
+ # Archive names are POSIX paths even on Windows. Reject Windows aliases too,
1136
+ # so drive paths, separators and alternate data streams cannot bypass checks.
1137
+ if (
1138
+ not name
1139
+ or "\\" in name
1140
+ or ":" in name
1141
+ or "\x00" in name
1142
+ or PurePosixPath(name).is_absolute()
1143
+ or PureWindowsPath(name).drive
1144
+ or ".." in PurePosixPath(name).parts
1145
+ ):
1146
+ raise tarfile.FilterError("download archive contains an unsafe path")
1147
+
1148
+
1149
+ class _DirectoryMergeTarFile(tarfile.TarFile):
1150
+ """Replace file entries without writing through existing destination aliases."""
1151
+
1152
+ def makefile(self, tarinfo: tarfile.TarInfo, targetpath: str) -> None:
1153
+ # Do this at the actual write path, after filtering. A filter can also
1154
+ # run for a link fallback's referent, whose path must remain untouched.
1155
+ target = Path(targetpath)
1156
+ if target.is_dir():
1157
+ raise FileExistsError(
1158
+ f"download member conflicts with destination type: {target}"
1159
+ )
1160
+ target.unlink(missing_ok=True)
1161
+ super().makefile(tarinfo, targetpath)
1162
+
1163
+
1164
+ def _extract_dir_contents(
1165
+ archive_path: Path,
1166
+ dst: Path,
1167
+ filter: Callable[[tarfile.TarInfo, str], tarfile.TarInfo | None] | None = None,
1168
+ ) -> None:
1169
+ data_filter = getattr(tarfile, "data_filter", None)
1170
+ if data_filter is None or not hasattr(tarfile.TarFile, "makelink_with_filter"):
1171
+ raise RuntimeError(
1172
+ "directory content downloads require a security-patched Python "
1173
+ "with tarfile.data_filter and filtered link fallback"
1174
+ )
1175
+ if dst.is_symlink():
1176
+ raise tarfile.FilterError("download destination must not be a symbolic link")
1177
+ with _DirectoryMergeTarFile.open(archive_path, "r:", errorlevel=1) as archive:
1178
+ members = archive.getmembers()
1179
+ if not members or members[0].name not in {".", "./"} or not members[0].isdir():
1180
+ raise _public.ApiError(
1181
+ "download archive did not contain a source directory"
1182
+ )
1183
+ # Rewrite the seekable archive's index as well as extraction members.
1184
+ # tarfile uses this index (and the original data offsets) for link fallback.
1185
+ for member in members:
1186
+ _safe_relative_name(member.name)
1187
+ if member.name in {".", "./"}:
1188
+ if not member.isdir():
1189
+ raise tarfile.FilterError("invalid source directory member")
1190
+ member.name = "."
1191
+ elif member.name.startswith("./"):
1192
+ member.name = member.name[2:]
1193
+ _safe_relative_name(member.name)
1194
+ else:
1195
+ raise tarfile.FilterError(
1196
+ "download archive member is outside the source directory"
1197
+ )
1198
+ if member.islnk():
1199
+ _safe_relative_name(member.linkname)
1200
+ if not member.linkname.startswith("./"):
1201
+ raise tarfile.FilterError(
1202
+ "hard link is outside the source directory"
1203
+ )
1204
+ member.linkname = member.linkname[2:]
1205
+ _safe_relative_name(member.linkname)
1206
+
1207
+ dst.mkdir(parents=True, exist_ok=True)
1208
+
1209
+ def safe_filter(
1210
+ member: tarfile.TarInfo, destination: str
1211
+ ) -> tarfile.TarInfo | None:
1212
+ if member.name == ".":
1213
+ return None # Do not apply the source root's metadata to dst.
1214
+ _safe_relative_name(member.name)
1215
+ # Check boundaries before invoking user code, but allow the callback
1216
+ # to skip special files before data_filter rejects those file types.
1217
+ if member.issym() or member.islnk():
1218
+ link = member.linkname
1219
+ if (
1220
+ PurePosixPath(link).is_absolute()
1221
+ or PureWindowsPath(link).drive
1222
+ or "\\" in link
1223
+ or ":" in link
1224
+ or "\x00" in link
1225
+ ):
1226
+ raise tarfile.FilterError(
1227
+ "download archive contains an unsafe link"
1228
+ )
1229
+ probe = copy.copy(member)
1230
+ if probe.isdev():
1231
+ probe.type = tarfile.REGTYPE
1232
+ data_filter(probe, destination)
1233
+ if filter is not None:
1234
+ member = filter(copy.copy(member), destination)
1235
+ if member is None:
1236
+ return None
1237
+ _safe_relative_name(member.name)
1238
+ if PurePosixPath(member.name) == PurePosixPath("."):
1239
+ raise tarfile.FilterError(
1240
+ "a filter cannot replace the destination root"
1241
+ )
1242
+ if member.issym() or member.islnk():
1243
+ link = member.linkname
1244
+ if (
1245
+ PurePosixPath(link).is_absolute()
1246
+ or PureWindowsPath(link).drive
1247
+ or "\\" in link
1248
+ or ":" in link
1249
+ or "\x00" in link
1250
+ ):
1251
+ raise tarfile.FilterError(
1252
+ "download archive contains an unsafe link"
1253
+ )
1254
+ member = data_filter(member, destination)
1255
+ target = Path(destination) / member.name
1256
+ if target.exists() and target.is_dir() != member.isdir():
1257
+ raise FileExistsError(
1258
+ f"download member conflicts with destination type: {target}"
1259
+ )
1260
+ return member
1261
+
1262
+ # errorlevel=1 (the default) propagates security/I/O failures while
1263
+ # allowing stdlib's nonfatal unresolved-link fallback to skip a link.
1264
+ archive.extractall(dst, filter=safe_filter)
1265
+
1266
+
1044
1267
  def _remote_parent_and_name(src: str) -> tuple[str, str]:
1045
1268
  normalized = src.rstrip("/") or "/"
1046
1269
  path = Path(normalized)