From 6f76769e0c3129a55430ec7fa6de74c444c58f55 Mon Sep 17 00:00:00 2001 From: API Engineering Date: Fri, 16 Oct 2026 07:38:39 +0000 Subject: [PATCH] [bot] Updated client based on openapi-380d7ee/clientgen --- DO_OPENAPI_COMMIT_SHA.txt | 2 +- src/pydo/_client.py | 48 + src/pydo/_vendor.py | 38 + src/pydo/aio/_client.py | 48 + src/pydo/aio/_vendor.py | 38 + src/pydo/aio/operations/__init__.py | 16 + src/pydo/aio/operations/_operations.py | 121048 ++++++++++++--------- src/pydo/operations/__init__.py | 16 + src/pydo/operations/_operations.py | 123963 +++++++++++++--------- 9 files changed, 150372 insertions(+), 94845 deletions(-) diff --git a/DO_OPENAPI_COMMIT_SHA.txt b/DO_OPENAPI_COMMIT_SHA.txt index 67d44682..5c897da7 100644 --- a/DO_OPENAPI_COMMIT_SHA.txt +++ b/DO_OPENAPI_COMMIT_SHA.txt @@ -1 +1 @@ -97dca99 +380d7ee diff --git a/src/pydo/_client.py b/src/pydo/_client.py index c59a0d6d..8962bc5e 100644 --- a/src/pydo/_client.py +++ b/src/pydo/_client.py @@ -36,7 +36,12 @@ FirewallsOperations, FunctionsAccessKeyOperations, FunctionsOperations, + GatewayApprovalsOperations, + GatewayCodeOperations, + GatewaySessionApprovalsOperations, + GatewayToolsOperations, GenaiOperations, + HarnessOperations, ImageActionsOperations, ImagesOperations, InferenceOperations, @@ -45,6 +50,8 @@ KubernetesOperations, LoadBalancersOperations, McpServersOperations, + MicrovmCheckpointsOperations, + MicrovmsOperations, MonitoringOperations, NfsOperations, OneClicksOperations, @@ -63,6 +70,7 @@ ReservedIPv6Operations, SecurityOperations, SessionsOperations, + SignalsOperations, SizesOperations, SnapshotsOperations, SpacesKeyOperations, @@ -108,6 +116,14 @@ class GeneratedClient: # pylint: disable=client-accepts-api-version-keyword,too :vartype tools: pydo.operations.ToolsOperations :ivar users: UsersOperations operations :vartype users: pydo.operations.UsersOperations + :ivar gateway_tools: GatewayToolsOperations operations + :vartype gateway_tools: pydo.operations.GatewayToolsOperations + :ivar gateway_code: GatewayCodeOperations operations + :vartype gateway_code: pydo.operations.GatewayCodeOperations + :ivar gateway_session_approvals: GatewaySessionApprovalsOperations operations + :vartype gateway_session_approvals: pydo.operations.GatewaySessionApprovalsOperations + :ivar gateway_approvals: GatewayApprovalsOperations operations + :vartype gateway_approvals: pydo.operations.GatewayApprovalsOperations :ivar one_clicks: OneClicksOperations operations :vartype one_clicks: pydo.operations.OneClicksOperations :ivar account: AccountOperations operations @@ -118,6 +134,8 @@ class GeneratedClient: # pylint: disable=client-accepts-api-version-keyword,too :vartype actions: pydo.operations.ActionsOperations :ivar addons: AddonsOperations operations :vartype addons: pydo.operations.AddonsOperations + :ivar harness: HarnessOperations operations + :vartype harness: pydo.operations.HarnessOperations :ivar apps: AppsOperations operations :vartype apps: pydo.operations.AppsOperations :ivar cdn: CdnOperations operations @@ -160,10 +178,16 @@ class GeneratedClient: # pylint: disable=client-accepts-api-version-keyword,too :vartype image_actions: pydo.operations.ImageActionsOperations :ivar insights: InsightsOperations operations :vartype insights: pydo.operations.InsightsOperations + :ivar signals: SignalsOperations operations + :vartype signals: pydo.operations.SignalsOperations :ivar kubernetes: KubernetesOperations operations :vartype kubernetes: pydo.operations.KubernetesOperations :ivar load_balancers: LoadBalancersOperations operations :vartype load_balancers: pydo.operations.LoadBalancersOperations + :ivar microvms: MicrovmsOperations operations + :vartype microvms: pydo.operations.MicrovmsOperations + :ivar microvm_checkpoints: MicrovmCheckpointsOperations operations + :vartype microvm_checkpoints: pydo.operations.MicrovmCheckpointsOperations :ivar monitoring: MonitoringOperations operations :vartype monitoring: pydo.operations.MonitoringOperations :ivar nfs: NfsOperations operations @@ -294,6 +318,18 @@ def __init__( self.users = UsersOperations( self._client, self._config, self._serialize, self._deserialize ) + self.gateway_tools = GatewayToolsOperations( + self._client, self._config, self._serialize, self._deserialize + ) + self.gateway_code = GatewayCodeOperations( + self._client, self._config, self._serialize, self._deserialize + ) + self.gateway_session_approvals = GatewaySessionApprovalsOperations( + self._client, self._config, self._serialize, self._deserialize + ) + self.gateway_approvals = GatewayApprovalsOperations( + self._client, self._config, self._serialize, self._deserialize + ) self.one_clicks = OneClicksOperations( self._client, self._config, self._serialize, self._deserialize ) @@ -309,6 +345,9 @@ def __init__( self.addons = AddonsOperations( self._client, self._config, self._serialize, self._deserialize ) + self.harness = HarnessOperations( + self._client, self._config, self._serialize, self._deserialize + ) self.apps = AppsOperations( self._client, self._config, self._serialize, self._deserialize ) @@ -372,12 +411,21 @@ def __init__( self.insights = InsightsOperations( self._client, self._config, self._serialize, self._deserialize ) + self.signals = SignalsOperations( + self._client, self._config, self._serialize, self._deserialize + ) self.kubernetes = KubernetesOperations( self._client, self._config, self._serialize, self._deserialize ) self.load_balancers = LoadBalancersOperations( self._client, self._config, self._serialize, self._deserialize ) + self.microvms = MicrovmsOperations( + self._client, self._config, self._serialize, self._deserialize + ) + self.microvm_checkpoints = MicrovmCheckpointsOperations( + self._client, self._config, self._serialize, self._deserialize + ) self.monitoring = MonitoringOperations( self._client, self._config, self._serialize, self._deserialize ) diff --git a/src/pydo/_vendor.py b/src/pydo/_vendor.py index 422d511d..94e5f091 100644 --- a/src/pydo/_vendor.py +++ b/src/pydo/_vendor.py @@ -3,6 +3,10 @@ # Changes may cause incorrect behavior and will be lost if the code is regenerated. # -------------------------------------------------------------------------- +from typing import Optional + +from azure.core import MatchConditions + def raise_if_not_implemented(cls, abstract_methods): not_implemented = [ @@ -15,3 +19,37 @@ def raise_if_not_implemented(cls, abstract_methods): cls.__name__, "', '".join(not_implemented) ) ) + + +def quote_etag(etag: Optional[str]) -> Optional[str]: + if not etag or etag == "*": + return etag + if etag.startswith("W/"): + return etag + if etag.startswith('"') and etag.endswith('"'): + return etag + if etag.startswith("'") and etag.endswith("'"): + return etag + return '"' + etag + '"' + + +def prep_if_match( + etag: Optional[str], match_condition: Optional[MatchConditions] +) -> Optional[str]: + if match_condition == MatchConditions.IfNotModified: + if_match = quote_etag(etag) if etag else None + return if_match + if match_condition == MatchConditions.IfPresent: + return "*" + return None + + +def prep_if_none_match( + etag: Optional[str], match_condition: Optional[MatchConditions] +) -> Optional[str]: + if match_condition == MatchConditions.IfModified: + if_none_match = quote_etag(etag) if etag else None + return if_none_match + if match_condition == MatchConditions.IfMissing: + return "*" + return None diff --git a/src/pydo/aio/_client.py b/src/pydo/aio/_client.py index cb653108..5bceb6d5 100644 --- a/src/pydo/aio/_client.py +++ b/src/pydo/aio/_client.py @@ -36,7 +36,12 @@ FirewallsOperations, FunctionsAccessKeyOperations, FunctionsOperations, + GatewayApprovalsOperations, + GatewayCodeOperations, + GatewaySessionApprovalsOperations, + GatewayToolsOperations, GenaiOperations, + HarnessOperations, ImageActionsOperations, ImagesOperations, InferenceOperations, @@ -45,6 +50,8 @@ KubernetesOperations, LoadBalancersOperations, McpServersOperations, + MicrovmCheckpointsOperations, + MicrovmsOperations, MonitoringOperations, NfsOperations, OneClicksOperations, @@ -63,6 +70,7 @@ ReservedIPv6Operations, SecurityOperations, SessionsOperations, + SignalsOperations, SizesOperations, SnapshotsOperations, SpacesKeyOperations, @@ -108,6 +116,14 @@ class GeneratedClient: # pylint: disable=client-accepts-api-version-keyword,too :vartype tools: pydo.aio.operations.ToolsOperations :ivar users: UsersOperations operations :vartype users: pydo.aio.operations.UsersOperations + :ivar gateway_tools: GatewayToolsOperations operations + :vartype gateway_tools: pydo.aio.operations.GatewayToolsOperations + :ivar gateway_code: GatewayCodeOperations operations + :vartype gateway_code: pydo.aio.operations.GatewayCodeOperations + :ivar gateway_session_approvals: GatewaySessionApprovalsOperations operations + :vartype gateway_session_approvals: pydo.aio.operations.GatewaySessionApprovalsOperations + :ivar gateway_approvals: GatewayApprovalsOperations operations + :vartype gateway_approvals: pydo.aio.operations.GatewayApprovalsOperations :ivar one_clicks: OneClicksOperations operations :vartype one_clicks: pydo.aio.operations.OneClicksOperations :ivar account: AccountOperations operations @@ -118,6 +134,8 @@ class GeneratedClient: # pylint: disable=client-accepts-api-version-keyword,too :vartype actions: pydo.aio.operations.ActionsOperations :ivar addons: AddonsOperations operations :vartype addons: pydo.aio.operations.AddonsOperations + :ivar harness: HarnessOperations operations + :vartype harness: pydo.aio.operations.HarnessOperations :ivar apps: AppsOperations operations :vartype apps: pydo.aio.operations.AppsOperations :ivar cdn: CdnOperations operations @@ -160,10 +178,16 @@ class GeneratedClient: # pylint: disable=client-accepts-api-version-keyword,too :vartype image_actions: pydo.aio.operations.ImageActionsOperations :ivar insights: InsightsOperations operations :vartype insights: pydo.aio.operations.InsightsOperations + :ivar signals: SignalsOperations operations + :vartype signals: pydo.aio.operations.SignalsOperations :ivar kubernetes: KubernetesOperations operations :vartype kubernetes: pydo.aio.operations.KubernetesOperations :ivar load_balancers: LoadBalancersOperations operations :vartype load_balancers: pydo.aio.operations.LoadBalancersOperations + :ivar microvms: MicrovmsOperations operations + :vartype microvms: pydo.aio.operations.MicrovmsOperations + :ivar microvm_checkpoints: MicrovmCheckpointsOperations operations + :vartype microvm_checkpoints: pydo.aio.operations.MicrovmCheckpointsOperations :ivar monitoring: MonitoringOperations operations :vartype monitoring: pydo.aio.operations.MonitoringOperations :ivar nfs: NfsOperations operations @@ -294,6 +318,18 @@ def __init__( self.users = UsersOperations( self._client, self._config, self._serialize, self._deserialize ) + self.gateway_tools = GatewayToolsOperations( + self._client, self._config, self._serialize, self._deserialize + ) + self.gateway_code = GatewayCodeOperations( + self._client, self._config, self._serialize, self._deserialize + ) + self.gateway_session_approvals = GatewaySessionApprovalsOperations( + self._client, self._config, self._serialize, self._deserialize + ) + self.gateway_approvals = GatewayApprovalsOperations( + self._client, self._config, self._serialize, self._deserialize + ) self.one_clicks = OneClicksOperations( self._client, self._config, self._serialize, self._deserialize ) @@ -309,6 +345,9 @@ def __init__( self.addons = AddonsOperations( self._client, self._config, self._serialize, self._deserialize ) + self.harness = HarnessOperations( + self._client, self._config, self._serialize, self._deserialize + ) self.apps = AppsOperations( self._client, self._config, self._serialize, self._deserialize ) @@ -372,12 +411,21 @@ def __init__( self.insights = InsightsOperations( self._client, self._config, self._serialize, self._deserialize ) + self.signals = SignalsOperations( + self._client, self._config, self._serialize, self._deserialize + ) self.kubernetes = KubernetesOperations( self._client, self._config, self._serialize, self._deserialize ) self.load_balancers = LoadBalancersOperations( self._client, self._config, self._serialize, self._deserialize ) + self.microvms = MicrovmsOperations( + self._client, self._config, self._serialize, self._deserialize + ) + self.microvm_checkpoints = MicrovmCheckpointsOperations( + self._client, self._config, self._serialize, self._deserialize + ) self.monitoring = MonitoringOperations( self._client, self._config, self._serialize, self._deserialize ) diff --git a/src/pydo/aio/_vendor.py b/src/pydo/aio/_vendor.py index 422d511d..94e5f091 100644 --- a/src/pydo/aio/_vendor.py +++ b/src/pydo/aio/_vendor.py @@ -3,6 +3,10 @@ # Changes may cause incorrect behavior and will be lost if the code is regenerated. # -------------------------------------------------------------------------- +from typing import Optional + +from azure.core import MatchConditions + def raise_if_not_implemented(cls, abstract_methods): not_implemented = [ @@ -15,3 +19,37 @@ def raise_if_not_implemented(cls, abstract_methods): cls.__name__, "', '".join(not_implemented) ) ) + + +def quote_etag(etag: Optional[str]) -> Optional[str]: + if not etag or etag == "*": + return etag + if etag.startswith("W/"): + return etag + if etag.startswith('"') and etag.endswith('"'): + return etag + if etag.startswith("'") and etag.endswith("'"): + return etag + return '"' + etag + '"' + + +def prep_if_match( + etag: Optional[str], match_condition: Optional[MatchConditions] +) -> Optional[str]: + if match_condition == MatchConditions.IfNotModified: + if_match = quote_etag(etag) if etag else None + return if_match + if match_condition == MatchConditions.IfPresent: + return "*" + return None + + +def prep_if_none_match( + etag: Optional[str], match_condition: Optional[MatchConditions] +) -> Optional[str]: + if match_condition == MatchConditions.IfModified: + if_none_match = quote_etag(etag) if etag else None + return if_none_match + if match_condition == MatchConditions.IfMissing: + return "*" + return None diff --git a/src/pydo/aio/operations/__init__.py b/src/pydo/aio/operations/__init__.py index 163af5e7..95dd025c 100644 --- a/src/pydo/aio/operations/__init__.py +++ b/src/pydo/aio/operations/__init__.py @@ -12,11 +12,16 @@ from ._operations import ToolbeltsOperations from ._operations import ToolsOperations from ._operations import UsersOperations +from ._operations import GatewayToolsOperations +from ._operations import GatewayCodeOperations +from ._operations import GatewaySessionApprovalsOperations +from ._operations import GatewayApprovalsOperations from ._operations import OneClicksOperations from ._operations import AccountOperations from ._operations import SshKeysOperations from ._operations import ActionsOperations from ._operations import AddonsOperations +from ._operations import HarnessOperations from ._operations import AppsOperations from ._operations import CdnOperations from ._operations import CertificatesOperations @@ -38,8 +43,11 @@ from ._operations import ImagesOperations from ._operations import ImageActionsOperations from ._operations import InsightsOperations +from ._operations import SignalsOperations from ._operations import KubernetesOperations from ._operations import LoadBalancersOperations +from ._operations import MicrovmsOperations +from ._operations import MicrovmCheckpointsOperations from ._operations import MonitoringOperations from ._operations import NfsOperations from ._operations import OrganizationsOperations @@ -86,11 +94,16 @@ "ToolbeltsOperations", "ToolsOperations", "UsersOperations", + "GatewayToolsOperations", + "GatewayCodeOperations", + "GatewaySessionApprovalsOperations", + "GatewayApprovalsOperations", "OneClicksOperations", "AccountOperations", "SshKeysOperations", "ActionsOperations", "AddonsOperations", + "HarnessOperations", "AppsOperations", "CdnOperations", "CertificatesOperations", @@ -112,8 +125,11 @@ "ImagesOperations", "ImageActionsOperations", "InsightsOperations", + "SignalsOperations", "KubernetesOperations", "LoadBalancersOperations", + "MicrovmsOperations", + "MicrovmCheckpointsOperations", "MonitoringOperations", "NfsOperations", "OrganizationsOperations", diff --git a/src/pydo/aio/operations/_operations.py b/src/pydo/aio/operations/_operations.py index 3273a2ff..6fbc0da9 100644 --- a/src/pydo/aio/operations/_operations.py +++ b/src/pydo/aio/operations/_operations.py @@ -22,10 +22,12 @@ overload, ) +from azure.core import MatchConditions from azure.core.exceptions import ( ClientAuthenticationError, HttpResponseError, ResourceExistsError, + ResourceModifiedError, ResourceNotFoundError, ResourceNotModifiedError, map_error, @@ -261,6 +263,12 @@ build_functions_list_namespaces_request, build_functions_list_triggers_request, build_functions_update_trigger_request, + build_gateway_approvals_update_request, + build_gateway_code_run_request, + build_gateway_session_approvals_update_request, + build_gateway_tools_list_request, + build_gateway_tools_list_search_request, + build_gateway_tools_run_request, build_genai_attach_agent_function_request, build_genai_attach_agent_guardrails_request, build_genai_attach_agent_request, @@ -337,9 +345,9 @@ build_genai_get_scenario_set_download_url_request, build_genai_get_scenario_set_request, build_genai_get_scheduled_indexing_request, - build_genai_get_simulation_journey_request, - build_genai_get_simulation_journey_trajectory_request, - build_genai_get_simulation_journey_trajectory_url_request, + build_genai_get_simulation_episode_request, + build_genai_get_simulation_episode_trajectory_request, + build_genai_get_simulation_episode_trajectory_url_request, build_genai_get_simulation_run_request, build_genai_get_workspace_request, build_genai_import_custom_model_request, @@ -376,7 +384,7 @@ build_genai_list_scenario_library_scenarios_request, build_genai_list_scenario_sets_request, build_genai_list_scenarios_request, - build_genai_list_simulation_journeys_request, + build_genai_list_simulation_episodes_request, build_genai_list_simulation_runs_request, build_genai_list_workspaces_request, build_genai_patch_cancel_simulation_run_request, @@ -403,6 +411,60 @@ build_genai_update_scenario_set_request, build_genai_update_simulation_run_request, build_genai_update_workspace_request, + build_harness_cancel_session_workspace_transfer_request, + build_harness_cancel_trigger_execution_request, + build_harness_commit_session_workspace_transfer_request, + build_harness_create_config_clone_request, + build_harness_create_config_request, + build_harness_create_session_checkpoint_request, + build_harness_create_session_fork_request, + build_harness_create_session_input_request, + build_harness_create_session_request, + build_harness_create_session_workspace_part_upload_urls_request, + build_harness_create_session_workspace_transfer_request, + build_harness_create_session_workspace_upload_request, + build_harness_create_template_request, + build_harness_create_trigger_request, + build_harness_delete_config_request, + build_harness_delete_session_checkpoint_request, + build_harness_delete_session_request, + build_harness_delete_template_request, + build_harness_delete_trigger_request, + build_harness_get_config_request, + build_harness_get_session_checkpoint_request, + build_harness_get_session_events_request, + build_harness_get_session_ingress_request, + build_harness_get_session_port_forward_request, + build_harness_get_session_request, + build_harness_get_session_workspace_download_request, + build_harness_get_session_workspace_transfer_request, + build_harness_get_template_build_request, + build_harness_get_template_request, + build_harness_get_trigger_by_session_request, + build_harness_get_trigger_execution_request, + build_harness_get_trigger_request, + build_harness_list_config_sessions_request, + build_harness_list_configs_request, + build_harness_list_reusable_sessions_request, + build_harness_list_session_checkpoints_request, + build_harness_list_session_sandbox_sizes_request, + build_harness_list_sessions_request, + build_harness_list_template_builds_request, + build_harness_list_templates_request, + build_harness_list_trigger_executions_request, + build_harness_list_triggers_request, + build_harness_list_webhook_providers_request, + build_harness_post_session_hitl_request, + build_harness_post_session_pause_request, + build_harness_post_session_resume_request, + build_harness_post_session_sandbox_exec_request, + build_harness_post_trigger_rotate_secret_request, + build_harness_post_trigger_webhook_request, + build_harness_revert_session_checkpoint_request, + build_harness_update_session_request, + build_harness_update_template_request, + build_harness_update_trigger_request, + build_harness_validate_session_policy_request, build_image_actions_get_request, build_image_actions_list_request, build_image_actions_post_request, @@ -442,10 +504,13 @@ build_insights_get_prom_query_range_request, build_insights_get_prom_query_request, build_insights_get_prom_series_request, + build_insights_get_trace_request, build_insights_list_alert_instances_request, build_insights_list_alert_rules_request, build_insights_list_notification_channels_request, build_insights_post_logs_search_request, + build_insights_post_spans_search_request, + build_insights_post_traces_search_request, build_insights_update_alert_rule_request, build_insights_update_notification_channel_request, build_invoices_get_by_uuid_request, @@ -499,6 +564,19 @@ build_mcp_servers_patch_request, build_mcp_servers_run_resync_request, build_mcp_servers_update_tools_request, + build_microvm_checkpoints_create_request, + build_microvm_checkpoints_delete_request, + build_microvm_checkpoints_get_request, + build_microvm_checkpoints_list_request, + build_microvms_create_request, + build_microvms_delete_request, + build_microvms_get_console_request, + build_microvms_get_options_request, + build_microvms_get_request, + build_microvms_list_request, + build_microvms_post_pause_request, + build_microvms_post_resume_request, + build_microvms_run_command_request, build_monitoring_create_alert_policy_request, build_monitoring_create_destination_request, build_monitoring_create_sink_request, @@ -680,6 +758,20 @@ build_sessions_delete_request, build_sessions_list_request, build_sessions_list_search_request, + build_signals_create_deletion_request, + build_signals_create_export_request, + build_signals_get_consent_request, + build_signals_get_deletion_request, + build_signals_get_export_download_request, + build_signals_get_export_options_request, + build_signals_get_export_request, + build_signals_list_agent_sessions_request, + build_signals_list_consents_request, + build_signals_list_deletions_request, + build_signals_list_exports_request, + build_signals_list_session_dialogues_request, + build_signals_update_consent_request, + build_signals_update_team_consent_request, build_sizes_list_request, build_snapshots_delete_request, build_snapshots_get_request, @@ -11076,14 +11168,14 @@ async def get(self, user_id: str, **kwargs: Any) -> JSON: return cast(JSON, deserialized) # type: ignore -class OneClicksOperations: +class GatewayToolsOperations: """ .. warning:: **DO NOT** instantiate this class directly. Instead, you should access the following operations through :class:`~pydo.aio.GeneratedClient`'s - :attr:`one_clicks` attribute. + :attr:`gateway_tools` attribute. """ def __init__(self, *args, **kwargs) -> None: @@ -11096,24 +11188,42 @@ def __init__(self, *args, **kwargs) -> None: ) @distributed_trace_async - async def list(self, *, type: Optional[str] = None, **kwargs: Any) -> JSON: - """List 1-Click Applications. + async def list( + self, + *, + x_session_id: str, + page_size: int = 20, + page_token: Optional[str] = None, + etag: Optional[str] = None, + match_condition: Optional[MatchConditions] = None, + **kwargs: Any + ) -> Optional[JSON]: + # pylint: disable=line-too-long + """List Session Tools. - To list all available 1-Click applications, send a GET request to ``/v2/1-clicks``. The - ``type`` may - be provided as query paramater in order to restrict results to a certain type of 1-Click, for - example: ``/v2/1-clicks?type=droplet``. Current supported types are ``kubernetes`` and - ``droplet``. + Lists the catalog tools that a session can call, ordered by name. The list applies the + session's ``tools`` selection and omits tools that its policy always denies. - The response will be a JSON object with a key called ``1_clicks``. This will be set to an array - of - 1-Click application data, each of which will contain the the slug and type for the 1-Click. + Each page includes an ``ETag`` header. Send it in ``If-None-Match`` to receive ``304 Not + Modified`` while the page is unchanged. - :keyword type: Restrict results to a certain type of 1-Click. Known values are: "droplet" and - "kubernetes". Default value is None. - :paramtype type: str - :return: JSON object - :rtype: JSON + :keyword x_session_id: The session to act in: the UUID at the end of the session's + ``sessionUrn`` and ``mcpUrl``. The session must belong to the authenticated user and team. + Required. + :paramtype x_session_id: str + :keyword page_size: The number of tools returned per page, from 1 to 100. Defaults to 20 when + omitted. Other values are rejected with 400. Default value is 20. + :paramtype page_size: int + :keyword page_token: The ``next_page_token`` from the previous response. A token is only valid + for the session that received it. Default value is None. + :paramtype page_token: str + :keyword etag: check if resource is changed. Set None to skip checking etag. Default value is + None. + :paramtype etag: str + :keyword match_condition: The match condition to use upon the etag. Default value is None. + :paramtype match_condition: ~azure.core.MatchConditions + :return: JSON object or None + :rtype: JSON or None :raises ~azure.core.exceptions.HttpResponseError: Example: @@ -11121,36 +11231,103 @@ async def list(self, *, type: Optional[str] = None, **kwargs: Any) -> JSON: # response body for status code(s): 200 response == { - "1_clicks": [ + "next_page_token": "str", # Optional. An opaque cursor for the next page of + results. Pass it back verbatim as ``page_token``. It is empty on the last page. + "tools": [ { - "slug": "str", # The slug identifier for the 1-Click - application. Required. - "type": "str" # The type of the 1-Click application. - Required. + "_meta": { + "str": {} # Optional. Additional tool metadata. + }, + "annotations": { + "destructiveHint": bool, # Optional. The tool may + perform destructive updates. + "idempotentHint": bool, # Optional. Repeating a call + with the same arguments has no additional effect. + "openWorldHint": bool, # Optional. The tool + interacts with external systems beyond the provider's own data. + "readOnlyHint": bool, # Optional. The tool does not + modify anything. + "title": "str" # Optional. Human-readable tool + title. + }, + "description": "str", # Optional. Tools ordered by name. + "inputSchema": { + "str": {} # Optional. The JSON Schema for the tool's + ``arguments``. + }, + "name": "str", # Optional. The provider-qualified tool name. + Pass it as ``tool`` to invoke the tool. + "outputSchema": { + "str": {} # Optional. The JSON Schema for the tool's + output, or for the output view bound to the tool in the session. + }, + "parallelizable": bool, # Optional. Whether the tool can run + alongside other calls in one invoke request. + "streamingSafe": bool, # Optional. Tools ordered by name. + "title": "str", # Optional. Tools ordered by name. + "version": "str" # Optional. The immutable tool version. } - ] + ], + "version": "str" # Optional. The catalog version. + } + # response body for status code(s): 400, 412, 429, 500, 503 + response == { + "call_id": "str", # A unique identifier for this request, when one was + assigned. Required. + "error": { + "class": "str", # Optional. The failure class. Branch on this value + rather than on ``message``"" , which may change. Known values are: + "invalid_argument", "unauthorized", "forbidden", "rate_limited", "not_found", + "timeout", "upstream_error", "output_too_large", "execution_failed", + "unavailable", "canceled", "render_required", "unsupported_media_type", and + "view_materialization_failed". + "message": "str", # Optional. A human-readable explanation that is + safe to show to an agent or end user. + "recovery_hint": "str", # Optional. The suggested next step. Known + values are: "fix_args", "refresh_auth", "retry_later", "narrow_output", + "contact_support", and "verify_provider_state". + "retriable": bool # Optional. Whether retrying the same request + later can succeed. + }, + "status": "str" # Required. "failed" + } + # response body for status code(s): 401 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. } """ error_map: MutableMapping[int, Type[HttpResponseError]] = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError, 304: ResourceNotModifiedError, - 401: cast( - Type[HttpResponseError], - lambda response: ClientAuthenticationError(response=response), - ), - 429: HttpResponseError, - 500: HttpResponseError, } + if match_condition == MatchConditions.IfNotModified: + error_map[412] = ResourceModifiedError + elif match_condition == MatchConditions.IfPresent: + error_map[412] = ResourceNotFoundError + elif match_condition == MatchConditions.IfMissing: + error_map[412] = ResourceExistsError error_map.update(kwargs.pop("error_map", {}) or {}) _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - cls: ClsType[JSON] = kwargs.pop("cls", None) + cls: ClsType[Optional[JSON]] = kwargs.pop("cls", None) - _request = build_one_clicks_list_request( - type=type, + _request = build_gateway_tools_list_request( + x_session_id=x_session_id, + page_size=page_size, + page_token=page_token, + etag=etag, + match_condition=match_condition, headers=_headers, params=_params, ) @@ -11165,47 +11342,104 @@ async def list(self, *, type: Optional[str] = None, **kwargs: Any) -> JSON: response = pipeline_response.http_response - if response.status_code not in [200]: + if response.status_code not in [200, 304, 400, 401, 403, 412, 429, 500, 503]: if _stream: await response.read() # Load the body in memory and close the socket - map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + map_error( + status_code=response.status_code, response=response, error_map=error_map + ) raise HttpResponseError(response=response) + deserialized = None response_headers = {} - response_headers["ratelimit-limit"] = self._deserialize( - "int", response.headers.get("ratelimit-limit") - ) - response_headers["ratelimit-remaining"] = self._deserialize( - "int", response.headers.get("ratelimit-remaining") - ) - response_headers["ratelimit-reset"] = self._deserialize( - "int", response.headers.get("ratelimit-reset") - ) + if response.status_code == 200: + response_headers["ETag"] = self._deserialize( + "str", response.headers.get("ETag") + ) - if response.content: - deserialized = response.json() - else: - deserialized = None + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 400: + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 401: + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 403: + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 412: + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 429: + response_headers["Retry-After"] = self._deserialize( + "int", response.headers.get("Retry-After") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 500: + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 503: + if response.content: + deserialized = response.json() + else: + deserialized = None if cls: - return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore + return cls(pipeline_response, deserialized, response_headers) # type: ignore - return cast(JSON, deserialized) # type: ignore + return deserialized # type: ignore @overload - async def install_kubernetes( - self, body: JSON, *, content_type: str = "application/json", **kwargs: Any + async def list_search( + self, + body: JSON, + *, + x_session_id: str, + content_type: str = "application/json", + **kwargs: Any ) -> JSON: # pylint: disable=line-too-long - """Install Kubernetes 1-Click Applications. + """Search Session Tools. - To install a Kubernetes 1-Click application on a cluster, send a POST request to - ``/v2/1-clicks/kubernetes``. The ``addon_slugs`` and ``cluster_uuid`` must be provided as body - parameter in order to specify which 1-Click application(s) to install. To list all available - 1-Click Kubernetes applications, send a request to ``/v2/1-clicks?type=kubernetes``. + Finds the catalog tools that fit one or more use cases. This is the REST form of the + ``action_search`` MCP tool. Each query is searched independently and returns its own result + group, so you can see which tools serve which goal. Results include each tool's + ``inputSchema``\\ , so you can call a tool with ``POST /tools/invoke`` without another lookup. + + Results include only tools that the session can call. If the session policy requires approval + for ``action_search``\\ , the response is a failed result with ``_meta.status`` set to + ``requires_approval``. :param body: Required. :type body: JSON + :keyword x_session_id: The session to act in: the UUID at the end of the session's + ``sessionUrn`` and ``mcpUrl``. The session must belong to the authenticated user and team. + Required. + :paramtype x_session_id: str :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". :paramtype content_type: str @@ -11218,33 +11452,153 @@ async def install_kubernetes( # JSON input template you can fill out and use as your body input. body = { - "addon_slugs": [ - "str" # An array of 1-Click Application slugs to be installed to the - Kubernetes cluster. Required. + "queries": [ + { + "use_case": "str", # A normalized English description of one + complete use case: the action and the intended outcome, including any app + the user named. Put personal identifiers in ``known_fields`` instead. Up + to 512 bytes and 32 search terms, with at least one letter or digit. + Required. + "known_fields": "str" # Optional. Known workflow inputs as + one string of comma-separated ``key:value`` pairs, such as identifiers or + settings. Used as context, not for keyword matching. + } ], - "cluster_uuid": "str" # A unique ID for the Kubernetes cluster to which the - 1-Click Applications will be installed. Required. + "limit": 10, # Optional. Default value is 10. The maximum number of tools + returned per query. Values above 50 are capped at 50. + "providers": [ + "str" # Optional. Restricts every query to these providers. + ], + "tags": [ + "str" # Optional. Restricts every query to tools with these tags. + ] } # response body for status code(s): 200 response == { - "message": "str" # Optional. A message about the result of the request. + "call_id": "str", # A unique identifier for this call. Include it when + contacting support. Required. + "status": "str", # Required. Known values are: "succeeded" and "failed". + "_meta": { + "str": {} # Optional. Additional result metadata. Keys include: * + ``status``"" : ``requires_approval``"" , ``requires_authorization``"" , or + ``requires_connection_configuration`` when the call stopped before running. + * ``approval_id``"" , ``tool_name``"" , ``expires_at``"" , and + ``decide``"" : present with ``requires_approval``. ``decide`` gives the + ``method``"" , ``path``"" , and ``body`` of the approval request to send. + Approve the call, then repeat it with the same arguments before + ``expires_at``. * `retry`: `{"attempt_count": N, "exhausted": bool}` when + the gateway retried the call. * ``output_view``"" : the output view + applied to the result. + }, + "error": { + "class": "str", # Optional. The failure class. Branch on this value + rather than on ``message``"" , which may change. Known values are: + "invalid_argument", "unauthorized", "forbidden", "rate_limited", "not_found", + "timeout", "upstream_error", "output_too_large", "execution_failed", + "unavailable", "canceled", "render_required", "unsupported_media_type", and + "view_materialization_failed". + "message": "str", # Optional. A human-readable explanation that is + safe to show to an agent or end user. + "recovery_hint": "str", # Optional. The suggested next step. Known + values are: "fix_args", "refresh_auth", "retry_later", "narrow_output", + "contact_support", and "verify_provider_state". + "retriable": bool # Optional. Whether retrying the same request + later can succeed. + }, + "output": { + "results": [ + { + "guidance": "str", # Optional. A suggested next + step. Present only when no tools matched. + "index": 0, # Optional. The 1-based position of the + query in the request. + "results": [ + { + "description": "str", # Optional. + One result group per query, in request order. + "inputSchema": { + "str": {} # Optional. The + JSON Schema for the tool's ``arguments``. + }, + "name": "str", # Optional. The + provider-qualified tool name. Pass it as ``tool`` to invoke + the tool. + "score": 0.0, # Optional. The + relevance score. Higher is more relevant. + "title": "str", # Optional. One + result group per query, in request order. + "version": "str" # Optional. One + result group per query, in request order. + } + ], + "use_case": "str" # Optional. The query's + ``use_case``. + } + ] + } + } + # response body for status code(s): 400, 412, 413, 429, 500, 503 + response == { + "call_id": "str", # A unique identifier for this request, when one was + assigned. Required. + "error": { + "class": "str", # Optional. The failure class. Branch on this value + rather than on ``message``"" , which may change. Known values are: + "invalid_argument", "unauthorized", "forbidden", "rate_limited", "not_found", + "timeout", "upstream_error", "output_too_large", "execution_failed", + "unavailable", "canceled", "render_required", "unsupported_media_type", and + "view_materialization_failed". + "message": "str", # Optional. A human-readable explanation that is + safe to show to an agent or end user. + "recovery_hint": "str", # Optional. The suggested next step. Known + values are: "fix_args", "refresh_auth", "retry_later", "narrow_output", + "contact_support", and "verify_provider_state". + "retriable": bool # Optional. Whether retrying the same request + later can succeed. + }, + "status": "str" # Required. "failed" + } + # response body for status code(s): 401 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. } """ @overload - async def install_kubernetes( - self, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any + async def list_search( + self, + body: IO[bytes], + *, + x_session_id: str, + content_type: str = "application/json", + **kwargs: Any ) -> JSON: - """Install Kubernetes 1-Click Applications. + # pylint: disable=line-too-long + """Search Session Tools. - To install a Kubernetes 1-Click application on a cluster, send a POST request to - ``/v2/1-clicks/kubernetes``. The ``addon_slugs`` and ``cluster_uuid`` must be provided as body - parameter in order to specify which 1-Click application(s) to install. To list all available - 1-Click Kubernetes applications, send a request to ``/v2/1-clicks?type=kubernetes``. + Finds the catalog tools that fit one or more use cases. This is the REST form of the + ``action_search`` MCP tool. Each query is searched independently and returns its own result + group, so you can see which tools serve which goal. Results include each tool's + ``inputSchema``\\ , so you can call a tool with ``POST /tools/invoke`` without another lookup. + + Results include only tools that the session can call. If the session policy requires approval + for ``action_search``\\ , the response is a failed result with ``_meta.status`` set to + ``requires_approval``. :param body: Required. :type body: IO[bytes] + :keyword x_session_id: The session to act in: the UUID at the end of the session's + ``sessionUrn`` and ``mcpUrl``. The session must belong to the authenticated user and team. + Required. + :paramtype x_session_id: str :keyword content_type: Body Parameter content-type. Content type parameter for binary body. Default value is "application/json". :paramtype content_type: str @@ -11257,24 +11611,124 @@ async def install_kubernetes( # response body for status code(s): 200 response == { - "message": "str" # Optional. A message about the result of the request. + "call_id": "str", # A unique identifier for this call. Include it when + contacting support. Required. + "status": "str", # Required. Known values are: "succeeded" and "failed". + "_meta": { + "str": {} # Optional. Additional result metadata. Keys include: * + ``status``"" : ``requires_approval``"" , ``requires_authorization``"" , or + ``requires_connection_configuration`` when the call stopped before running. + * ``approval_id``"" , ``tool_name``"" , ``expires_at``"" , and + ``decide``"" : present with ``requires_approval``. ``decide`` gives the + ``method``"" , ``path``"" , and ``body`` of the approval request to send. + Approve the call, then repeat it with the same arguments before + ``expires_at``. * `retry`: `{"attempt_count": N, "exhausted": bool}` when + the gateway retried the call. * ``output_view``"" : the output view + applied to the result. + }, + "error": { + "class": "str", # Optional. The failure class. Branch on this value + rather than on ``message``"" , which may change. Known values are: + "invalid_argument", "unauthorized", "forbidden", "rate_limited", "not_found", + "timeout", "upstream_error", "output_too_large", "execution_failed", + "unavailable", "canceled", "render_required", "unsupported_media_type", and + "view_materialization_failed". + "message": "str", # Optional. A human-readable explanation that is + safe to show to an agent or end user. + "recovery_hint": "str", # Optional. The suggested next step. Known + values are: "fix_args", "refresh_auth", "retry_later", "narrow_output", + "contact_support", and "verify_provider_state". + "retriable": bool # Optional. Whether retrying the same request + later can succeed. + }, + "output": { + "results": [ + { + "guidance": "str", # Optional. A suggested next + step. Present only when no tools matched. + "index": 0, # Optional. The 1-based position of the + query in the request. + "results": [ + { + "description": "str", # Optional. + One result group per query, in request order. + "inputSchema": { + "str": {} # Optional. The + JSON Schema for the tool's ``arguments``. + }, + "name": "str", # Optional. The + provider-qualified tool name. Pass it as ``tool`` to invoke + the tool. + "score": 0.0, # Optional. The + relevance score. Higher is more relevant. + "title": "str", # Optional. One + result group per query, in request order. + "version": "str" # Optional. One + result group per query, in request order. + } + ], + "use_case": "str" # Optional. The query's + ``use_case``. + } + ] + } + } + # response body for status code(s): 400, 412, 413, 429, 500, 503 + response == { + "call_id": "str", # A unique identifier for this request, when one was + assigned. Required. + "error": { + "class": "str", # Optional. The failure class. Branch on this value + rather than on ``message``"" , which may change. Known values are: + "invalid_argument", "unauthorized", "forbidden", "rate_limited", "not_found", + "timeout", "upstream_error", "output_too_large", "execution_failed", + "unavailable", "canceled", "render_required", "unsupported_media_type", and + "view_materialization_failed". + "message": "str", # Optional. A human-readable explanation that is + safe to show to an agent or end user. + "recovery_hint": "str", # Optional. The suggested next step. Known + values are: "fix_args", "refresh_auth", "retry_later", "narrow_output", + "contact_support", and "verify_provider_state". + "retriable": bool # Optional. Whether retrying the same request + later can succeed. + }, + "status": "str" # Required. "failed" + } + # response body for status code(s): 401 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. } """ @distributed_trace_async - async def install_kubernetes( - self, body: Union[JSON, IO[bytes]], **kwargs: Any + async def list_search( + self, body: Union[JSON, IO[bytes]], *, x_session_id: str, **kwargs: Any ) -> JSON: # pylint: disable=line-too-long - """Install Kubernetes 1-Click Applications. + """Search Session Tools. - To install a Kubernetes 1-Click application on a cluster, send a POST request to - ``/v2/1-clicks/kubernetes``. The ``addon_slugs`` and ``cluster_uuid`` must be provided as body - parameter in order to specify which 1-Click application(s) to install. To list all available - 1-Click Kubernetes applications, send a request to ``/v2/1-clicks?type=kubernetes``. + Finds the catalog tools that fit one or more use cases. This is the REST form of the + ``action_search`` MCP tool. Each query is searched independently and returns its own result + group, so you can see which tools serve which goal. Results include each tool's + ``inputSchema``\\ , so you can call a tool with ``POST /tools/invoke`` without another lookup. + + Results include only tools that the session can call. If the session policy requires approval + for ``action_search``\\ , the response is a failed result with ``_meta.status`` set to + ``requires_approval``. :param body: Is either a JSON type or a IO[bytes] type. Required. :type body: JSON or IO[bytes] + :keyword x_session_id: The session to act in: the UUID at the end of the session's + ``sessionUrn`` and ``mcpUrl``. The session must belong to the authenticated user and team. + Required. + :paramtype x_session_id: str :return: JSON object :rtype: JSON :raises ~azure.core.exceptions.HttpResponseError: @@ -11284,29 +11738,130 @@ async def install_kubernetes( # JSON input template you can fill out and use as your body input. body = { - "addon_slugs": [ - "str" # An array of 1-Click Application slugs to be installed to the - Kubernetes cluster. Required. + "queries": [ + { + "use_case": "str", # A normalized English description of one + complete use case: the action and the intended outcome, including any app + the user named. Put personal identifiers in ``known_fields`` instead. Up + to 512 bytes and 32 search terms, with at least one letter or digit. + Required. + "known_fields": "str" # Optional. Known workflow inputs as + one string of comma-separated ``key:value`` pairs, such as identifiers or + settings. Used as context, not for keyword matching. + } ], - "cluster_uuid": "str" # A unique ID for the Kubernetes cluster to which the - 1-Click Applications will be installed. Required. + "limit": 10, # Optional. Default value is 10. The maximum number of tools + returned per query. Values above 50 are capped at 50. + "providers": [ + "str" # Optional. Restricts every query to these providers. + ], + "tags": [ + "str" # Optional. Restricts every query to tools with these tags. + ] } # response body for status code(s): 200 response == { - "message": "str" # Optional. A message about the result of the request. + "call_id": "str", # A unique identifier for this call. Include it when + contacting support. Required. + "status": "str", # Required. Known values are: "succeeded" and "failed". + "_meta": { + "str": {} # Optional. Additional result metadata. Keys include: * + ``status``"" : ``requires_approval``"" , ``requires_authorization``"" , or + ``requires_connection_configuration`` when the call stopped before running. + * ``approval_id``"" , ``tool_name``"" , ``expires_at``"" , and + ``decide``"" : present with ``requires_approval``. ``decide`` gives the + ``method``"" , ``path``"" , and ``body`` of the approval request to send. + Approve the call, then repeat it with the same arguments before + ``expires_at``. * `retry`: `{"attempt_count": N, "exhausted": bool}` when + the gateway retried the call. * ``output_view``"" : the output view + applied to the result. + }, + "error": { + "class": "str", # Optional. The failure class. Branch on this value + rather than on ``message``"" , which may change. Known values are: + "invalid_argument", "unauthorized", "forbidden", "rate_limited", "not_found", + "timeout", "upstream_error", "output_too_large", "execution_failed", + "unavailable", "canceled", "render_required", "unsupported_media_type", and + "view_materialization_failed". + "message": "str", # Optional. A human-readable explanation that is + safe to show to an agent or end user. + "recovery_hint": "str", # Optional. The suggested next step. Known + values are: "fix_args", "refresh_auth", "retry_later", "narrow_output", + "contact_support", and "verify_provider_state". + "retriable": bool # Optional. Whether retrying the same request + later can succeed. + }, + "output": { + "results": [ + { + "guidance": "str", # Optional. A suggested next + step. Present only when no tools matched. + "index": 0, # Optional. The 1-based position of the + query in the request. + "results": [ + { + "description": "str", # Optional. + One result group per query, in request order. + "inputSchema": { + "str": {} # Optional. The + JSON Schema for the tool's ``arguments``. + }, + "name": "str", # Optional. The + provider-qualified tool name. Pass it as ``tool`` to invoke + the tool. + "score": 0.0, # Optional. The + relevance score. Higher is more relevant. + "title": "str", # Optional. One + result group per query, in request order. + "version": "str" # Optional. One + result group per query, in request order. + } + ], + "use_case": "str" # Optional. The query's + ``use_case``. + } + ] + } + } + # response body for status code(s): 400, 412, 413, 429, 500, 503 + response == { + "call_id": "str", # A unique identifier for this request, when one was + assigned. Required. + "error": { + "class": "str", # Optional. The failure class. Branch on this value + rather than on ``message``"" , which may change. Known values are: + "invalid_argument", "unauthorized", "forbidden", "rate_limited", "not_found", + "timeout", "upstream_error", "output_too_large", "execution_failed", + "unavailable", "canceled", "render_required", "unsupported_media_type", and + "view_materialization_failed". + "message": "str", # Optional. A human-readable explanation that is + safe to show to an agent or end user. + "recovery_hint": "str", # Optional. The suggested next step. Known + values are: "fix_args", "refresh_auth", "retry_later", "narrow_output", + "contact_support", and "verify_provider_state". + "retriable": bool # Optional. Whether retrying the same request + later can succeed. + }, + "status": "str" # Required. "failed" + } + # response body for status code(s): 401 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. } """ error_map: MutableMapping[int, Type[HttpResponseError]] = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError, 304: ResourceNotModifiedError, - 401: cast( - Type[HttpResponseError], - lambda response: ClientAuthenticationError(response=response), - ), - 429: HttpResponseError, - 500: HttpResponseError, } error_map.update(kwargs.pop("error_map", {}) or {}) @@ -11326,7 +11881,8 @@ async def install_kubernetes( else: _json = body - _request = build_one_clicks_install_kubernetes_request( + _request = build_gateway_tools_list_search_request( + x_session_id=x_session_id, content_type=content_type, json=_json, content=_content, @@ -11344,277 +11900,72 @@ async def install_kubernetes( response = pipeline_response.http_response - if response.status_code not in [200]: + if response.status_code not in [200, 400, 401, 403, 412, 413, 429, 500, 503]: if _stream: await response.read() # Load the body in memory and close the socket - map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore - raise HttpResponseError(response=response) - - response_headers = {} - response_headers["ratelimit-limit"] = self._deserialize( - "int", response.headers.get("ratelimit-limit") - ) - response_headers["ratelimit-remaining"] = self._deserialize( - "int", response.headers.get("ratelimit-remaining") - ) - response_headers["ratelimit-reset"] = self._deserialize( - "int", response.headers.get("ratelimit-reset") - ) - - if response.content: - deserialized = response.json() - else: - deserialized = None - - if cls: - return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore - - return cast(JSON, deserialized) # type: ignore - - -class AccountOperations: - """ - .. warning:: - **DO NOT** instantiate this class directly. - - Instead, you should access the following operations through - :class:`~pydo.aio.GeneratedClient`'s - :attr:`account` attribute. - """ - - def __init__(self, *args, **kwargs) -> None: - input_args = list(args) - self._client = input_args.pop(0) if input_args else kwargs.pop("client") - self._config = input_args.pop(0) if input_args else kwargs.pop("config") - self._serialize = input_args.pop(0) if input_args else kwargs.pop("serializer") - self._deserialize = ( - input_args.pop(0) if input_args else kwargs.pop("deserializer") - ) - - @distributed_trace_async - async def get(self, **kwargs: Any) -> JSON: - # pylint: disable=line-too-long - """Get User Information. - - To show information about the current user account, send a GET request to ``/v2/account``. - - :return: JSON object - :rtype: JSON - :raises ~azure.core.exceptions.HttpResponseError: - - Example: - .. code-block:: python - - # response body for status code(s): 200 - response == { - "account": { - "droplet_limit": 0, # The total number of Droplets current user or - team may have active at one time. :code:`
`:code:`
`Requires - ``droplet:read`` scope. Required. - "email": "str", # The email address used by the current user to - register for DigitalOcean. Required. - "email_verified": False, # Default value is False. If true, the user - has verified their account via email. False otherwise. Required. - "floating_ip_limit": 0, # The total number of Floating IPs the - current user or team may have. :code:`
`:code:`
`Requires - ``reserved_ip:read`` scope. Required. - "status": "active", # Default value is "active". This value is one - of "active", "warning" or "locked". Known values are: "active", "warning", - and "locked". - "status_message": "str", # A human-readable message giving more - details about the status of the account. Required. - "uuid": "str", # The unique universal identifier for the current - user. Required. - "name": "str", # Optional. The display name for the current user. - "team": { - "name": "str", # Optional. The name for the current team. - "uuid": "str" # Optional. The unique universal identifier - for the current team. - } - } - } - """ - error_map: MutableMapping[int, Type[HttpResponseError]] = { - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - 401: cast( - Type[HttpResponseError], - lambda response: ClientAuthenticationError(response=response), - ), - 429: HttpResponseError, - 500: HttpResponseError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) - - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} - - cls: ClsType[JSON] = kwargs.pop("cls", None) - - _request = build_account_get_request( - headers=_headers, - params=_params, - ) - _request.url = self._client.format_url(_request.url) - - _stream = False - pipeline_response: PipelineResponse = ( - await self._client._pipeline.run( # pylint: disable=protected-access - _request, stream=_stream, **kwargs + map_error( + status_code=response.status_code, response=response, error_map=error_map ) - ) - - response = pipeline_response.http_response - - if response.status_code not in [200]: - if _stream: - await response.read() # Load the body in memory and close the socket - map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore raise HttpResponseError(response=response) response_headers = {} - response_headers["ratelimit-limit"] = self._deserialize( - "int", response.headers.get("ratelimit-limit") - ) - response_headers["ratelimit-remaining"] = self._deserialize( - "int", response.headers.get("ratelimit-remaining") - ) - response_headers["ratelimit-reset"] = self._deserialize( - "int", response.headers.get("ratelimit-reset") - ) - - if response.content: - deserialized = response.json() - else: - deserialized = None - - if cls: - return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore - - return cast(JSON, deserialized) # type: ignore - - -class SshKeysOperations: - """ - .. warning:: - **DO NOT** instantiate this class directly. - - Instead, you should access the following operations through - :class:`~pydo.aio.GeneratedClient`'s - :attr:`ssh_keys` attribute. - """ - - def __init__(self, *args, **kwargs) -> None: - input_args = list(args) - self._client = input_args.pop(0) if input_args else kwargs.pop("client") - self._config = input_args.pop(0) if input_args else kwargs.pop("config") - self._serialize = input_args.pop(0) if input_args else kwargs.pop("serializer") - self._deserialize = ( - input_args.pop(0) if input_args else kwargs.pop("deserializer") - ) - - @distributed_trace_async - async def list(self, *, per_page: int = 20, page: int = 1, **kwargs: Any) -> JSON: - # pylint: disable=line-too-long - """List All SSH Keys. - - To list all of the keys in your account, send a GET request to ``/v2/account/keys``. The - response will be a JSON object with a key set to ``ssh_keys``. The value of this will be an - array of ssh_key objects, each of which contains the standard ssh_key attributes. - - :keyword per_page: Number of items returned per page. Default value is 20. - :paramtype per_page: int - :keyword page: Which 'page' of paginated results to return. Default value is 1. - :paramtype page: int - :return: JSON object - :rtype: JSON - :raises ~azure.core.exceptions.HttpResponseError: + if response.status_code == 200: + if response.content: + deserialized = response.json() + else: + deserialized = None - Example: - .. code-block:: python + if response.status_code == 400: + if response.content: + deserialized = response.json() + else: + deserialized = None - # response body for status code(s): 200 - response == { - "meta": { - "total": 0 # Optional. Number of objects returned by the request. - }, - "links": { - "pages": {} - }, - "ssh_keys": [ - { - "name": "str", # A human-readable display name for this key, - used to easily identify the SSH keys when they are displayed. Required. - "public_key": "str", # The entire public key string that was - uploaded. Embedded into the root user's ``authorized_keys`` file if you - include this key during Droplet creation. Required. - "fingerprint": "str", # Optional. A unique identifier that - differentiates this key from other keys using a format that SSH - recognizes. The fingerprint is created when the key is added to your - account. - "id": 0 # Optional. A unique identification number for this - key. Can be used to embed a specific SSH key into a Droplet. - } - ] - } - """ - error_map: MutableMapping[int, Type[HttpResponseError]] = { - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - 401: cast( - Type[HttpResponseError], - lambda response: ClientAuthenticationError(response=response), - ), - 429: HttpResponseError, - 500: HttpResponseError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) + if response.status_code == 401: + if response.content: + deserialized = response.json() + else: + deserialized = None - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} + if response.status_code == 403: + if response.content: + deserialized = response.json() + else: + deserialized = None - cls: ClsType[JSON] = kwargs.pop("cls", None) + if response.status_code == 412: + if response.content: + deserialized = response.json() + else: + deserialized = None - _request = build_ssh_keys_list_request( - per_page=per_page, - page=page, - headers=_headers, - params=_params, - ) - _request.url = self._client.format_url(_request.url) + if response.status_code == 413: + if response.content: + deserialized = response.json() + else: + deserialized = None - _stream = False - pipeline_response: PipelineResponse = ( - await self._client._pipeline.run( # pylint: disable=protected-access - _request, stream=_stream, **kwargs + if response.status_code == 429: + response_headers["Retry-After"] = self._deserialize( + "int", response.headers.get("Retry-After") ) - ) - - response = pipeline_response.http_response - if response.status_code not in [200]: - if _stream: - await response.read() # Load the body in memory and close the socket - map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore - raise HttpResponseError(response=response) + if response.content: + deserialized = response.json() + else: + deserialized = None - response_headers = {} - response_headers["ratelimit-limit"] = self._deserialize( - "int", response.headers.get("ratelimit-limit") - ) - response_headers["ratelimit-remaining"] = self._deserialize( - "int", response.headers.get("ratelimit-remaining") - ) - response_headers["ratelimit-reset"] = self._deserialize( - "int", response.headers.get("ratelimit-reset") - ) + if response.status_code == 500: + if response.content: + deserialized = response.json() + else: + deserialized = None - if response.content: - deserialized = response.json() - else: - deserialized = None + if response.status_code == 503: + if response.content: + deserialized = response.json() + else: + deserialized = None if cls: return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore @@ -11622,18 +11973,36 @@ async def list(self, *, per_page: int = 20, page: int = 1, **kwargs: Any) -> JSO return cast(JSON, deserialized) # type: ignore @overload - async def create( - self, body: JSON, *, content_type: str = "application/json", **kwargs: Any + async def run( + self, + body: JSON, + *, + x_session_id: str, + content_type: str = "application/json", + **kwargs: Any ) -> JSON: # pylint: disable=line-too-long - """Create a New SSH Key. + """Invoke Session Tools. - To add a new SSH public key to your DigitalOcean account, send a POST request to - ``/v2/account/keys``. Set the ``name`` attribute to the name you wish to use and the - ``public_key`` attribute to the full public key you are adding. + Runs 1 to 10 tool calls in parallel and returns one result per call, in request order. This is + the REST form of the ``action_invoke`` MCP tool. + + Each call is checked against the session's tool selection and policy, uses the session actor's + connections, and applies any output view bound to the tool in the session. A well-formed + request returns ``200`` even when some calls fail. Check each item's ``result.status``\\ , and + use ``success_count`` and ``error_count`` to branch without scanning every item. + + A call whose policy requires approval fails with ``_meta.status`` set to ``requires_approval``. + Have the session's owner approve it, then repeat the call with the same arguments. A call that + needs the end user to connect a provider fails with ``_meta.status`` set to + ``requires_authorization`` or ``requires_connection_configuration``. :param body: Required. :type body: JSON + :keyword x_session_id: The session to act in: the UUID at the end of the session's + ``sessionUrn`` and ``mcpUrl``. The session must belong to the authenticated user and team. + Required. + :paramtype x_session_id: str :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". :paramtype content_type: str @@ -11646,48 +12015,145 @@ async def create( # JSON input template you can fill out and use as your body input. body = { - "name": "str", # A human-readable display name for this key, used to easily - identify the SSH keys when they are displayed. Required. - "public_key": "str", # The entire public key string that was uploaded. - Embedded into the root user's ``authorized_keys`` file if you include this key - during Droplet creation. Required. - "fingerprint": "str", # Optional. A unique identifier that differentiates - this key from other keys using a format that SSH recognizes. The fingerprint is - created when the key is added to your account. - "id": 0 # Optional. A unique identification number for this key. Can be used - to embed a specific SSH key into a Droplet. + "tools": [ + { + "tool": "str", # The tool name, as returned by ``GET + /tools`` or ``POST /tools/search``. Names are case-insensitive. Required. + "arguments": { + "str": {} # Optional. The tool's arguments, matching + its ``inputSchema``. + }, + "version": "str" # Optional. An immutable tool version. Omit + to use the released version. + } + ], + "rationale": "str" # Optional. One sentence explaining why the tools are + being called. Values longer than 512 characters are truncated. } - # response body for status code(s): 201 + # response body for status code(s): 200 response == { - "ssh_key": { - "name": "str", # A human-readable display name for this key, used to - easily identify the SSH keys when they are displayed. Required. - "public_key": "str", # The entire public key string that was - uploaded. Embedded into the root user's ``authorized_keys`` file if you - include this key during Droplet creation. Required. - "fingerprint": "str", # Optional. A unique identifier that - differentiates this key from other keys using a format that SSH recognizes. - The fingerprint is created when the key is added to your account. - "id": 0 # Optional. A unique identification number for this key. Can - be used to embed a specific SSH key into a Droplet. - } + "error_count": 0, # Optional. + "results": [ + { + "index": 0, # Optional. The 0-based position of the call in + the request. + "result": { + "call_id": "str", # A unique identifier for this + call. Include it when contacting support. Required. + "status": "str", # Required. Known values are: + "succeeded" and "failed". + "_meta": { + "str": {} # Optional. Additional result + metadata. Keys include: * ``status``"" : + ``requires_approval``"" , ``requires_authorization``"" , or + ``requires_connection_configuration`` when the call stopped + before running. * ``approval_id``"" , ``tool_name``"" , + ``expires_at``"" , and ``decide``"" : present with + ``requires_approval``. ``decide`` gives the ``method``"" , + ``path``"" , and ``body`` of the approval request to send. + Approve the call, then repeat it with the same arguments before + ``expires_at``. * `retry`: `{"attempt_count": N, "exhausted": + bool}` when the gateway retried the call. * + ``output_view``"" : the output view applied to the result. + }, + "error": { + "class": "str", # Optional. The failure + class. Branch on this value rather than on ``message``"" , which + may change. Known values are: "invalid_argument", "unauthorized", + "forbidden", "rate_limited", "not_found", "timeout", + "upstream_error", "output_too_large", "execution_failed", + "unavailable", "canceled", "render_required", + "unsupported_media_type", and "view_materialization_failed". + "message": "str", # Optional. A + human-readable explanation that is safe to show to an agent or + end user. + "recovery_hint": "str", # Optional. The + suggested next step. Known values are: "fix_args", + "refresh_auth", "retry_later", "narrow_output", + "contact_support", and "verify_provider_state". + "retriable": bool # Optional. Whether + retrying the same request later can succeed. + }, + "output": { + "str": {} # Optional. The tool's JSON + output. Present when ``status`` is ``succeeded``. Its shape is + the tool's output schema, or the output view bound to the tool in + the session. + } + }, + "tool": "str", # Optional. The canonical tool name. + "version": "str" # Optional. The tool version that ran. + } + ], + "success_count": 0, # Optional. + "total_count": 0 # Optional. + } + # response body for status code(s): 400, 402, 412, 413, 429, 500, 503 + response == { + "call_id": "str", # A unique identifier for this request, when one was + assigned. Required. + "error": { + "class": "str", # Optional. The failure class. Branch on this value + rather than on ``message``"" , which may change. Known values are: + "invalid_argument", "unauthorized", "forbidden", "rate_limited", "not_found", + "timeout", "upstream_error", "output_too_large", "execution_failed", + "unavailable", "canceled", "render_required", "unsupported_media_type", and + "view_materialization_failed". + "message": "str", # Optional. A human-readable explanation that is + safe to show to an agent or end user. + "recovery_hint": "str", # Optional. The suggested next step. Known + values are: "fix_args", "refresh_auth", "retry_later", "narrow_output", + "contact_support", and "verify_provider_state". + "retriable": bool # Optional. Whether retrying the same request + later can succeed. + }, + "status": "str" # Required. "failed" + } + # response body for status code(s): 401 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. } """ @overload - async def create( - self, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any + async def run( + self, + body: IO[bytes], + *, + x_session_id: str, + content_type: str = "application/json", + **kwargs: Any ) -> JSON: # pylint: disable=line-too-long - """Create a New SSH Key. + """Invoke Session Tools. - To add a new SSH public key to your DigitalOcean account, send a POST request to - ``/v2/account/keys``. Set the ``name`` attribute to the name you wish to use and the - ``public_key`` attribute to the full public key you are adding. + Runs 1 to 10 tool calls in parallel and returns one result per call, in request order. This is + the REST form of the ``action_invoke`` MCP tool. + + Each call is checked against the session's tool selection and policy, uses the session actor's + connections, and applies any output view bound to the tool in the session. A well-formed + request returns ``200`` even when some calls fail. Check each item's ``result.status``\\ , and + use ``success_count`` and ``error_count`` to branch without scanning every item. + + A call whose policy requires approval fails with ``_meta.status`` set to ``requires_approval``. + Have the session's owner approve it, then repeat the call with the same arguments. A call that + needs the end user to connect a provider fails with ``_meta.status`` set to + ``requires_authorization`` or ``requires_connection_configuration``. :param body: Required. :type body: IO[bytes] + :keyword x_session_id: The session to act in: the UUID at the end of the session's + ``sessionUrn`` and ``mcpUrl``. The session must belong to the authenticated user and team. + Required. + :paramtype x_session_id: str :keyword content_type: Body Parameter content-type. Content type parameter for binary body. Default value is "application/json". :paramtype content_type: str @@ -11698,34 +12164,124 @@ async def create( Example: .. code-block:: python - # response body for status code(s): 201 + # response body for status code(s): 200 response == { - "ssh_key": { - "name": "str", # A human-readable display name for this key, used to - easily identify the SSH keys when they are displayed. Required. - "public_key": "str", # The entire public key string that was - uploaded. Embedded into the root user's ``authorized_keys`` file if you - include this key during Droplet creation. Required. - "fingerprint": "str", # Optional. A unique identifier that - differentiates this key from other keys using a format that SSH recognizes. - The fingerprint is created when the key is added to your account. - "id": 0 # Optional. A unique identification number for this key. Can - be used to embed a specific SSH key into a Droplet. - } + "error_count": 0, # Optional. + "results": [ + { + "index": 0, # Optional. The 0-based position of the call in + the request. + "result": { + "call_id": "str", # A unique identifier for this + call. Include it when contacting support. Required. + "status": "str", # Required. Known values are: + "succeeded" and "failed". + "_meta": { + "str": {} # Optional. Additional result + metadata. Keys include: * ``status``"" : + ``requires_approval``"" , ``requires_authorization``"" , or + ``requires_connection_configuration`` when the call stopped + before running. * ``approval_id``"" , ``tool_name``"" , + ``expires_at``"" , and ``decide``"" : present with + ``requires_approval``. ``decide`` gives the ``method``"" , + ``path``"" , and ``body`` of the approval request to send. + Approve the call, then repeat it with the same arguments before + ``expires_at``. * `retry`: `{"attempt_count": N, "exhausted": + bool}` when the gateway retried the call. * + ``output_view``"" : the output view applied to the result. + }, + "error": { + "class": "str", # Optional. The failure + class. Branch on this value rather than on ``message``"" , which + may change. Known values are: "invalid_argument", "unauthorized", + "forbidden", "rate_limited", "not_found", "timeout", + "upstream_error", "output_too_large", "execution_failed", + "unavailable", "canceled", "render_required", + "unsupported_media_type", and "view_materialization_failed". + "message": "str", # Optional. A + human-readable explanation that is safe to show to an agent or + end user. + "recovery_hint": "str", # Optional. The + suggested next step. Known values are: "fix_args", + "refresh_auth", "retry_later", "narrow_output", + "contact_support", and "verify_provider_state". + "retriable": bool # Optional. Whether + retrying the same request later can succeed. + }, + "output": { + "str": {} # Optional. The tool's JSON + output. Present when ``status`` is ``succeeded``. Its shape is + the tool's output schema, or the output view bound to the tool in + the session. + } + }, + "tool": "str", # Optional. The canonical tool name. + "version": "str" # Optional. The tool version that ran. + } + ], + "success_count": 0, # Optional. + "total_count": 0 # Optional. + } + # response body for status code(s): 400, 402, 412, 413, 429, 500, 503 + response == { + "call_id": "str", # A unique identifier for this request, when one was + assigned. Required. + "error": { + "class": "str", # Optional. The failure class. Branch on this value + rather than on ``message``"" , which may change. Known values are: + "invalid_argument", "unauthorized", "forbidden", "rate_limited", "not_found", + "timeout", "upstream_error", "output_too_large", "execution_failed", + "unavailable", "canceled", "render_required", "unsupported_media_type", and + "view_materialization_failed". + "message": "str", # Optional. A human-readable explanation that is + safe to show to an agent or end user. + "recovery_hint": "str", # Optional. The suggested next step. Known + values are: "fix_args", "refresh_auth", "retry_later", "narrow_output", + "contact_support", and "verify_provider_state". + "retriable": bool # Optional. Whether retrying the same request + later can succeed. + }, + "status": "str" # Required. "failed" + } + # response body for status code(s): 401 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. } """ @distributed_trace_async - async def create(self, body: Union[JSON, IO[bytes]], **kwargs: Any) -> JSON: + async def run( + self, body: Union[JSON, IO[bytes]], *, x_session_id: str, **kwargs: Any + ) -> JSON: # pylint: disable=line-too-long - """Create a New SSH Key. + """Invoke Session Tools. - To add a new SSH public key to your DigitalOcean account, send a POST request to - ``/v2/account/keys``. Set the ``name`` attribute to the name you wish to use and the - ``public_key`` attribute to the full public key you are adding. + Runs 1 to 10 tool calls in parallel and returns one result per call, in request order. This is + the REST form of the ``action_invoke`` MCP tool. + + Each call is checked against the session's tool selection and policy, uses the session actor's + connections, and applies any output view bound to the tool in the session. A well-formed + request returns ``200`` even when some calls fail. Check each item's ``result.status``\\ , and + use ``success_count`` and ``error_count`` to branch without scanning every item. + + A call whose policy requires approval fails with ``_meta.status`` set to ``requires_approval``. + Have the session's owner approve it, then repeat the call with the same arguments. A call that + needs the end user to connect a provider fails with ``_meta.status`` set to + ``requires_authorization`` or ``requires_connection_configuration``. :param body: Is either a JSON type or a IO[bytes] type. Required. :type body: JSON or IO[bytes] + :keyword x_session_id: The session to act in: the UUID at the end of the session's + ``sessionUrn`` and ``mcpUrl``. The session must belong to the authenticated user and team. + Required. + :paramtype x_session_id: str :return: JSON object :rtype: JSON :raises ~azure.core.exceptions.HttpResponseError: @@ -11735,44 +12291,118 @@ async def create(self, body: Union[JSON, IO[bytes]], **kwargs: Any) -> JSON: # JSON input template you can fill out and use as your body input. body = { - "name": "str", # A human-readable display name for this key, used to easily - identify the SSH keys when they are displayed. Required. - "public_key": "str", # The entire public key string that was uploaded. - Embedded into the root user's ``authorized_keys`` file if you include this key - during Droplet creation. Required. - "fingerprint": "str", # Optional. A unique identifier that differentiates - this key from other keys using a format that SSH recognizes. The fingerprint is - created when the key is added to your account. - "id": 0 # Optional. A unique identification number for this key. Can be used - to embed a specific SSH key into a Droplet. + "tools": [ + { + "tool": "str", # The tool name, as returned by ``GET + /tools`` or ``POST /tools/search``. Names are case-insensitive. Required. + "arguments": { + "str": {} # Optional. The tool's arguments, matching + its ``inputSchema``. + }, + "version": "str" # Optional. An immutable tool version. Omit + to use the released version. + } + ], + "rationale": "str" # Optional. One sentence explaining why the tools are + being called. Values longer than 512 characters are truncated. } - # response body for status code(s): 201 + # response body for status code(s): 200 response == { - "ssh_key": { - "name": "str", # A human-readable display name for this key, used to - easily identify the SSH keys when they are displayed. Required. - "public_key": "str", # The entire public key string that was - uploaded. Embedded into the root user's ``authorized_keys`` file if you - include this key during Droplet creation. Required. - "fingerprint": "str", # Optional. A unique identifier that - differentiates this key from other keys using a format that SSH recognizes. - The fingerprint is created when the key is added to your account. - "id": 0 # Optional. A unique identification number for this key. Can - be used to embed a specific SSH key into a Droplet. - } + "error_count": 0, # Optional. + "results": [ + { + "index": 0, # Optional. The 0-based position of the call in + the request. + "result": { + "call_id": "str", # A unique identifier for this + call. Include it when contacting support. Required. + "status": "str", # Required. Known values are: + "succeeded" and "failed". + "_meta": { + "str": {} # Optional. Additional result + metadata. Keys include: * ``status``"" : + ``requires_approval``"" , ``requires_authorization``"" , or + ``requires_connection_configuration`` when the call stopped + before running. * ``approval_id``"" , ``tool_name``"" , + ``expires_at``"" , and ``decide``"" : present with + ``requires_approval``. ``decide`` gives the ``method``"" , + ``path``"" , and ``body`` of the approval request to send. + Approve the call, then repeat it with the same arguments before + ``expires_at``. * `retry`: `{"attempt_count": N, "exhausted": + bool}` when the gateway retried the call. * + ``output_view``"" : the output view applied to the result. + }, + "error": { + "class": "str", # Optional. The failure + class. Branch on this value rather than on ``message``"" , which + may change. Known values are: "invalid_argument", "unauthorized", + "forbidden", "rate_limited", "not_found", "timeout", + "upstream_error", "output_too_large", "execution_failed", + "unavailable", "canceled", "render_required", + "unsupported_media_type", and "view_materialization_failed". + "message": "str", # Optional. A + human-readable explanation that is safe to show to an agent or + end user. + "recovery_hint": "str", # Optional. The + suggested next step. Known values are: "fix_args", + "refresh_auth", "retry_later", "narrow_output", + "contact_support", and "verify_provider_state". + "retriable": bool # Optional. Whether + retrying the same request later can succeed. + }, + "output": { + "str": {} # Optional. The tool's JSON + output. Present when ``status`` is ``succeeded``. Its shape is + the tool's output schema, or the output view bound to the tool in + the session. + } + }, + "tool": "str", # Optional. The canonical tool name. + "version": "str" # Optional. The tool version that ran. + } + ], + "success_count": 0, # Optional. + "total_count": 0 # Optional. + } + # response body for status code(s): 400, 402, 412, 413, 429, 500, 503 + response == { + "call_id": "str", # A unique identifier for this request, when one was + assigned. Required. + "error": { + "class": "str", # Optional. The failure class. Branch on this value + rather than on ``message``"" , which may change. Known values are: + "invalid_argument", "unauthorized", "forbidden", "rate_limited", "not_found", + "timeout", "upstream_error", "output_too_large", "execution_failed", + "unavailable", "canceled", "render_required", "unsupported_media_type", and + "view_materialization_failed". + "message": "str", # Optional. A human-readable explanation that is + safe to show to an agent or end user. + "recovery_hint": "str", # Optional. The suggested next step. Known + values are: "fix_args", "refresh_auth", "retry_later", "narrow_output", + "contact_support", and "verify_provider_state". + "retriable": bool # Optional. Whether retrying the same request + later can succeed. + }, + "status": "str" # Required. "failed" + } + # response body for status code(s): 401 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. } """ error_map: MutableMapping[int, Type[HttpResponseError]] = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError, 304: ResourceNotModifiedError, - 401: cast( - Type[HttpResponseError], - lambda response: ClientAuthenticationError(response=response), - ), - 429: HttpResponseError, - 500: HttpResponseError, } error_map.update(kwargs.pop("error_map", {}) or {}) @@ -11792,7 +12422,8 @@ async def create(self, body: Union[JSON, IO[bytes]], **kwargs: Any) -> JSON: else: _json = body - _request = build_ssh_keys_create_request( + _request = build_gateway_tools_run_request( + x_session_id=x_session_id, content_type=content_type, json=_json, content=_content, @@ -11810,129 +12441,71 @@ async def create(self, body: Union[JSON, IO[bytes]], **kwargs: Any) -> JSON: response = pipeline_response.http_response - if response.status_code not in [201]: + if response.status_code not in [ + 200, + 400, + 401, + 402, + 403, + 412, + 413, + 429, + 500, + 503, + ]: if _stream: await response.read() # Load the body in memory and close the socket - map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + map_error( + status_code=response.status_code, response=response, error_map=error_map + ) raise HttpResponseError(response=response) response_headers = {} - response_headers["ratelimit-limit"] = self._deserialize( - "int", response.headers.get("ratelimit-limit") - ) - response_headers["ratelimit-remaining"] = self._deserialize( - "int", response.headers.get("ratelimit-remaining") - ) - response_headers["ratelimit-reset"] = self._deserialize( - "int", response.headers.get("ratelimit-reset") - ) - - if response.content: - deserialized = response.json() - else: - deserialized = None - - if cls: - return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore - - return cast(JSON, deserialized) # type: ignore - - @distributed_trace_async - async def get(self, ssh_key_identifier: JSON, **kwargs: Any) -> JSON: - # pylint: disable=line-too-long - """Retrieve an Existing SSH Key. - - To get information about a key, send a GET request to ``/v2/account/keys/$KEY_ID`` or - ``/v2/account/keys/$KEY_FINGERPRINT``. - The response will be a JSON object with the key ``ssh_key`` and value an ssh_key object which - contains the standard ssh_key attributes. - - :param ssh_key_identifier: Either the ID or the fingerprint of an existing SSH key. Required. - :type ssh_key_identifier: JSON - :return: JSON object - :rtype: JSON - :raises ~azure.core.exceptions.HttpResponseError: - - Example: - .. code-block:: python - - # response body for status code(s): 200 - response == { - "ssh_key": { - "name": "str", # A human-readable display name for this key, used to - easily identify the SSH keys when they are displayed. Required. - "public_key": "str", # The entire public key string that was - uploaded. Embedded into the root user's ``authorized_keys`` file if you - include this key during Droplet creation. Required. - "fingerprint": "str", # Optional. A unique identifier that - differentiates this key from other keys using a format that SSH recognizes. - The fingerprint is created when the key is added to your account. - "id": 0 # Optional. A unique identification number for this key. Can - be used to embed a specific SSH key into a Droplet. - } - } - # response body for status code(s): 404 - response == { - "id": "str", # A short identifier corresponding to the HTTP status code - returned. For example, the ID for a response returning a 404 status code would - be "not_found.". Required. - "message": "str", # A message providing additional information about the - error, including details to help resolve it when possible. Required. - "request_id": "str" # Optional. Optionally, some endpoints may include a - request ID that should be provided when reporting bugs or opening support - tickets to help identify the issue. - } - """ - error_map: MutableMapping[int, Type[HttpResponseError]] = { - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - 401: cast( - Type[HttpResponseError], - lambda response: ClientAuthenticationError(response=response), - ), - 429: HttpResponseError, - 500: HttpResponseError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) + if response.status_code == 200: + if response.content: + deserialized = response.json() + else: + deserialized = None - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} + if response.status_code == 400: + if response.content: + deserialized = response.json() + else: + deserialized = None - cls: ClsType[JSON] = kwargs.pop("cls", None) + if response.status_code == 401: + if response.content: + deserialized = response.json() + else: + deserialized = None - _request = build_ssh_keys_get_request( - ssh_key_identifier=ssh_key_identifier, - headers=_headers, - params=_params, - ) - _request.url = self._client.format_url(_request.url) + if response.status_code == 402: + if response.content: + deserialized = response.json() + else: + deserialized = None - _stream = False - pipeline_response: PipelineResponse = ( - await self._client._pipeline.run( # pylint: disable=protected-access - _request, stream=_stream, **kwargs - ) - ) + if response.status_code == 403: + if response.content: + deserialized = response.json() + else: + deserialized = None - response = pipeline_response.http_response + if response.status_code == 412: + if response.content: + deserialized = response.json() + else: + deserialized = None - if response.status_code not in [200, 404]: - if _stream: - await response.read() # Load the body in memory and close the socket - map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore - raise HttpResponseError(response=response) + if response.status_code == 413: + if response.content: + deserialized = response.json() + else: + deserialized = None - response_headers = {} - if response.status_code == 200: - response_headers["ratelimit-limit"] = self._deserialize( - "int", response.headers.get("ratelimit-limit") - ) - response_headers["ratelimit-remaining"] = self._deserialize( - "int", response.headers.get("ratelimit-remaining") - ) - response_headers["ratelimit-reset"] = self._deserialize( - "int", response.headers.get("ratelimit-reset") + if response.status_code == 429: + response_headers["Retry-After"] = self._deserialize( + "int", response.headers.get("Retry-After") ) if response.content: @@ -11940,17 +12513,13 @@ async def get(self, ssh_key_identifier: JSON, **kwargs: Any) -> JSON: else: deserialized = None - if response.status_code == 404: - response_headers["ratelimit-limit"] = self._deserialize( - "int", response.headers.get("ratelimit-limit") - ) - response_headers["ratelimit-remaining"] = self._deserialize( - "int", response.headers.get("ratelimit-remaining") - ) - response_headers["ratelimit-reset"] = self._deserialize( - "int", response.headers.get("ratelimit-reset") - ) + if response.status_code == 500: + if response.content: + deserialized = response.json() + else: + deserialized = None + if response.status_code == 503: if response.content: deserialized = response.json() else: @@ -11961,26 +12530,57 @@ async def get(self, ssh_key_identifier: JSON, **kwargs: Any) -> JSON: return cast(JSON, deserialized) # type: ignore + +class GatewayCodeOperations: + """ + .. warning:: + **DO NOT** instantiate this class directly. + + Instead, you should access the following operations through + :class:`~pydo.aio.GeneratedClient`'s + :attr:`gateway_code` attribute. + """ + + def __init__(self, *args, **kwargs) -> None: + input_args = list(args) + self._client = input_args.pop(0) if input_args else kwargs.pop("client") + self._config = input_args.pop(0) if input_args else kwargs.pop("config") + self._serialize = input_args.pop(0) if input_args else kwargs.pop("serializer") + self._deserialize = ( + input_args.pop(0) if input_args else kwargs.pop("deserializer") + ) + @overload - async def update( + async def run( self, - ssh_key_identifier: JSON, body: JSON, *, + x_session_id: str, content_type: str = "application/json", **kwargs: Any ) -> JSON: # pylint: disable=line-too-long - """Update an SSH Key's Name. + """Execute Code. - To update the name of an SSH key, send a PUT request to either ``/v2/account/keys/$SSH_KEY_ID`` - or ``/v2/account/keys/$SSH_KEY_FINGERPRINT``. Set the ``name`` attribute to the new name you - want to use. + Runs Python code in an ephemeral sandbox and returns its ``stdout``\\ , ``stderr``\\ , and + ``exit_code``. This is the REST form of the ``action_code`` MCP tool. Use it for computation, + parsing, and data processing, or to chain several catalog tools in one request. - :param ssh_key_identifier: Either the ID or the fingerprint of an existing SSH key. Required. - :type ssh_key_identifier: JSON - :param body: Set the ``name`` attribute to the new name you want to use. Required. + The sandbox provides ``invoke_digitalocean_tool(name, arguments)``\\ , which calls a catalog + tool and returns a tool result. Those calls follow the session's tool selection and policy. + Each request runs in a fresh sandbox, so state is not kept between requests. Execution, + including tool calls, is time limited. Code that runs too long fails with a ``timeout`` error. + + A nonzero exit code is still a ``succeeded`` result; check ``output.exit_code``. If the session + policy requires approval for ``action_code``\\ , the response is a failed result with + ``_meta.status`` set to ``requires_approval``. + + :param body: Required. :type body: JSON + :keyword x_session_id: The session to act in: the UUID at the end of the session's + ``sessionUrn`` and ``mcpUrl``. The session must belong to the authenticated user and team. + Required. + :paramtype x_session_id: str :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". :paramtype content_type: str @@ -11993,26 +12593,72 @@ async def update( # JSON input template you can fill out and use as your body input. body = { - "name": "str" # Optional. A human-readable display name for this key, used - to easily identify the SSH keys when they are displayed. + "code": "str" # Python source to run. The sandbox provides + ``invoke_digitalocean_tool(name: str, arguments: dict) -> dict``"" , which calls + a catalog tool under the session's policy and returns a tool result. Required. } # response body for status code(s): 200 response == { - "ssh_key": { - "name": "str", # A human-readable display name for this key, used to - easily identify the SSH keys when they are displayed. Required. - "public_key": "str", # The entire public key string that was - uploaded. Embedded into the root user's ``authorized_keys`` file if you - include this key during Droplet creation. Required. - "fingerprint": "str", # Optional. A unique identifier that - differentiates this key from other keys using a format that SSH recognizes. - The fingerprint is created when the key is added to your account. - "id": 0 # Optional. A unique identification number for this key. Can - be used to embed a specific SSH key into a Droplet. + "call_id": "str", # A unique identifier for this call. Include it when + contacting support. Required. + "status": "str", # Required. Known values are: "succeeded" and "failed". + "_meta": { + "str": {} # Optional. Additional result metadata. Keys include: * + ``status``"" : ``requires_approval``"" , ``requires_authorization``"" , or + ``requires_connection_configuration`` when the call stopped before running. + * ``approval_id``"" , ``tool_name``"" , ``expires_at``"" , and + ``decide``"" : present with ``requires_approval``. ``decide`` gives the + ``method``"" , ``path``"" , and ``body`` of the approval request to send. + Approve the call, then repeat it with the same arguments before + ``expires_at``. * `retry`: `{"attempt_count": N, "exhausted": bool}` when + the gateway retried the call. * ``output_view``"" : the output view + applied to the result. + }, + "error": { + "class": "str", # Optional. The failure class. Branch on this value + rather than on ``message``"" , which may change. Known values are: + "invalid_argument", "unauthorized", "forbidden", "rate_limited", "not_found", + "timeout", "upstream_error", "output_too_large", "execution_failed", + "unavailable", "canceled", "render_required", "unsupported_media_type", and + "view_materialization_failed". + "message": "str", # Optional. A human-readable explanation that is + safe to show to an agent or end user. + "recovery_hint": "str", # Optional. The suggested next step. Known + values are: "fix_args", "refresh_auth", "retry_later", "narrow_output", + "contact_support", and "verify_provider_state". + "retriable": bool # Optional. Whether retrying the same request + later can succeed. + }, + "output": { + "exit_code": 0, # Optional. The process exit code. A nonzero exit + code is still a succeeded result. + "stderr": "str", # Optional. Omitted when empty. + "stdout": "str" # Optional. } } - # response body for status code(s): 404 + # response body for status code(s): 400, 412, 413, 429, 500, 503, 504 + response == { + "call_id": "str", # A unique identifier for this request, when one was + assigned. Required. + "error": { + "class": "str", # Optional. The failure class. Branch on this value + rather than on ``message``"" , which may change. Known values are: + "invalid_argument", "unauthorized", "forbidden", "rate_limited", "not_found", + "timeout", "upstream_error", "output_too_large", "execution_failed", + "unavailable", "canceled", "render_required", "unsupported_media_type", and + "view_materialization_failed". + "message": "str", # Optional. A human-readable explanation that is + safe to show to an agent or end user. + "recovery_hint": "str", # Optional. The suggested next step. Known + values are: "fix_args", "refresh_auth", "retry_later", "narrow_output", + "contact_support", and "verify_provider_state". + "retriable": bool # Optional. Whether retrying the same request + later can succeed. + }, + "status": "str" # Required. "failed" + } + # response body for status code(s): 401 response == { "id": "str", # A short identifier corresponding to the HTTP status code returned. For example, the ID for a response returning a 404 status code would @@ -12026,25 +12672,36 @@ async def update( """ @overload - async def update( + async def run( self, - ssh_key_identifier: JSON, body: IO[bytes], *, + x_session_id: str, content_type: str = "application/json", **kwargs: Any ) -> JSON: # pylint: disable=line-too-long - """Update an SSH Key's Name. + """Execute Code. - To update the name of an SSH key, send a PUT request to either ``/v2/account/keys/$SSH_KEY_ID`` - or ``/v2/account/keys/$SSH_KEY_FINGERPRINT``. Set the ``name`` attribute to the new name you - want to use. + Runs Python code in an ephemeral sandbox and returns its ``stdout``\\ , ``stderr``\\ , and + ``exit_code``. This is the REST form of the ``action_code`` MCP tool. Use it for computation, + parsing, and data processing, or to chain several catalog tools in one request. - :param ssh_key_identifier: Either the ID or the fingerprint of an existing SSH key. Required. - :type ssh_key_identifier: JSON - :param body: Set the ``name`` attribute to the new name you want to use. Required. + The sandbox provides ``invoke_digitalocean_tool(name, arguments)``\\ , which calls a catalog + tool and returns a tool result. Those calls follow the session's tool selection and policy. + Each request runs in a fresh sandbox, so state is not kept between requests. Execution, + including tool calls, is time limited. Code that runs too long fails with a ``timeout`` error. + + A nonzero exit code is still a ``succeeded`` result; check ``output.exit_code``. If the session + policy requires approval for ``action_code``\\ , the response is a failed result with + ``_meta.status`` set to ``requires_approval``. + + :param body: Required. :type body: IO[bytes] + :keyword x_session_id: The session to act in: the UUID at the end of the session's + ``sessionUrn`` and ``mcpUrl``. The session must belong to the authenticated user and team. + Required. + :paramtype x_session_id: str :keyword content_type: Body Parameter content-type. Content type parameter for binary body. Default value is "application/json". :paramtype content_type: str @@ -12057,20 +12714,65 @@ async def update( # response body for status code(s): 200 response == { - "ssh_key": { - "name": "str", # A human-readable display name for this key, used to - easily identify the SSH keys when they are displayed. Required. - "public_key": "str", # The entire public key string that was - uploaded. Embedded into the root user's ``authorized_keys`` file if you - include this key during Droplet creation. Required. - "fingerprint": "str", # Optional. A unique identifier that - differentiates this key from other keys using a format that SSH recognizes. - The fingerprint is created when the key is added to your account. - "id": 0 # Optional. A unique identification number for this key. Can - be used to embed a specific SSH key into a Droplet. + "call_id": "str", # A unique identifier for this call. Include it when + contacting support. Required. + "status": "str", # Required. Known values are: "succeeded" and "failed". + "_meta": { + "str": {} # Optional. Additional result metadata. Keys include: * + ``status``"" : ``requires_approval``"" , ``requires_authorization``"" , or + ``requires_connection_configuration`` when the call stopped before running. + * ``approval_id``"" , ``tool_name``"" , ``expires_at``"" , and + ``decide``"" : present with ``requires_approval``. ``decide`` gives the + ``method``"" , ``path``"" , and ``body`` of the approval request to send. + Approve the call, then repeat it with the same arguments before + ``expires_at``. * `retry`: `{"attempt_count": N, "exhausted": bool}` when + the gateway retried the call. * ``output_view``"" : the output view + applied to the result. + }, + "error": { + "class": "str", # Optional. The failure class. Branch on this value + rather than on ``message``"" , which may change. Known values are: + "invalid_argument", "unauthorized", "forbidden", "rate_limited", "not_found", + "timeout", "upstream_error", "output_too_large", "execution_failed", + "unavailable", "canceled", "render_required", "unsupported_media_type", and + "view_materialization_failed". + "message": "str", # Optional. A human-readable explanation that is + safe to show to an agent or end user. + "recovery_hint": "str", # Optional. The suggested next step. Known + values are: "fix_args", "refresh_auth", "retry_later", "narrow_output", + "contact_support", and "verify_provider_state". + "retriable": bool # Optional. Whether retrying the same request + later can succeed. + }, + "output": { + "exit_code": 0, # Optional. The process exit code. A nonzero exit + code is still a succeeded result. + "stderr": "str", # Optional. Omitted when empty. + "stdout": "str" # Optional. } } - # response body for status code(s): 404 + # response body for status code(s): 400, 412, 413, 429, 500, 503, 504 + response == { + "call_id": "str", # A unique identifier for this request, when one was + assigned. Required. + "error": { + "class": "str", # Optional. The failure class. Branch on this value + rather than on ``message``"" , which may change. Known values are: + "invalid_argument", "unauthorized", "forbidden", "rate_limited", "not_found", + "timeout", "upstream_error", "output_too_large", "execution_failed", + "unavailable", "canceled", "render_required", "unsupported_media_type", and + "view_materialization_failed". + "message": "str", # Optional. A human-readable explanation that is + safe to show to an agent or end user. + "recovery_hint": "str", # Optional. The suggested next step. Known + values are: "fix_args", "refresh_auth", "retry_later", "narrow_output", + "contact_support", and "verify_provider_state". + "retriable": bool # Optional. Whether retrying the same request + later can succeed. + }, + "status": "str" # Required. "failed" + } + # response body for status code(s): 401 response == { "id": "str", # A short identifier corresponding to the HTTP status code returned. For example, the ID for a response returning a 404 status code would @@ -12084,21 +12786,31 @@ async def update( """ @distributed_trace_async - async def update( - self, ssh_key_identifier: JSON, body: Union[JSON, IO[bytes]], **kwargs: Any + async def run( + self, body: Union[JSON, IO[bytes]], *, x_session_id: str, **kwargs: Any ) -> JSON: # pylint: disable=line-too-long - """Update an SSH Key's Name. + """Execute Code. - To update the name of an SSH key, send a PUT request to either ``/v2/account/keys/$SSH_KEY_ID`` - or ``/v2/account/keys/$SSH_KEY_FINGERPRINT``. Set the ``name`` attribute to the new name you - want to use. + Runs Python code in an ephemeral sandbox and returns its ``stdout``\\ , ``stderr``\\ , and + ``exit_code``. This is the REST form of the ``action_code`` MCP tool. Use it for computation, + parsing, and data processing, or to chain several catalog tools in one request. - :param ssh_key_identifier: Either the ID or the fingerprint of an existing SSH key. Required. - :type ssh_key_identifier: JSON - :param body: Set the ``name`` attribute to the new name you want to use. Is either a JSON type - or a IO[bytes] type. Required. + The sandbox provides ``invoke_digitalocean_tool(name, arguments)``\\ , which calls a catalog + tool and returns a tool result. Those calls follow the session's tool selection and policy. + Each request runs in a fresh sandbox, so state is not kept between requests. Execution, + including tool calls, is time limited. Code that runs too long fails with a ``timeout`` error. + + A nonzero exit code is still a ``succeeded`` result; check ``output.exit_code``. If the session + policy requires approval for ``action_code``\\ , the response is a failed result with + ``_meta.status`` set to ``requires_approval``. + + :param body: Is either a JSON type or a IO[bytes] type. Required. :type body: JSON or IO[bytes] + :keyword x_session_id: The session to act in: the UUID at the end of the session's + ``sessionUrn`` and ``mcpUrl``. The session must belong to the authenticated user and team. + Required. + :paramtype x_session_id: str :return: JSON object :rtype: JSON :raises ~azure.core.exceptions.HttpResponseError: @@ -12108,26 +12820,72 @@ async def update( # JSON input template you can fill out and use as your body input. body = { - "name": "str" # Optional. A human-readable display name for this key, used - to easily identify the SSH keys when they are displayed. + "code": "str" # Python source to run. The sandbox provides + ``invoke_digitalocean_tool(name: str, arguments: dict) -> dict``"" , which calls + a catalog tool under the session's policy and returns a tool result. Required. } # response body for status code(s): 200 response == { - "ssh_key": { - "name": "str", # A human-readable display name for this key, used to - easily identify the SSH keys when they are displayed. Required. - "public_key": "str", # The entire public key string that was - uploaded. Embedded into the root user's ``authorized_keys`` file if you - include this key during Droplet creation. Required. - "fingerprint": "str", # Optional. A unique identifier that - differentiates this key from other keys using a format that SSH recognizes. - The fingerprint is created when the key is added to your account. - "id": 0 # Optional. A unique identification number for this key. Can - be used to embed a specific SSH key into a Droplet. + "call_id": "str", # A unique identifier for this call. Include it when + contacting support. Required. + "status": "str", # Required. Known values are: "succeeded" and "failed". + "_meta": { + "str": {} # Optional. Additional result metadata. Keys include: * + ``status``"" : ``requires_approval``"" , ``requires_authorization``"" , or + ``requires_connection_configuration`` when the call stopped before running. + * ``approval_id``"" , ``tool_name``"" , ``expires_at``"" , and + ``decide``"" : present with ``requires_approval``. ``decide`` gives the + ``method``"" , ``path``"" , and ``body`` of the approval request to send. + Approve the call, then repeat it with the same arguments before + ``expires_at``. * `retry`: `{"attempt_count": N, "exhausted": bool}` when + the gateway retried the call. * ``output_view``"" : the output view + applied to the result. + }, + "error": { + "class": "str", # Optional. The failure class. Branch on this value + rather than on ``message``"" , which may change. Known values are: + "invalid_argument", "unauthorized", "forbidden", "rate_limited", "not_found", + "timeout", "upstream_error", "output_too_large", "execution_failed", + "unavailable", "canceled", "render_required", "unsupported_media_type", and + "view_materialization_failed". + "message": "str", # Optional. A human-readable explanation that is + safe to show to an agent or end user. + "recovery_hint": "str", # Optional. The suggested next step. Known + values are: "fix_args", "refresh_auth", "retry_later", "narrow_output", + "contact_support", and "verify_provider_state". + "retriable": bool # Optional. Whether retrying the same request + later can succeed. + }, + "output": { + "exit_code": 0, # Optional. The process exit code. A nonzero exit + code is still a succeeded result. + "stderr": "str", # Optional. Omitted when empty. + "stdout": "str" # Optional. } } - # response body for status code(s): 404 + # response body for status code(s): 400, 412, 413, 429, 500, 503, 504 + response == { + "call_id": "str", # A unique identifier for this request, when one was + assigned. Required. + "error": { + "class": "str", # Optional. The failure class. Branch on this value + rather than on ``message``"" , which may change. Known values are: + "invalid_argument", "unauthorized", "forbidden", "rate_limited", "not_found", + "timeout", "upstream_error", "output_too_large", "execution_failed", + "unavailable", "canceled", "render_required", "unsupported_media_type", and + "view_materialization_failed". + "message": "str", # Optional. A human-readable explanation that is + safe to show to an agent or end user. + "recovery_hint": "str", # Optional. The suggested next step. Known + values are: "fix_args", "refresh_auth", "retry_later", "narrow_output", + "contact_support", and "verify_provider_state". + "retriable": bool # Optional. Whether retrying the same request + later can succeed. + }, + "status": "str" # Required. "failed" + } + # response body for status code(s): 401 response == { "id": "str", # A short identifier corresponding to the HTTP status code returned. For example, the ID for a response returning a 404 status code would @@ -12140,15 +12898,10 @@ async def update( } """ error_map: MutableMapping[int, Type[HttpResponseError]] = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError, 304: ResourceNotModifiedError, - 401: cast( - Type[HttpResponseError], - lambda response: ClientAuthenticationError(response=response), - ), - 429: HttpResponseError, - 500: HttpResponseError, } error_map.update(kwargs.pop("error_map", {}) or {}) @@ -12168,8 +12921,8 @@ async def update( else: _json = body - _request = build_ssh_keys_update_request( - ssh_key_identifier=ssh_key_identifier, + _request = build_gateway_code_run_request( + x_session_id=x_session_id, content_type=content_type, json=_json, content=_content, @@ -12187,164 +12940,104 @@ async def update( response = pipeline_response.http_response - if response.status_code not in [200, 404]: + if response.status_code not in [ + 200, + 400, + 401, + 403, + 412, + 413, + 429, + 500, + 503, + 504, + ]: if _stream: await response.read() # Load the body in memory and close the socket - map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + map_error( + status_code=response.status_code, response=response, error_map=error_map + ) raise HttpResponseError(response=response) response_headers = {} if response.status_code == 200: - response_headers["ratelimit-limit"] = self._deserialize( - "int", response.headers.get("ratelimit-limit") - ) - response_headers["ratelimit-remaining"] = self._deserialize( - "int", response.headers.get("ratelimit-remaining") - ) - response_headers["ratelimit-reset"] = self._deserialize( - "int", response.headers.get("ratelimit-reset") - ) - if response.content: deserialized = response.json() else: deserialized = None - if response.status_code == 404: - response_headers["ratelimit-limit"] = self._deserialize( - "int", response.headers.get("ratelimit-limit") - ) - response_headers["ratelimit-remaining"] = self._deserialize( - "int", response.headers.get("ratelimit-remaining") - ) - response_headers["ratelimit-reset"] = self._deserialize( - "int", response.headers.get("ratelimit-reset") - ) - + if response.status_code == 400: if response.content: deserialized = response.json() else: deserialized = None - if cls: - return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore - - return cast(JSON, deserialized) # type: ignore - - @distributed_trace_async - async def delete(self, ssh_key_identifier: JSON, **kwargs: Any) -> Optional[JSON]: - # pylint: disable=line-too-long - """Delete an SSH Key. - - To destroy a public SSH key that you have in your account, send a DELETE request to - ``/v2/account/keys/$KEY_ID`` or ``/v2/account/keys/$KEY_FINGERPRINT``. - A 204 status will be returned, indicating that the action was successful and that the response - body is empty. - - :param ssh_key_identifier: Either the ID or the fingerprint of an existing SSH key. Required. - :type ssh_key_identifier: JSON - :return: JSON object or None - :rtype: JSON or None - :raises ~azure.core.exceptions.HttpResponseError: - - Example: - .. code-block:: python - - # response body for status code(s): 404 - response == { - "id": "str", # A short identifier corresponding to the HTTP status code - returned. For example, the ID for a response returning a 404 status code would - be "not_found.". Required. - "message": "str", # A message providing additional information about the - error, including details to help resolve it when possible. Required. - "request_id": "str" # Optional. Optionally, some endpoints may include a - request ID that should be provided when reporting bugs or opening support - tickets to help identify the issue. - } - """ - error_map: MutableMapping[int, Type[HttpResponseError]] = { - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - 401: cast( - Type[HttpResponseError], - lambda response: ClientAuthenticationError(response=response), - ), - 429: HttpResponseError, - 500: HttpResponseError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) + if response.status_code == 401: + if response.content: + deserialized = response.json() + else: + deserialized = None - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} + if response.status_code == 403: + if response.content: + deserialized = response.json() + else: + deserialized = None - cls: ClsType[Optional[JSON]] = kwargs.pop("cls", None) + if response.status_code == 412: + if response.content: + deserialized = response.json() + else: + deserialized = None - _request = build_ssh_keys_delete_request( - ssh_key_identifier=ssh_key_identifier, - headers=_headers, - params=_params, - ) - _request.url = self._client.format_url(_request.url) + if response.status_code == 413: + if response.content: + deserialized = response.json() + else: + deserialized = None - _stream = False - pipeline_response: PipelineResponse = ( - await self._client._pipeline.run( # pylint: disable=protected-access - _request, stream=_stream, **kwargs + if response.status_code == 429: + response_headers["Retry-After"] = self._deserialize( + "int", response.headers.get("Retry-After") ) - ) - - response = pipeline_response.http_response - if response.status_code not in [204, 404]: - if _stream: - await response.read() # Load the body in memory and close the socket - map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore - raise HttpResponseError(response=response) + if response.content: + deserialized = response.json() + else: + deserialized = None - deserialized = None - response_headers = {} - if response.status_code == 204: - response_headers["ratelimit-limit"] = self._deserialize( - "int", response.headers.get("ratelimit-limit") - ) - response_headers["ratelimit-remaining"] = self._deserialize( - "int", response.headers.get("ratelimit-remaining") - ) - response_headers["ratelimit-reset"] = self._deserialize( - "int", response.headers.get("ratelimit-reset") - ) + if response.status_code == 500: + if response.content: + deserialized = response.json() + else: + deserialized = None - if response.status_code == 404: - response_headers["ratelimit-limit"] = self._deserialize( - "int", response.headers.get("ratelimit-limit") - ) - response_headers["ratelimit-remaining"] = self._deserialize( - "int", response.headers.get("ratelimit-remaining") - ) - response_headers["ratelimit-reset"] = self._deserialize( - "int", response.headers.get("ratelimit-reset") - ) + if response.status_code == 503: + if response.content: + deserialized = response.json() + else: + deserialized = None + if response.status_code == 504: if response.content: deserialized = response.json() else: deserialized = None if cls: - return cls(pipeline_response, deserialized, response_headers) # type: ignore + return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore - return deserialized # type: ignore + return cast(JSON, deserialized) # type: ignore -class ActionsOperations: +class GatewaySessionApprovalsOperations: """ .. warning:: **DO NOT** instantiate this class directly. Instead, you should access the following operations through :class:`~pydo.aio.GeneratedClient`'s - :attr:`actions` attribute. + :attr:`gateway_session_approvals` attribute. """ def __init__(self, *args, **kwargs) -> None: @@ -12356,19 +13049,40 @@ def __init__(self, *args, **kwargs) -> None: input_args.pop(0) if input_args else kwargs.pop("deserializer") ) - @distributed_trace_async - async def list(self, *, per_page: int = 20, page: int = 1, **kwargs: Any) -> JSON: + @overload + async def update( + self, + session_id: str, + approval_id: str, + body: JSON, + *, + content_type: str = "application/json", + **kwargs: Any + ) -> JSON: # pylint: disable=line-too-long - """List All Actions. + """Decide a Pending Approval. - This will be the entire list of actions taken on your account, so it will be quite large. As - with any large collection returned by the API, the results will be paginated with only 20 on - each page by default. + Approves or denies a tool call that is waiting for approval. A tool result returns + ``_meta.status`` set to ``requires_approval`` when the session policy sets ``ask`` for the + call, and its ``_meta.decide`` gives this request's path. - :keyword per_page: Number of items returned per page. Default value is 20. - :paramtype per_page: int - :keyword page: Which 'page' of paginated results to return. Default value is 1. - :paramtype page: int + Send the decision with the session owner's token. Approval is a separate control request, not a + tool, so an agent cannot approve its own calls. After approving, repeat the original call with + the same arguments before ``expires_at``. An approval applies only to that session, tool + version, and set of arguments, and is used once. To ask again after a denial or expiry, repeat + the tool call. + + :param session_id: The session UUID: the value at the end of the session's ``sessionUrn`` and + ``mcpUrl``. The session must belong to the authenticated user and team. Required. + :type session_id: str + :param approval_id: The ``approval_id`` from a tool result whose ``_meta.status`` is + ``requires_approval``. Required. + :type approval_id: str + :param body: Required. + :type body: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str :return: JSON object :rtype: JSON :raises ~azure.core.exceptions.HttpResponseError: @@ -12376,132 +13090,164 @@ async def list(self, *, per_page: int = 20, page: int = 1, **kwargs: Any) -> JSO Example: .. code-block:: python + # JSON input template you can fill out and use as your body input. + body = { + "decision": "str" # Whether to let the pending call run. Required. Known + values are: "approve" and "deny". + } + # response body for status code(s): 200 response == { - "meta": { - "total": 0 # Optional. Number of objects returned by the request. + "expires_at": "2020-02-20 00:00:00", # Optional. Repeat an approved call + with the same arguments before this time. + "id": "str", # Optional. The approval ID. + "status": "str" # Optional. Known values are: "approved" and "denied". + } + # response body for status code(s): 400, 404, 409, 412, 429, 500, 503 + response == { + "call_id": "str", # A unique identifier for this request, when one was + assigned. Required. + "error": { + "class": "str", # Optional. The failure class. Branch on this value + rather than on ``message``"" , which may change. Known values are: + "invalid_argument", "unauthorized", "forbidden", "rate_limited", "not_found", + "timeout", "upstream_error", "output_too_large", "execution_failed", + "unavailable", "canceled", "render_required", "unsupported_media_type", and + "view_materialization_failed". + "message": "str", # Optional. A human-readable explanation that is + safe to show to an agent or end user. + "recovery_hint": "str", # Optional. The suggested next step. Known + values are: "fix_args", "refresh_auth", "retry_later", "narrow_output", + "contact_support", and "verify_provider_state". + "retriable": bool # Optional. Whether retrying the same request + later can succeed. }, - "actions": [ - { - "completed_at": "2020-02-20 00:00:00", # Optional. A time - value given in ISO8601 combined date and time format that represents when - the action was completed. - "id": 0, # Optional. A unique numeric ID that can be used to - identify and reference an action. - "region": { - "available": bool, # This is a boolean value that - represents whether new Droplets can be created in this region. - Required. - "features": [ - "str" # This attribute is set to an array - which contains features available in this region. Required. - ], - "name": "str", # The display name of the region. - This will be a full name that is used in the control panel and other - interfaces. Required. - "sizes": [ - "str" # This attribute is set to an array - which contains the identifying slugs for the sizes available in - this region. sizes:read is required to view. Required. - ], - "slug": "str" # A human-readable string that is used - as a unique identifier for each region. Required. - }, - "region_slug": "str", # Optional. A human-readable string - that is used as a unique identifier for each region. - "resource_id": 0, # Optional. A unique identifier for the - resource that the action is associated with. - "resource_type": "str", # Optional. The type of resource - that the action is associated with. - "started_at": "2020-02-20 00:00:00", # Optional. A time - value given in ISO8601 combined date and time format that represents when - the action was initiated. - "status": "in-progress", # Optional. Default value is - "in-progress". The current status of the action. This can be - "in-progress", "completed", or "errored". Known values are: - "in-progress", "completed", and "errored". - "type": "str" # Optional. This is the type of action that - the object represents. For example, this could be "transfer" to represent - the state of an image transfer action. - } - ], - "links": { - "pages": {} - } + "status": "str" # Required. "failed" + } + # response body for status code(s): 401 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. } """ - error_map: MutableMapping[int, Type[HttpResponseError]] = { - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - 401: cast( - Type[HttpResponseError], - lambda response: ClientAuthenticationError(response=response), - ), - 429: HttpResponseError, - 500: HttpResponseError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) - - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} - - cls: ClsType[JSON] = kwargs.pop("cls", None) - - _request = build_actions_list_request( - per_page=per_page, - page=page, - headers=_headers, - params=_params, - ) - _request.url = self._client.format_url(_request.url) - - _stream = False - pipeline_response: PipelineResponse = ( - await self._client._pipeline.run( # pylint: disable=protected-access - _request, stream=_stream, **kwargs - ) - ) - response = pipeline_response.http_response + @overload + async def update( + self, + session_id: str, + approval_id: str, + body: IO[bytes], + *, + content_type: str = "application/json", + **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """Decide a Pending Approval. - if response.status_code not in [200]: - if _stream: - await response.read() # Load the body in memory and close the socket - map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore - raise HttpResponseError(response=response) + Approves or denies a tool call that is waiting for approval. A tool result returns + ``_meta.status`` set to ``requires_approval`` when the session policy sets ``ask`` for the + call, and its ``_meta.decide`` gives this request's path. - response_headers = {} - response_headers["ratelimit-limit"] = self._deserialize( - "int", response.headers.get("ratelimit-limit") - ) - response_headers["ratelimit-remaining"] = self._deserialize( - "int", response.headers.get("ratelimit-remaining") - ) - response_headers["ratelimit-reset"] = self._deserialize( - "int", response.headers.get("ratelimit-reset") - ) + Send the decision with the session owner's token. Approval is a separate control request, not a + tool, so an agent cannot approve its own calls. After approving, repeat the original call with + the same arguments before ``expires_at``. An approval applies only to that session, tool + version, and set of arguments, and is used once. To ask again after a denial or expiry, repeat + the tool call. - if response.content: - deserialized = response.json() - else: - deserialized = None + :param session_id: The session UUID: the value at the end of the session's ``sessionUrn`` and + ``mcpUrl``. The session must belong to the authenticated user and team. Required. + :type session_id: str + :param approval_id: The ``approval_id`` from a tool result whose ``_meta.status`` is + ``requires_approval``. Required. + :type approval_id: str + :param body: Required. + :type body: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: - if cls: - return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore + Example: + .. code-block:: python - return cast(JSON, deserialized) # type: ignore + # response body for status code(s): 200 + response == { + "expires_at": "2020-02-20 00:00:00", # Optional. Repeat an approved call + with the same arguments before this time. + "id": "str", # Optional. The approval ID. + "status": "str" # Optional. Known values are: "approved" and "denied". + } + # response body for status code(s): 400, 404, 409, 412, 429, 500, 503 + response == { + "call_id": "str", # A unique identifier for this request, when one was + assigned. Required. + "error": { + "class": "str", # Optional. The failure class. Branch on this value + rather than on ``message``"" , which may change. Known values are: + "invalid_argument", "unauthorized", "forbidden", "rate_limited", "not_found", + "timeout", "upstream_error", "output_too_large", "execution_failed", + "unavailable", "canceled", "render_required", "unsupported_media_type", and + "view_materialization_failed". + "message": "str", # Optional. A human-readable explanation that is + safe to show to an agent or end user. + "recovery_hint": "str", # Optional. The suggested next step. Known + values are: "fix_args", "refresh_auth", "retry_later", "narrow_output", + "contact_support", and "verify_provider_state". + "retriable": bool # Optional. Whether retrying the same request + later can succeed. + }, + "status": "str" # Required. "failed" + } + # response body for status code(s): 401 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ @distributed_trace_async - async def get(self, action_id: int, **kwargs: Any) -> JSON: + async def update( + self, + session_id: str, + approval_id: str, + body: Union[JSON, IO[bytes]], + **kwargs: Any + ) -> JSON: # pylint: disable=line-too-long - """Retrieve an Existing Action. + """Decide a Pending Approval. - To retrieve a specific action object, send a GET request to ``/v2/actions/$ACTION_ID``. + Approves or denies a tool call that is waiting for approval. A tool result returns + ``_meta.status`` set to ``requires_approval`` when the session policy sets ``ask`` for the + call, and its ``_meta.decide`` gives this request's path. - :param action_id: A unique numeric ID that can be used to identify and reference an action. - Required. - :type action_id: int + Send the decision with the session owner's token. Approval is a separate control request, not a + tool, so an agent cannot approve its own calls. After approving, repeat the original call with + the same arguments before ``expires_at``. An approval applies only to that session, tool + version, and set of arguments, and is used once. To ask again after a denial or expiry, repeat + the tool call. + + :param session_id: The session UUID: the value at the end of the session's ``sessionUrn`` and + ``mcpUrl``. The session must belong to the authenticated user and team. Required. + :type session_id: str + :param approval_id: The ``approval_id`` from a tool result whose ``_meta.status`` is + ``requires_approval``. Required. + :type approval_id: str + :param body: Is either a JSON type or a IO[bytes] type. Required. + :type body: JSON or IO[bytes] :return: JSON object :rtype: JSON :raises ~azure.core.exceptions.HttpResponseError: @@ -12509,50 +13255,41 @@ async def get(self, action_id: int, **kwargs: Any) -> JSON: Example: .. code-block:: python + # JSON input template you can fill out and use as your body input. + body = { + "decision": "str" # Whether to let the pending call run. Required. Known + values are: "approve" and "deny". + } + # response body for status code(s): 200 response == { - "action": { - "completed_at": "2020-02-20 00:00:00", # Optional. A time value - given in ISO8601 combined date and time format that represents when the - action was completed. - "id": 0, # Optional. A unique numeric ID that can be used to - identify and reference an action. - "region": { - "available": bool, # This is a boolean value that represents - whether new Droplets can be created in this region. Required. - "features": [ - "str" # This attribute is set to an array which - contains features available in this region. Required. - ], - "name": "str", # The display name of the region. This will - be a full name that is used in the control panel and other interfaces. - Required. - "sizes": [ - "str" # This attribute is set to an array which - contains the identifying slugs for the sizes available in this - region. sizes:read is required to view. Required. - ], - "slug": "str" # A human-readable string that is used as a - unique identifier for each region. Required. - }, - "region_slug": "str", # Optional. A human-readable string that is - used as a unique identifier for each region. - "resource_id": 0, # Optional. A unique identifier for the resource - that the action is associated with. - "resource_type": "str", # Optional. The type of resource that the - action is associated with. - "started_at": "2020-02-20 00:00:00", # Optional. A time value given - in ISO8601 combined date and time format that represents when the action was - initiated. - "status": "in-progress", # Optional. Default value is "in-progress". - The current status of the action. This can be "in-progress", "completed", or - "errored". Known values are: "in-progress", "completed", and "errored". - "type": "str" # Optional. This is the type of action that the object - represents. For example, this could be "transfer" to represent the state of - an image transfer action. - } + "expires_at": "2020-02-20 00:00:00", # Optional. Repeat an approved call + with the same arguments before this time. + "id": "str", # Optional. The approval ID. + "status": "str" # Optional. Known values are: "approved" and "denied". } - # response body for status code(s): 404 + # response body for status code(s): 400, 404, 409, 412, 429, 500, 503 + response == { + "call_id": "str", # A unique identifier for this request, when one was + assigned. Required. + "error": { + "class": "str", # Optional. The failure class. Branch on this value + rather than on ``message``"" , which may change. Known values are: + "invalid_argument", "unauthorized", "forbidden", "rate_limited", "not_found", + "timeout", "upstream_error", "output_too_large", "execution_failed", + "unavailable", "canceled", "render_required", "unsupported_media_type", and + "view_materialization_failed". + "message": "str", # Optional. A human-readable explanation that is + safe to show to an agent or end user. + "recovery_hint": "str", # Optional. The suggested next step. Known + values are: "fix_args", "refresh_auth", "retry_later", "narrow_output", + "contact_support", and "verify_provider_state". + "retriable": bool # Optional. Whether retrying the same request + later can succeed. + }, + "status": "str" # Required. "failed" + } + # response body for status code(s): 401 response == { "id": "str", # A short identifier corresponding to the HTTP status code returned. For example, the ID for a response returning a 404 status code would @@ -12565,25 +13302,35 @@ async def get(self, action_id: int, **kwargs: Any) -> JSON: } """ error_map: MutableMapping[int, Type[HttpResponseError]] = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError, 304: ResourceNotModifiedError, - 401: cast( - Type[HttpResponseError], - lambda response: ClientAuthenticationError(response=response), - ), - 429: HttpResponseError, - 500: HttpResponseError, } error_map.update(kwargs.pop("error_map", {}) or {}) - _headers = kwargs.pop("headers", {}) or {} + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = kwargs.pop("params", {}) or {} + content_type: Optional[str] = kwargs.pop( + "content_type", _headers.pop("Content-Type", None) + ) cls: ClsType[JSON] = kwargs.pop("cls", None) - _request = build_actions_get_request( - action_id=action_id, + content_type = content_type or "application/json" + _json = None + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _json = body + + _request = build_gateway_session_approvals_update_request( + session_id=session_id, + approval_id=approval_id, + content_type=content_type, + json=_json, + content=_content, headers=_headers, params=_params, ) @@ -12598,38 +13345,71 @@ async def get(self, action_id: int, **kwargs: Any) -> JSON: response = pipeline_response.http_response - if response.status_code not in [200, 404]: + if response.status_code not in [ + 200, + 400, + 401, + 403, + 404, + 409, + 412, + 429, + 500, + 503, + ]: if _stream: await response.read() # Load the body in memory and close the socket - map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + map_error( + status_code=response.status_code, response=response, error_map=error_map + ) raise HttpResponseError(response=response) response_headers = {} if response.status_code == 200: - response_headers["ratelimit-limit"] = self._deserialize( - "int", response.headers.get("ratelimit-limit") - ) - response_headers["ratelimit-remaining"] = self._deserialize( - "int", response.headers.get("ratelimit-remaining") - ) - response_headers["ratelimit-reset"] = self._deserialize( - "int", response.headers.get("ratelimit-reset") - ) + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 400: + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 401: + if response.content: + deserialized = response.json() + else: + deserialized = None + if response.status_code == 403: if response.content: deserialized = response.json() else: deserialized = None if response.status_code == 404: - response_headers["ratelimit-limit"] = self._deserialize( - "int", response.headers.get("ratelimit-limit") - ) - response_headers["ratelimit-remaining"] = self._deserialize( - "int", response.headers.get("ratelimit-remaining") - ) - response_headers["ratelimit-reset"] = self._deserialize( - "int", response.headers.get("ratelimit-reset") + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 409: + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 412: + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 429: + response_headers["Retry-After"] = self._deserialize( + "int", response.headers.get("Retry-After") ) if response.content: @@ -12637,20 +13417,32 @@ async def get(self, action_id: int, **kwargs: Any) -> JSON: else: deserialized = None + if response.status_code == 500: + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 503: + if response.content: + deserialized = response.json() + else: + deserialized = None + if cls: return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore return cast(JSON, deserialized) # type: ignore -class AddonsOperations: +class GatewayApprovalsOperations: """ .. warning:: **DO NOT** instantiate this class directly. Instead, you should access the following operations through :class:`~pydo.aio.GeneratedClient`'s - :attr:`addons` attribute. + :attr:`gateway_approvals` attribute. """ def __init__(self, *args, **kwargs) -> None: @@ -12662,14 +13454,36 @@ def __init__(self, *args, **kwargs) -> None: input_args.pop(0) if input_args else kwargs.pop("deserializer") ) - @distributed_trace_async - async def get_app(self, **kwargs: Any) -> JSON: + @overload + async def update( + self, + approval_id: str, + body: JSON, + *, + x_session_id: str, + content_type: str = "application/json", + **kwargs: Any + ) -> JSON: # pylint: disable=line-too-long - """List Available Add-On Applications. + """Decide a Pending Approval by Session Header. - To fetch details of all available Add-On Applications, send a GET request to - ``/v2/add-ons/apps``. + Approves or denies a tool call that is waiting for approval, identifying the session with the + ``X-Session-Id`` header. It behaves the same as ``POST + /approvals/session/{session_id}/{approval_id}``\\ , which is the path returned in a result's + ``_meta.decide``. + :param approval_id: The ``approval_id`` from a tool result whose ``_meta.status`` is + ``requires_approval``. Required. + :type approval_id: str + :param body: Required. + :type body: JSON + :keyword x_session_id: The session to act in: the UUID at the end of the session's + ``sessionUrn`` and ``mcpUrl``. The session must belong to the authenticated user and team. + Required. + :paramtype x_session_id: str + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str :return: JSON object :rtype: JSON :raises ~azure.core.exceptions.HttpResponseError: @@ -12677,176 +13491,157 @@ async def get_app(self, **kwargs: Any) -> JSON: Example: .. code-block:: python + # JSON input template you can fill out and use as your body input. + body = { + "decision": "str" # Whether to let the pending call run. Required. Known + values are: "approve" and "deny". + } + # response body for status code(s): 200 response == { - "apps": [ - { - "app_slug": "str", # The slug identifier for the application - associated with the resource. Required. - "eula": "str", # The End User License Agreement URL for the - resource. Required. - "plans": [ - { - "active": bool, # Indicates if the plan is - currently active. Required. - "app_id": 0, # ID of the app associated with - this plan. Required. - "available": bool, # Indicates if the plan - is available for selection. Required. - "by_default": bool, # Indicates if this plan - is the default option for the app. Required. - "created_at": "2020-02-20 00:00:00", # - Timestamp when the plan was created. Required. - "display_name": "str", # Display name for a - given plan. Required. - "id": 0, # ID of a given plan. Required. - "price_per_month": 0, # Price of a month's - usage of the plan in US dollars. Required. - "slug": "str", # Slug identifier for the - plan. Required. - "state": "str", # Current state of the plan. - Required. Known values are: "unknown", "draft", "in_review", - "approved", "suspended", and "archived". - "updated_at": "2020-02-20 00:00:00", # - Timestamp when the plan was last updated. Required. - "uuid": "str", # Unique identifier for the - plan. Required. - "description": "str", # Optional. - Description of an app plan. - "dimensions": [ - { - "display_name": "str", # - Display name for the dimension. Required. - "feature_name": "str", # - Name of the feature associated with the dimension. - Required. - "id": 0, # Unique identifier - for the dimension. Required. - "sku": "str", # Unique - string identifier for the dimension, tied to a price. - Required. - "slug": "str", # Slug - identifier for the dimension. Required. - "volumes": [ - { - "id": 0, # - Unique identifier for the addon. Required. - "low_volume": - 0, # The minimum volume for the volume pricing - tier. Required. - "max_volume": - 0, # The maximum volume for the volume pricing - tier. Required. - "price_per_unit": "str" # The price per unit for - the volume tier in US dollars. Required. - } - ] - } - ], - "features": [ - { - "created_at": "2020-02-20 - 00:00:00", # Timestamp when the feature was created. - Required. - "id": 0, # Unique identifier - for the app feature. Required. - "name": "str", # Name of the - feature. Required. - "type": "str", # Feature - type, indicating the kind of data it holds. Required. - Known values are: "unknown", "string", "boolean", and - "allowance". - "updated_at": "2020-02-20 - 00:00:00", # Timestamp when the feature was last - updated. Required. - "value": {}, - "unit": "str" # Optional. - Unit of measurement for the feature, if applicable. Units - apply to allowance features. Known values are: - "unit_unknown", "GB", "GIB", "count", "byte", and - "byte_second". - } - ] - } - ], - "tos": "str" # The Terms of Service URL for the resource. - Required. - } - ] + "expires_at": "2020-02-20 00:00:00", # Optional. Repeat an approved call + with the same arguments before this time. + "id": "str", # Optional. The approval ID. + "status": "str" # Optional. Known values are: "approved" and "denied". + } + # response body for status code(s): 400, 404, 409, 412, 429, 500, 503 + response == { + "call_id": "str", # A unique identifier for this request, when one was + assigned. Required. + "error": { + "class": "str", # Optional. The failure class. Branch on this value + rather than on ``message``"" , which may change. Known values are: + "invalid_argument", "unauthorized", "forbidden", "rate_limited", "not_found", + "timeout", "upstream_error", "output_too_large", "execution_failed", + "unavailable", "canceled", "render_required", "unsupported_media_type", and + "view_materialization_failed". + "message": "str", # Optional. A human-readable explanation that is + safe to show to an agent or end user. + "recovery_hint": "str", # Optional. The suggested next step. Known + values are: "fix_args", "refresh_auth", "retry_later", "narrow_output", + "contact_support", and "verify_provider_state". + "retriable": bool # Optional. Whether retrying the same request + later can succeed. + }, + "status": "str" # Required. "failed" + } + # response body for status code(s): 401 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. } """ - error_map: MutableMapping[int, Type[HttpResponseError]] = { - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - 401: cast( - Type[HttpResponseError], - lambda response: ClientAuthenticationError(response=response), - ), - 429: HttpResponseError, - 500: HttpResponseError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) - - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} - - cls: ClsType[JSON] = kwargs.pop("cls", None) - - _request = build_addons_get_app_request( - headers=_headers, - params=_params, - ) - _request.url = self._client.format_url(_request.url) - - _stream = False - pipeline_response: PipelineResponse = ( - await self._client._pipeline.run( # pylint: disable=protected-access - _request, stream=_stream, **kwargs - ) - ) - response = pipeline_response.http_response - - if response.status_code not in [200]: - if _stream: - await response.read() # Load the body in memory and close the socket - map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore - raise HttpResponseError(response=response) + @overload + async def update( + self, + approval_id: str, + body: IO[bytes], + *, + x_session_id: str, + content_type: str = "application/json", + **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """Decide a Pending Approval by Session Header. - response_headers = {} - response_headers["ratelimit-limit"] = self._deserialize( - "int", response.headers.get("ratelimit-limit") - ) - response_headers["ratelimit-remaining"] = self._deserialize( - "int", response.headers.get("ratelimit-remaining") - ) - response_headers["ratelimit-reset"] = self._deserialize( - "int", response.headers.get("ratelimit-reset") - ) + Approves or denies a tool call that is waiting for approval, identifying the session with the + ``X-Session-Id`` header. It behaves the same as ``POST + /approvals/session/{session_id}/{approval_id}``\\ , which is the path returned in a result's + ``_meta.decide``. - if response.content: - deserialized = response.json() - else: - deserialized = None + :param approval_id: The ``approval_id`` from a tool result whose ``_meta.status`` is + ``requires_approval``. Required. + :type approval_id: str + :param body: Required. + :type body: IO[bytes] + :keyword x_session_id: The session to act in: the UUID at the end of the session's + ``sessionUrn`` and ``mcpUrl``. The session must belong to the authenticated user and team. + Required. + :paramtype x_session_id: str + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: - if cls: - return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore + Example: + .. code-block:: python - return cast(JSON, deserialized) # type: ignore + # response body for status code(s): 200 + response == { + "expires_at": "2020-02-20 00:00:00", # Optional. Repeat an approved call + with the same arguments before this time. + "id": "str", # Optional. The approval ID. + "status": "str" # Optional. Known values are: "approved" and "denied". + } + # response body for status code(s): 400, 404, 409, 412, 429, 500, 503 + response == { + "call_id": "str", # A unique identifier for this request, when one was + assigned. Required. + "error": { + "class": "str", # Optional. The failure class. Branch on this value + rather than on ``message``"" , which may change. Known values are: + "invalid_argument", "unauthorized", "forbidden", "rate_limited", "not_found", + "timeout", "upstream_error", "output_too_large", "execution_failed", + "unavailable", "canceled", "render_required", "unsupported_media_type", and + "view_materialization_failed". + "message": "str", # Optional. A human-readable explanation that is + safe to show to an agent or end user. + "recovery_hint": "str", # Optional. The suggested next step. Known + values are: "fix_args", "refresh_auth", "retry_later", "narrow_output", + "contact_support", and "verify_provider_state". + "retriable": bool # Optional. Whether retrying the same request + later can succeed. + }, + "status": "str" # Required. "failed" + } + # response body for status code(s): 401 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ @distributed_trace_async - async def get_app_metadata(self, app_slug: str, **kwargs: Any) -> JSON: + async def update( + self, + approval_id: str, + body: Union[JSON, IO[bytes]], + *, + x_session_id: str, + **kwargs: Any + ) -> JSON: # pylint: disable=line-too-long - """Get Metadata for an Add-On Application. + """Decide a Pending Approval by Session Header. - To find out what metadata is required for a specific add-on, send a GET request to - ``/v2/add-ons/apps/{app_slug}/metadata``. - Metadata varies by application. + Approves or denies a tool call that is waiting for approval, identifying the session with the + ``X-Session-Id`` header. It behaves the same as ``POST + /approvals/session/{session_id}/{approval_id}``\\ , which is the path returned in a result's + ``_meta.decide``. - :param app_slug: The slug identifier for the application whose metadata is being requested. + :param approval_id: The ``approval_id`` from a tool result whose ``_meta.status`` is + ``requires_approval``. Required. + :type approval_id: str + :param body: Is either a JSON type or a IO[bytes] type. Required. + :type body: JSON or IO[bytes] + :keyword x_session_id: The session to act in: the UUID at the end of the session's + ``sessionUrn`` and ``mcpUrl``. The session must belong to the authenticated user and team. Required. - :type app_slug: str + :paramtype x_session_id: str :return: JSON object :rtype: JSON :raises ~azure.core.exceptions.HttpResponseError: @@ -12854,26 +13649,41 @@ async def get_app_metadata(self, app_slug: str, **kwargs: Any) -> JSON: Example: .. code-block:: python + # JSON input template you can fill out and use as your body input. + body = { + "decision": "str" # Whether to let the pending call run. Required. Known + values are: "approve" and "deny". + } + # response body for status code(s): 200 response == { - "metadata": [ - { - "description": "str", # A brief description of the metadata - item. Required. - "display_name": "str", # The display name of the metadata - item. Required. - "id": 0, # Unique identifier for the addon metadata item. - Required. - "name": "str", # The name of the metadata item. Required. - "type": "str", # The data type of the metadata value. - Required. Known values are: "string" and "boolean". - "options": [ - "str" # Optional. options. - ] - } - ] + "expires_at": "2020-02-20 00:00:00", # Optional. Repeat an approved call + with the same arguments before this time. + "id": "str", # Optional. The approval ID. + "status": "str" # Optional. Known values are: "approved" and "denied". } - # response body for status code(s): 404 + # response body for status code(s): 400, 404, 409, 412, 429, 500, 503 + response == { + "call_id": "str", # A unique identifier for this request, when one was + assigned. Required. + "error": { + "class": "str", # Optional. The failure class. Branch on this value + rather than on ``message``"" , which may change. Known values are: + "invalid_argument", "unauthorized", "forbidden", "rate_limited", "not_found", + "timeout", "upstream_error", "output_too_large", "execution_failed", + "unavailable", "canceled", "render_required", "unsupported_media_type", and + "view_materialization_failed". + "message": "str", # Optional. A human-readable explanation that is + safe to show to an agent or end user. + "recovery_hint": "str", # Optional. The suggested next step. Known + values are: "fix_args", "refresh_auth", "retry_later", "narrow_output", + "contact_support", and "verify_provider_state". + "retriable": bool # Optional. Whether retrying the same request + later can succeed. + }, + "status": "str" # Required. "failed" + } + # response body for status code(s): 401 response == { "id": "str", # A short identifier corresponding to the HTTP status code returned. For example, the ID for a response returning a 404 status code would @@ -12886,25 +13696,35 @@ async def get_app_metadata(self, app_slug: str, **kwargs: Any) -> JSON: } """ error_map: MutableMapping[int, Type[HttpResponseError]] = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError, 304: ResourceNotModifiedError, - 401: cast( - Type[HttpResponseError], - lambda response: ClientAuthenticationError(response=response), - ), - 429: HttpResponseError, - 500: HttpResponseError, } error_map.update(kwargs.pop("error_map", {}) or {}) - _headers = kwargs.pop("headers", {}) or {} + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = kwargs.pop("params", {}) or {} + content_type: Optional[str] = kwargs.pop( + "content_type", _headers.pop("Content-Type", None) + ) cls: ClsType[JSON] = kwargs.pop("cls", None) - _request = build_addons_get_app_metadata_request( - app_slug=app_slug, + content_type = content_type or "application/json" + _json = None + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _json = body + + _request = build_gateway_approvals_update_request( + approval_id=approval_id, + x_session_id=x_session_id, + content_type=content_type, + json=_json, + content=_content, headers=_headers, params=_params, ) @@ -12919,38 +13739,71 @@ async def get_app_metadata(self, app_slug: str, **kwargs: Any) -> JSON: response = pipeline_response.http_response - if response.status_code not in [200, 404]: + if response.status_code not in [ + 200, + 400, + 401, + 403, + 404, + 409, + 412, + 429, + 500, + 503, + ]: if _stream: await response.read() # Load the body in memory and close the socket - map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + map_error( + status_code=response.status_code, response=response, error_map=error_map + ) raise HttpResponseError(response=response) response_headers = {} if response.status_code == 200: - response_headers["ratelimit-limit"] = self._deserialize( - "int", response.headers.get("ratelimit-limit") - ) - response_headers["ratelimit-remaining"] = self._deserialize( - "int", response.headers.get("ratelimit-remaining") - ) - response_headers["ratelimit-reset"] = self._deserialize( - "int", response.headers.get("ratelimit-reset") - ) + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 400: + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 401: + if response.content: + deserialized = response.json() + else: + deserialized = None + if response.status_code == 403: if response.content: deserialized = response.json() else: deserialized = None if response.status_code == 404: - response_headers["ratelimit-limit"] = self._deserialize( - "int", response.headers.get("ratelimit-limit") - ) - response_headers["ratelimit-remaining"] = self._deserialize( - "int", response.headers.get("ratelimit-remaining") - ) - response_headers["ratelimit-reset"] = self._deserialize( - "int", response.headers.get("ratelimit-reset") + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 409: + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 412: + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 429: + response_headers["Retry-After"] = self._deserialize( + "int", response.headers.get("Retry-After") ) if response.content: @@ -12958,18 +13811,60 @@ async def get_app_metadata(self, app_slug: str, **kwargs: Any) -> JSON: else: deserialized = None + if response.status_code == 500: + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 503: + if response.content: + deserialized = response.json() + else: + deserialized = None + if cls: return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore return cast(JSON, deserialized) # type: ignore + +class OneClicksOperations: + """ + .. warning:: + **DO NOT** instantiate this class directly. + + Instead, you should access the following operations through + :class:`~pydo.aio.GeneratedClient`'s + :attr:`one_clicks` attribute. + """ + + def __init__(self, *args, **kwargs) -> None: + input_args = list(args) + self._client = input_args.pop(0) if input_args else kwargs.pop("client") + self._config = input_args.pop(0) if input_args else kwargs.pop("config") + self._serialize = input_args.pop(0) if input_args else kwargs.pop("serializer") + self._deserialize = ( + input_args.pop(0) if input_args else kwargs.pop("deserializer") + ) + @distributed_trace_async - async def list(self, **kwargs: Any) -> JSON: - # pylint: disable=line-too-long - """List all Add-On Resources. + async def list(self, *, type: Optional[str] = None, **kwargs: Any) -> JSON: + """List 1-Click Applications. - To fetch all Add-On Resources under your team, send a GET request to ``/v2/add-ons/saas``. + To list all available 1-Click applications, send a GET request to ``/v2/1-clicks``. The + ``type`` may + be provided as query paramater in order to restrict results to a certain type of 1-Click, for + example: ``/v2/1-clicks?type=droplet``. Current supported types are ``kubernetes`` and + ``droplet``. + + The response will be a JSON object with a key called ``1_clicks``. This will be set to an array + of + 1-Click application data, each of which will contain the the slug and type for the 1-Click. + :keyword type: Restrict results to a certain type of 1-Click. Known values are: "droplet" and + "kubernetes". Default value is None. + :paramtype type: str :return: JSON object :rtype: JSON :raises ~azure.core.exceptions.HttpResponseError: @@ -12979,38 +13874,12 @@ async def list(self, **kwargs: Any) -> JSON: # response body for status code(s): 200 response == { - "resources": [ + "1_clicks": [ { - "app_slug": "str", # The slug identifier for the application - associated with the resource. Required. - "has_config": bool, # Indicates if the resource has - configuration values set by the vendor. Required. - "name": "str", # The name of the addon resource. Required. - "plan_slug": "str", # The slug identifier for the plan - associated with the resource. Required. - "state": "str", # The state the resource is currently in. - Required. Known values are: "pending", "provisioning", "provisioned", - "deprovisioning", "deprovisioned", "provisioning-failed", - "deprovisioning-failed", and "suspended". - "uuid": "str", # The unique identifier for the addon - resource. Required. - "app_name": "str", # Optional. The name of the application - associated with the resource. - "message": "str", # Optional. A message related to the - resource, if applicable. - "metadata": [ - { - "name": "str", # The name of the metadata - item to be set. Required. - "value": {} - } - ], - "plan_name": "str", # Optional. The name of the plan - associated with the resource. - "plan_price_per_month": 0, # Optional. The price of the plan - per month in US dollars. - "sso_url": "str" # Optional. The Single Sign-On URL for the - resource, if applicable. + "slug": "str", # The slug identifier for the 1-Click + application. Required. + "type": "str" # The type of the 1-Click application. + Required. } ] } @@ -13033,7 +13902,8 @@ async def list(self, **kwargs: Any) -> JSON: cls: ClsType[JSON] = kwargs.pop("cls", None) - _request = build_addons_list_request( + _request = build_one_clicks_list_request( + type=type, headers=_headers, params=_params, ) @@ -13076,17 +13946,16 @@ async def list(self, **kwargs: Any) -> JSON: return cast(JSON, deserialized) # type: ignore @overload - async def create( + async def install_kubernetes( self, body: JSON, *, content_type: str = "application/json", **kwargs: Any ) -> JSON: # pylint: disable=line-too-long - """Create/Provision a New Add-on Resource. + """Install Kubernetes 1-Click Applications. - To create an add-on resource, send a POST request to ``/v2/add-ons/saas`` with required - parameters. - Some add-ons require additional metadata to be provided in the request body. To find out - what metadata is required for a specific add-on, send a GET request to - ``/v2/add-ons/apps/{app_slug}/metadata``. + To install a Kubernetes 1-Click application on a cluster, send a POST request to + ``/v2/1-clicks/kubernetes``. The ``addon_slugs`` and ``cluster_uuid`` must be provided as body + parameter in order to specify which 1-Click application(s) to install. To list all available + 1-Click Kubernetes applications, send a request to ``/v2/1-clicks?type=kubernetes``. :param body: Required. :type body: JSON @@ -13102,73 +13971,30 @@ async def create( # JSON input template you can fill out and use as your body input. body = { - "app_slug": "str", # The slug identifier for the application associated with - the resource. Required. - "metadata": [ - { - "name": "str", # The name of the metadata item to be set. - Required. - "value": {} - } + "addon_slugs": [ + "str" # An array of 1-Click Application slugs to be installed to the + Kubernetes cluster. Required. ], - "name": "str", # The name of the addon resource. Required. - "plan_slug": "str", # The slug identifier for the plan associated with the - resource. Required. - "fleet_uuid": "str", # Optional. UUID of the fleet/project to which this - resource will belong. - "linked_droplet_id": 0 # Optional. ID of the droplet to be linked to this - resource, if applicable. + "cluster_uuid": "str" # A unique ID for the Kubernetes cluster to which the + 1-Click Applications will be installed. Required. } # response body for status code(s): 200 response == { - "resource": { - "app_slug": "str", # The slug identifier for the application - associated with the resource. Required. - "has_config": bool, # Indicates if the resource has configuration - values set by the vendor. Required. - "name": "str", # The name of the addon resource. Required. - "plan_slug": "str", # The slug identifier for the plan associated - with the resource. Required. - "state": "str", # The state the resource is currently in. Required. - Known values are: "pending", "provisioning", "provisioned", "deprovisioning", - "deprovisioned", "provisioning-failed", "deprovisioning-failed", and - "suspended". - "uuid": "str", # The unique identifier for the addon resource. - Required. - "app_name": "str", # Optional. The name of the application - associated with the resource. - "message": "str", # Optional. A message related to the resource, if - applicable. - "metadata": [ - { - "name": "str", # The name of the metadata item to be - set. Required. - "value": {} - } - ], - "plan_name": "str", # Optional. The name of the plan associated with - the resource. - "plan_price_per_month": 0, # Optional. The price of the plan per - month in US dollars. - "sso_url": "str" # Optional. The Single Sign-On URL for the - resource, if applicable. - } + "message": "str" # Optional. A message about the result of the request. } """ @overload - async def create( + async def install_kubernetes( self, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any ) -> JSON: - # pylint: disable=line-too-long - """Create/Provision a New Add-on Resource. + """Install Kubernetes 1-Click Applications. - To create an add-on resource, send a POST request to ``/v2/add-ons/saas`` with required - parameters. - Some add-ons require additional metadata to be provided in the request body. To find out - what metadata is required for a specific add-on, send a GET request to - ``/v2/add-ons/apps/{app_slug}/metadata``. + To install a Kubernetes 1-Click application on a cluster, send a POST request to + ``/v2/1-clicks/kubernetes``. The ``addon_slugs`` and ``cluster_uuid`` must be provided as body + parameter in order to specify which 1-Click application(s) to install. To list all available + 1-Click Kubernetes applications, send a request to ``/v2/1-clicks?type=kubernetes``. :param body: Required. :type body: IO[bytes] @@ -13184,51 +14010,21 @@ async def create( # response body for status code(s): 200 response == { - "resource": { - "app_slug": "str", # The slug identifier for the application - associated with the resource. Required. - "has_config": bool, # Indicates if the resource has configuration - values set by the vendor. Required. - "name": "str", # The name of the addon resource. Required. - "plan_slug": "str", # The slug identifier for the plan associated - with the resource. Required. - "state": "str", # The state the resource is currently in. Required. - Known values are: "pending", "provisioning", "provisioned", "deprovisioning", - "deprovisioned", "provisioning-failed", "deprovisioning-failed", and - "suspended". - "uuid": "str", # The unique identifier for the addon resource. - Required. - "app_name": "str", # Optional. The name of the application - associated with the resource. - "message": "str", # Optional. A message related to the resource, if - applicable. - "metadata": [ - { - "name": "str", # The name of the metadata item to be - set. Required. - "value": {} - } - ], - "plan_name": "str", # Optional. The name of the plan associated with - the resource. - "plan_price_per_month": 0, # Optional. The price of the plan per - month in US dollars. - "sso_url": "str" # Optional. The Single Sign-On URL for the - resource, if applicable. - } + "message": "str" # Optional. A message about the result of the request. } """ @distributed_trace_async - async def create(self, body: Union[JSON, IO[bytes]], **kwargs: Any) -> JSON: + async def install_kubernetes( + self, body: Union[JSON, IO[bytes]], **kwargs: Any + ) -> JSON: # pylint: disable=line-too-long - """Create/Provision a New Add-on Resource. + """Install Kubernetes 1-Click Applications. - To create an add-on resource, send a POST request to ``/v2/add-ons/saas`` with required - parameters. - Some add-ons require additional metadata to be provided in the request body. To find out - what metadata is required for a specific add-on, send a GET request to - ``/v2/add-ons/apps/{app_slug}/metadata``. + To install a Kubernetes 1-Click application on a cluster, send a POST request to + ``/v2/1-clicks/kubernetes``. The ``addon_slugs`` and ``cluster_uuid`` must be provided as body + parameter in order to specify which 1-Click application(s) to install. To list all available + 1-Click Kubernetes applications, send a request to ``/v2/1-clicks?type=kubernetes``. :param body: Is either a JSON type or a IO[bytes] type. Required. :type body: JSON or IO[bytes] @@ -13241,58 +14037,17 @@ async def create(self, body: Union[JSON, IO[bytes]], **kwargs: Any) -> JSON: # JSON input template you can fill out and use as your body input. body = { - "app_slug": "str", # The slug identifier for the application associated with - the resource. Required. - "metadata": [ - { - "name": "str", # The name of the metadata item to be set. - Required. - "value": {} - } + "addon_slugs": [ + "str" # An array of 1-Click Application slugs to be installed to the + Kubernetes cluster. Required. ], - "name": "str", # The name of the addon resource. Required. - "plan_slug": "str", # The slug identifier for the plan associated with the - resource. Required. - "fleet_uuid": "str", # Optional. UUID of the fleet/project to which this - resource will belong. - "linked_droplet_id": 0 # Optional. ID of the droplet to be linked to this - resource, if applicable. + "cluster_uuid": "str" # A unique ID for the Kubernetes cluster to which the + 1-Click Applications will be installed. Required. } # response body for status code(s): 200 response == { - "resource": { - "app_slug": "str", # The slug identifier for the application - associated with the resource. Required. - "has_config": bool, # Indicates if the resource has configuration - values set by the vendor. Required. - "name": "str", # The name of the addon resource. Required. - "plan_slug": "str", # The slug identifier for the plan associated - with the resource. Required. - "state": "str", # The state the resource is currently in. Required. - Known values are: "pending", "provisioning", "provisioned", "deprovisioning", - "deprovisioned", "provisioning-failed", "deprovisioning-failed", and - "suspended". - "uuid": "str", # The unique identifier for the addon resource. - Required. - "app_name": "str", # Optional. The name of the application - associated with the resource. - "message": "str", # Optional. A message related to the resource, if - applicable. - "metadata": [ - { - "name": "str", # The name of the metadata item to be - set. Required. - "value": {} - } - ], - "plan_name": "str", # Optional. The name of the plan associated with - the resource. - "plan_price_per_month": 0, # Optional. The price of the plan per - month in US dollars. - "sso_url": "str" # Optional. The Single Sign-On URL for the - resource, if applicable. - } + "message": "str" # Optional. A message about the result of the request. } """ error_map: MutableMapping[int, Type[HttpResponseError]] = { @@ -13324,7 +14079,7 @@ async def create(self, body: Union[JSON, IO[bytes]], **kwargs: Any) -> JSON: else: _json = body - _request = build_addons_create_request( + _request = build_one_clicks_install_kubernetes_request( content_type=content_type, json=_json, content=_content, @@ -13369,17 +14124,33 @@ async def create(self, body: Union[JSON, IO[bytes]], **kwargs: Any) -> JSON: return cast(JSON, deserialized) # type: ignore + +class AccountOperations: + """ + .. warning:: + **DO NOT** instantiate this class directly. + + Instead, you should access the following operations through + :class:`~pydo.aio.GeneratedClient`'s + :attr:`account` attribute. + """ + + def __init__(self, *args, **kwargs) -> None: + input_args = list(args) + self._client = input_args.pop(0) if input_args else kwargs.pop("client") + self._config = input_args.pop(0) if input_args else kwargs.pop("config") + self._serialize = input_args.pop(0) if input_args else kwargs.pop("serializer") + self._deserialize = ( + input_args.pop(0) if input_args else kwargs.pop("deserializer") + ) + @distributed_trace_async - async def get(self, resource_uuid: str, **kwargs: Any) -> JSON: + async def get(self, **kwargs: Any) -> JSON: # pylint: disable=line-too-long - """Get details on an Add-On Resource. + """Get User Information. - To fetch details of a specific Add-On Resource, send a GET request to - ``/v2/add-ons/saas/{resource_uuid}``. - Replace ``{resource_uuid}`` with the UUID of the resource you want to retrieve. + To show information about the current user account, send a GET request to ``/v2/account``. - :param resource_uuid: The UUID of the add-on resource to retrieve. Required. - :type resource_uuid: str :return: JSON object :rtype: JSON :raises ~azure.core.exceptions.HttpResponseError: @@ -13389,50 +14160,32 @@ async def get(self, resource_uuid: str, **kwargs: Any) -> JSON: # response body for status code(s): 200 response == { - "resource": { - "app_slug": "str", # The slug identifier for the application - associated with the resource. Required. - "has_config": bool, # Indicates if the resource has configuration - values set by the vendor. Required. - "name": "str", # The name of the addon resource. Required. - "plan_slug": "str", # The slug identifier for the plan associated - with the resource. Required. - "state": "str", # The state the resource is currently in. Required. - Known values are: "pending", "provisioning", "provisioned", "deprovisioning", - "deprovisioned", "provisioning-failed", "deprovisioning-failed", and - "suspended". - "uuid": "str", # The unique identifier for the addon resource. - Required. - "app_name": "str", # Optional. The name of the application - associated with the resource. - "message": "str", # Optional. A message related to the resource, if - applicable. - "metadata": [ - { - "name": "str", # The name of the metadata item to be - set. Required. - "value": {} - } - ], - "plan_name": "str", # Optional. The name of the plan associated with - the resource. - "plan_price_per_month": 0, # Optional. The price of the plan per - month in US dollars. - "sso_url": "str" # Optional. The Single Sign-On URL for the - resource, if applicable. + "account": { + "droplet_limit": 0, # The total number of Droplets current user or + team may have active at one time. :code:`
`:code:`
`Requires + ``droplet:read`` scope. Required. + "email": "str", # The email address used by the current user to + register for DigitalOcean. Required. + "email_verified": False, # Default value is False. If true, the user + has verified their account via email. False otherwise. Required. + "floating_ip_limit": 0, # The total number of Floating IPs the + current user or team may have. :code:`
`:code:`
`Requires + ``reserved_ip:read`` scope. Required. + "status": "active", # Default value is "active". This value is one + of "active", "warning" or "locked". Known values are: "active", "warning", + and "locked". + "status_message": "str", # A human-readable message giving more + details about the status of the account. Required. + "uuid": "str", # The unique universal identifier for the current + user. Required. + "name": "str", # Optional. The display name for the current user. + "team": { + "name": "str", # Optional. The name for the current team. + "uuid": "str" # Optional. The unique universal identifier + for the current team. + } } } - # response body for status code(s): 404 - response == { - "id": "str", # A short identifier corresponding to the HTTP status code - returned. For example, the ID for a response returning a 404 status code would - be "not_found.". Required. - "message": "str", # A message providing additional information about the - error, including details to help resolve it when possible. Required. - "request_id": "str" # Optional. Optionally, some endpoints may include a - request ID that should be provided when reporting bugs or opening support - tickets to help identify the issue. - } """ error_map: MutableMapping[int, Type[HttpResponseError]] = { 404: ResourceNotFoundError, @@ -13452,8 +14205,7 @@ async def get(self, resource_uuid: str, **kwargs: Any) -> JSON: cls: ClsType[JSON] = kwargs.pop("cls", None) - _request = build_addons_get_request( - resource_uuid=resource_uuid, + _request = build_account_get_request( headers=_headers, params=_params, ) @@ -13468,80 +14220,96 @@ async def get(self, resource_uuid: str, **kwargs: Any) -> JSON: response = pipeline_response.http_response - if response.status_code not in [200, 404]: + if response.status_code not in [200]: if _stream: await response.read() # Load the body in memory and close the socket map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore raise HttpResponseError(response=response) response_headers = {} - if response.status_code == 200: - response_headers["ratelimit-limit"] = self._deserialize( - "int", response.headers.get("ratelimit-limit") - ) - response_headers["ratelimit-remaining"] = self._deserialize( - "int", response.headers.get("ratelimit-remaining") - ) - response_headers["ratelimit-reset"] = self._deserialize( - "int", response.headers.get("ratelimit-reset") - ) - - if response.content: - deserialized = response.json() - else: - deserialized = None - - if response.status_code == 404: - response_headers["ratelimit-limit"] = self._deserialize( - "int", response.headers.get("ratelimit-limit") - ) - response_headers["ratelimit-remaining"] = self._deserialize( - "int", response.headers.get("ratelimit-remaining") - ) - response_headers["ratelimit-reset"] = self._deserialize( - "int", response.headers.get("ratelimit-reset") - ) + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) - if response.content: - deserialized = response.json() - else: - deserialized = None + if response.content: + deserialized = response.json() + else: + deserialized = None if cls: return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore return cast(JSON, deserialized) # type: ignore + +class SshKeysOperations: + """ + .. warning:: + **DO NOT** instantiate this class directly. + + Instead, you should access the following operations through + :class:`~pydo.aio.GeneratedClient`'s + :attr:`ssh_keys` attribute. + """ + + def __init__(self, *args, **kwargs) -> None: + input_args = list(args) + self._client = input_args.pop(0) if input_args else kwargs.pop("client") + self._config = input_args.pop(0) if input_args else kwargs.pop("config") + self._serialize = input_args.pop(0) if input_args else kwargs.pop("serializer") + self._deserialize = ( + input_args.pop(0) if input_args else kwargs.pop("deserializer") + ) + @distributed_trace_async - async def delete(self, resource_uuid: str, **kwargs: Any) -> Optional[JSON]: + async def list(self, *, per_page: int = 20, page: int = 1, **kwargs: Any) -> JSON: # pylint: disable=line-too-long - """Delete/Deprovision an Add-on Resource. + """List All SSH Keys. - To delete an add-on resource, send a DELETE request to ``/v2/add-ons/saas/{resource_uuid}`` - with the UUID of the resource to delete. - You cannot retrieve the resource after it has been deleted. The response indicates a request - was sent to the 3rd party add-on provider to delete the resource. - You will no longer be billed for this resource. + To list all of the keys in your account, send a GET request to ``/v2/account/keys``. The + response will be a JSON object with a key set to ``ssh_keys``. The value of this will be an + array of ssh_key objects, each of which contains the standard ssh_key attributes. - :param resource_uuid: A unique identifier for the add-on resource. Required. - :type resource_uuid: str - :return: JSON object or None - :rtype: JSON or None + :keyword per_page: Number of items returned per page. Default value is 20. + :paramtype per_page: int + :keyword page: Which 'page' of paginated results to return. Default value is 1. + :paramtype page: int + :return: JSON object + :rtype: JSON :raises ~azure.core.exceptions.HttpResponseError: Example: .. code-block:: python - # response body for status code(s): 404 + # response body for status code(s): 200 response == { - "id": "str", # A short identifier corresponding to the HTTP status code - returned. For example, the ID for a response returning a 404 status code would - be "not_found.". Required. - "message": "str", # A message providing additional information about the - error, including details to help resolve it when possible. Required. - "request_id": "str" # Optional. Optionally, some endpoints may include a - request ID that should be provided when reporting bugs or opening support - tickets to help identify the issue. + "meta": { + "total": 0 # Optional. Number of objects returned by the request. + }, + "links": { + "pages": {} + }, + "ssh_keys": [ + { + "name": "str", # A human-readable display name for this key, + used to easily identify the SSH keys when they are displayed. Required. + "public_key": "str", # The entire public key string that was + uploaded. Embedded into the root user's ``authorized_keys`` file if you + include this key during Droplet creation. Required. + "fingerprint": "str", # Optional. A unique identifier that + differentiates this key from other keys using a format that SSH + recognizes. The fingerprint is created when the key is added to your + account. + "id": 0 # Optional. A unique identification number for this + key. Can be used to embed a specific SSH key into a Droplet. + } + ] } """ error_map: MutableMapping[int, Type[HttpResponseError]] = { @@ -13560,10 +14328,11 @@ async def delete(self, resource_uuid: str, **kwargs: Any) -> Optional[JSON]: _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - cls: ClsType[Optional[JSON]] = kwargs.pop("cls", None) + cls: ClsType[JSON] = kwargs.pop("cls", None) - _request = build_addons_delete_request( - resource_uuid=resource_uuid, + _request = build_ssh_keys_list_request( + per_page=per_page, + page=page, headers=_headers, params=_params, ) @@ -13578,65 +14347,44 @@ async def delete(self, resource_uuid: str, **kwargs: Any) -> Optional[JSON]: response = pipeline_response.http_response - if response.status_code not in [200, 404]: + if response.status_code not in [200]: if _stream: await response.read() # Load the body in memory and close the socket map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore raise HttpResponseError(response=response) - deserialized = None response_headers = {} - if response.status_code == 200: - response_headers["ratelimit-limit"] = self._deserialize( - "int", response.headers.get("ratelimit-limit") - ) - response_headers["ratelimit-remaining"] = self._deserialize( - "int", response.headers.get("ratelimit-remaining") - ) - response_headers["ratelimit-reset"] = self._deserialize( - "int", response.headers.get("ratelimit-reset") - ) - - if response.status_code == 404: - response_headers["ratelimit-limit"] = self._deserialize( - "int", response.headers.get("ratelimit-limit") - ) - response_headers["ratelimit-remaining"] = self._deserialize( - "int", response.headers.get("ratelimit-remaining") - ) - response_headers["ratelimit-reset"] = self._deserialize( - "int", response.headers.get("ratelimit-reset") - ) + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) - if response.content: - deserialized = response.json() - else: - deserialized = None + if response.content: + deserialized = response.json() + else: + deserialized = None if cls: - return cls(pipeline_response, deserialized, response_headers) # type: ignore + return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore - return deserialized # type: ignore + return cast(JSON, deserialized) # type: ignore @overload - async def patch( - self, - resource_uuid: str, - body: JSON, - *, - content_type: str = "application/json", - **kwargs: Any + async def create( + self, body: JSON, *, content_type: str = "application/json", **kwargs: Any ) -> JSON: # pylint: disable=line-too-long - """Update the name for an Add-On Resource. + """Create a New SSH Key. - To change the name of an Add-On Resource, send a PATCH request to - ``/v2/add-ons/saas/{resource_uuid}``. - Replace ``{resource_uuid}`` with the UUID of the resource for which you want to change the - name. + To add a new SSH public key to your DigitalOcean account, send a POST request to + ``/v2/account/keys``. Set the ``name`` attribute to the name you wish to use and the + ``public_key`` attribute to the full public key you are adding. - :param resource_uuid: The UUID of the add-on resource to rename. Required. - :type resource_uuid: str :param body: Required. :type body: JSON :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. @@ -13651,76 +14399,46 @@ async def patch( # JSON input template you can fill out and use as your body input. body = { - "name": "str" # The new name for the add-on resource. Required. + "name": "str", # A human-readable display name for this key, used to easily + identify the SSH keys when they are displayed. Required. + "public_key": "str", # The entire public key string that was uploaded. + Embedded into the root user's ``authorized_keys`` file if you include this key + during Droplet creation. Required. + "fingerprint": "str", # Optional. A unique identifier that differentiates + this key from other keys using a format that SSH recognizes. The fingerprint is + created when the key is added to your account. + "id": 0 # Optional. A unique identification number for this key. Can be used + to embed a specific SSH key into a Droplet. } - # response body for status code(s): 200 + # response body for status code(s): 201 response == { - "resource": { - "app_slug": "str", # The slug identifier for the application - associated with the resource. Required. - "has_config": bool, # Indicates if the resource has configuration - values set by the vendor. Required. - "name": "str", # The name of the addon resource. Required. - "plan_slug": "str", # The slug identifier for the plan associated - with the resource. Required. - "state": "str", # The state the resource is currently in. Required. - Known values are: "pending", "provisioning", "provisioned", "deprovisioning", - "deprovisioned", "provisioning-failed", "deprovisioning-failed", and - "suspended". - "uuid": "str", # The unique identifier for the addon resource. - Required. - "app_name": "str", # Optional. The name of the application - associated with the resource. - "message": "str", # Optional. A message related to the resource, if - applicable. - "metadata": [ - { - "name": "str", # The name of the metadata item to be - set. Required. - "value": {} - } - ], - "plan_name": "str", # Optional. The name of the plan associated with - the resource. - "plan_price_per_month": 0, # Optional. The price of the plan per - month in US dollars. - "sso_url": "str" # Optional. The Single Sign-On URL for the - resource, if applicable. + "ssh_key": { + "name": "str", # A human-readable display name for this key, used to + easily identify the SSH keys when they are displayed. Required. + "public_key": "str", # The entire public key string that was + uploaded. Embedded into the root user's ``authorized_keys`` file if you + include this key during Droplet creation. Required. + "fingerprint": "str", # Optional. A unique identifier that + differentiates this key from other keys using a format that SSH recognizes. + The fingerprint is created when the key is added to your account. + "id": 0 # Optional. A unique identification number for this key. Can + be used to embed a specific SSH key into a Droplet. } } - # response body for status code(s): 404 - response == { - "id": "str", # A short identifier corresponding to the HTTP status code - returned. For example, the ID for a response returning a 404 status code would - be "not_found.". Required. - "message": "str", # A message providing additional information about the - error, including details to help resolve it when possible. Required. - "request_id": "str" # Optional. Optionally, some endpoints may include a - request ID that should be provided when reporting bugs or opening support - tickets to help identify the issue. - } """ @overload - async def patch( - self, - resource_uuid: str, - body: IO[bytes], - *, - content_type: str = "application/json", - **kwargs: Any + async def create( + self, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any ) -> JSON: # pylint: disable=line-too-long - """Update the name for an Add-On Resource. + """Create a New SSH Key. - To change the name of an Add-On Resource, send a PATCH request to - ``/v2/add-ons/saas/{resource_uuid}``. - Replace ``{resource_uuid}`` with the UUID of the resource for which you want to change the - name. + To add a new SSH public key to your DigitalOcean account, send a POST request to + ``/v2/account/keys``. Set the ``name`` attribute to the name you wish to use and the + ``public_key`` attribute to the full public key you are adding. - :param resource_uuid: The UUID of the add-on resource to rename. Required. - :type resource_uuid: str :param body: Required. :type body: IO[bytes] :keyword content_type: Body Parameter content-type. Content type parameter for binary body. @@ -13733,68 +14451,32 @@ async def patch( Example: .. code-block:: python - # response body for status code(s): 200 + # response body for status code(s): 201 response == { - "resource": { - "app_slug": "str", # The slug identifier for the application - associated with the resource. Required. - "has_config": bool, # Indicates if the resource has configuration - values set by the vendor. Required. - "name": "str", # The name of the addon resource. Required. - "plan_slug": "str", # The slug identifier for the plan associated - with the resource. Required. - "state": "str", # The state the resource is currently in. Required. - Known values are: "pending", "provisioning", "provisioned", "deprovisioning", - "deprovisioned", "provisioning-failed", "deprovisioning-failed", and - "suspended". - "uuid": "str", # The unique identifier for the addon resource. - Required. - "app_name": "str", # Optional. The name of the application - associated with the resource. - "message": "str", # Optional. A message related to the resource, if - applicable. - "metadata": [ - { - "name": "str", # The name of the metadata item to be - set. Required. - "value": {} - } - ], - "plan_name": "str", # Optional. The name of the plan associated with - the resource. - "plan_price_per_month": 0, # Optional. The price of the plan per - month in US dollars. - "sso_url": "str" # Optional. The Single Sign-On URL for the - resource, if applicable. + "ssh_key": { + "name": "str", # A human-readable display name for this key, used to + easily identify the SSH keys when they are displayed. Required. + "public_key": "str", # The entire public key string that was + uploaded. Embedded into the root user's ``authorized_keys`` file if you + include this key during Droplet creation. Required. + "fingerprint": "str", # Optional. A unique identifier that + differentiates this key from other keys using a format that SSH recognizes. + The fingerprint is created when the key is added to your account. + "id": 0 # Optional. A unique identification number for this key. Can + be used to embed a specific SSH key into a Droplet. } } - # response body for status code(s): 404 - response == { - "id": "str", # A short identifier corresponding to the HTTP status code - returned. For example, the ID for a response returning a 404 status code would - be "not_found.". Required. - "message": "str", # A message providing additional information about the - error, including details to help resolve it when possible. Required. - "request_id": "str" # Optional. Optionally, some endpoints may include a - request ID that should be provided when reporting bugs or opening support - tickets to help identify the issue. - } """ @distributed_trace_async - async def patch( - self, resource_uuid: str, body: Union[JSON, IO[bytes]], **kwargs: Any - ) -> JSON: + async def create(self, body: Union[JSON, IO[bytes]], **kwargs: Any) -> JSON: # pylint: disable=line-too-long - """Update the name for an Add-On Resource. + """Create a New SSH Key. - To change the name of an Add-On Resource, send a PATCH request to - ``/v2/add-ons/saas/{resource_uuid}``. - Replace ``{resource_uuid}`` with the UUID of the resource for which you want to change the - name. + To add a new SSH public key to your DigitalOcean account, send a POST request to + ``/v2/account/keys``. Set the ``name`` attribute to the name you wish to use and the + ``public_key`` attribute to the full public key you are adding. - :param resource_uuid: The UUID of the add-on resource to rename. Required. - :type resource_uuid: str :param body: Is either a JSON type or a IO[bytes] type. Required. :type body: JSON or IO[bytes] :return: JSON object @@ -13806,55 +14488,33 @@ async def patch( # JSON input template you can fill out and use as your body input. body = { - "name": "str" # The new name for the add-on resource. Required. + "name": "str", # A human-readable display name for this key, used to easily + identify the SSH keys when they are displayed. Required. + "public_key": "str", # The entire public key string that was uploaded. + Embedded into the root user's ``authorized_keys`` file if you include this key + during Droplet creation. Required. + "fingerprint": "str", # Optional. A unique identifier that differentiates + this key from other keys using a format that SSH recognizes. The fingerprint is + created when the key is added to your account. + "id": 0 # Optional. A unique identification number for this key. Can be used + to embed a specific SSH key into a Droplet. } - # response body for status code(s): 200 + # response body for status code(s): 201 response == { - "resource": { - "app_slug": "str", # The slug identifier for the application - associated with the resource. Required. - "has_config": bool, # Indicates if the resource has configuration - values set by the vendor. Required. - "name": "str", # The name of the addon resource. Required. - "plan_slug": "str", # The slug identifier for the plan associated - with the resource. Required. - "state": "str", # The state the resource is currently in. Required. - Known values are: "pending", "provisioning", "provisioned", "deprovisioning", - "deprovisioned", "provisioning-failed", "deprovisioning-failed", and - "suspended". - "uuid": "str", # The unique identifier for the addon resource. - Required. - "app_name": "str", # Optional. The name of the application - associated with the resource. - "message": "str", # Optional. A message related to the resource, if - applicable. - "metadata": [ - { - "name": "str", # The name of the metadata item to be - set. Required. - "value": {} - } - ], - "plan_name": "str", # Optional. The name of the plan associated with - the resource. - "plan_price_per_month": 0, # Optional. The price of the plan per - month in US dollars. - "sso_url": "str" # Optional. The Single Sign-On URL for the - resource, if applicable. + "ssh_key": { + "name": "str", # A human-readable display name for this key, used to + easily identify the SSH keys when they are displayed. Required. + "public_key": "str", # The entire public key string that was + uploaded. Embedded into the root user's ``authorized_keys`` file if you + include this key during Droplet creation. Required. + "fingerprint": "str", # Optional. A unique identifier that + differentiates this key from other keys using a format that SSH recognizes. + The fingerprint is created when the key is added to your account. + "id": 0 # Optional. A unique identification number for this key. Can + be used to embed a specific SSH key into a Droplet. } } - # response body for status code(s): 404 - response == { - "id": "str", # A short identifier corresponding to the HTTP status code - returned. For example, the ID for a response returning a 404 status code would - be "not_found.". Required. - "message": "str", # A message providing additional information about the - error, including details to help resolve it when possible. Required. - "request_id": "str" # Optional. Optionally, some endpoints may include a - request ID that should be provided when reporting bugs or opening support - tickets to help identify the issue. - } """ error_map: MutableMapping[int, Type[HttpResponseError]] = { 404: ResourceNotFoundError, @@ -13885,8 +14545,7 @@ async def patch( else: _json = body - _request = build_addons_patch_request( - resource_uuid=resource_uuid, + _request = build_ssh_keys_create_request( content_type=content_type, json=_json, content=_content, @@ -13904,6 +14563,113 @@ async def patch( response = pipeline_response.http_response + if response.status_code not in [201]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + response_headers = {} + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore + + return cast(JSON, deserialized) # type: ignore + + @distributed_trace_async + async def get(self, ssh_key_identifier: JSON, **kwargs: Any) -> JSON: + # pylint: disable=line-too-long + """Retrieve an Existing SSH Key. + + To get information about a key, send a GET request to ``/v2/account/keys/$KEY_ID`` or + ``/v2/account/keys/$KEY_FINGERPRINT``. + The response will be a JSON object with the key ``ssh_key`` and value an ssh_key object which + contains the standard ssh_key attributes. + + :param ssh_key_identifier: Either the ID or the fingerprint of an existing SSH key. Required. + :type ssh_key_identifier: JSON + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 200 + response == { + "ssh_key": { + "name": "str", # A human-readable display name for this key, used to + easily identify the SSH keys when they are displayed. Required. + "public_key": "str", # The entire public key string that was + uploaded. Embedded into the root user's ``authorized_keys`` file if you + include this key during Droplet creation. Required. + "fingerprint": "str", # Optional. A unique identifier that + differentiates this key from other keys using a format that SSH recognizes. + The fingerprint is created when the key is added to your account. + "id": 0 # Optional. A unique identification number for this key. Can + be used to embed a specific SSH key into a Droplet. + } + } + # response body for status code(s): 404 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[JSON] = kwargs.pop("cls", None) + + _request = build_ssh_keys_get_request( + ssh_key_identifier=ssh_key_identifier, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + if response.status_code not in [200, 404]: if _stream: await response.read() # Load the body in memory and close the socket @@ -13949,25 +14715,24 @@ async def patch( return cast(JSON, deserialized) # type: ignore @overload - async def patch_plan( + async def update( self, - resource_uuid: str, + ssh_key_identifier: JSON, body: JSON, *, content_type: str = "application/json", **kwargs: Any ) -> JSON: # pylint: disable=line-too-long - """Update the plan for an Add-On Resource. + """Update an SSH Key's Name. - To change the plan associated with an Add-On Resource, send a PATCH request to - ``/v2/add-ons/saas/{resource_uuid}/plan``. - Replace ``{resource_uuid}`` with the UUID of the resource for which you want to change the - plan. + To update the name of an SSH key, send a PUT request to either ``/v2/account/keys/$SSH_KEY_ID`` + or ``/v2/account/keys/$SSH_KEY_FINGERPRINT``. Set the ``name`` attribute to the new name you + want to use. - :param resource_uuid: The UUID of the add-on resource to update. Required. - :type resource_uuid: str - :param body: Required. + :param ssh_key_identifier: Either the ID or the fingerprint of an existing SSH key. Required. + :type ssh_key_identifier: JSON + :param body: Set the ``name`` attribute to the new name you want to use. Required. :type body: JSON :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". @@ -13981,43 +14746,23 @@ async def patch_plan( # JSON input template you can fill out and use as your body input. body = { - "plan_slug": "str" # The slug identifier for the new plan to apply to the - add-on resource. Required. + "name": "str" # Optional. A human-readable display name for this key, used + to easily identify the SSH keys when they are displayed. } # response body for status code(s): 200 response == { - "resource": { - "app_slug": "str", # The slug identifier for the application - associated with the resource. Required. - "has_config": bool, # Indicates if the resource has configuration - values set by the vendor. Required. - "name": "str", # The name of the addon resource. Required. - "plan_slug": "str", # The slug identifier for the plan associated - with the resource. Required. - "state": "str", # The state the resource is currently in. Required. - Known values are: "pending", "provisioning", "provisioned", "deprovisioning", - "deprovisioned", "provisioning-failed", "deprovisioning-failed", and - "suspended". - "uuid": "str", # The unique identifier for the addon resource. - Required. - "app_name": "str", # Optional. The name of the application - associated with the resource. - "message": "str", # Optional. A message related to the resource, if - applicable. - "metadata": [ - { - "name": "str", # The name of the metadata item to be - set. Required. - "value": {} - } - ], - "plan_name": "str", # Optional. The name of the plan associated with - the resource. - "plan_price_per_month": 0, # Optional. The price of the plan per - month in US dollars. - "sso_url": "str" # Optional. The Single Sign-On URL for the - resource, if applicable. + "ssh_key": { + "name": "str", # A human-readable display name for this key, used to + easily identify the SSH keys when they are displayed. Required. + "public_key": "str", # The entire public key string that was + uploaded. Embedded into the root user's ``authorized_keys`` file if you + include this key during Droplet creation. Required. + "fingerprint": "str", # Optional. A unique identifier that + differentiates this key from other keys using a format that SSH recognizes. + The fingerprint is created when the key is added to your account. + "id": 0 # Optional. A unique identification number for this key. Can + be used to embed a specific SSH key into a Droplet. } } # response body for status code(s): 404 @@ -14034,25 +14779,24 @@ async def patch_plan( """ @overload - async def patch_plan( + async def update( self, - resource_uuid: str, + ssh_key_identifier: JSON, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any ) -> JSON: # pylint: disable=line-too-long - """Update the plan for an Add-On Resource. + """Update an SSH Key's Name. - To change the plan associated with an Add-On Resource, send a PATCH request to - ``/v2/add-ons/saas/{resource_uuid}/plan``. - Replace ``{resource_uuid}`` with the UUID of the resource for which you want to change the - plan. + To update the name of an SSH key, send a PUT request to either ``/v2/account/keys/$SSH_KEY_ID`` + or ``/v2/account/keys/$SSH_KEY_FINGERPRINT``. Set the ``name`` attribute to the new name you + want to use. - :param resource_uuid: The UUID of the add-on resource to update. Required. - :type resource_uuid: str - :param body: Required. + :param ssh_key_identifier: Either the ID or the fingerprint of an existing SSH key. Required. + :type ssh_key_identifier: JSON + :param body: Set the ``name`` attribute to the new name you want to use. Required. :type body: IO[bytes] :keyword content_type: Body Parameter content-type. Content type parameter for binary body. Default value is "application/json". @@ -14066,37 +14810,17 @@ async def patch_plan( # response body for status code(s): 200 response == { - "resource": { - "app_slug": "str", # The slug identifier for the application - associated with the resource. Required. - "has_config": bool, # Indicates if the resource has configuration - values set by the vendor. Required. - "name": "str", # The name of the addon resource. Required. - "plan_slug": "str", # The slug identifier for the plan associated - with the resource. Required. - "state": "str", # The state the resource is currently in. Required. - Known values are: "pending", "provisioning", "provisioned", "deprovisioning", - "deprovisioned", "provisioning-failed", "deprovisioning-failed", and - "suspended". - "uuid": "str", # The unique identifier for the addon resource. - Required. - "app_name": "str", # Optional. The name of the application - associated with the resource. - "message": "str", # Optional. A message related to the resource, if - applicable. - "metadata": [ - { - "name": "str", # The name of the metadata item to be - set. Required. - "value": {} - } - ], - "plan_name": "str", # Optional. The name of the plan associated with - the resource. - "plan_price_per_month": 0, # Optional. The price of the plan per - month in US dollars. - "sso_url": "str" # Optional. The Single Sign-On URL for the - resource, if applicable. + "ssh_key": { + "name": "str", # A human-readable display name for this key, used to + easily identify the SSH keys when they are displayed. Required. + "public_key": "str", # The entire public key string that was + uploaded. Embedded into the root user's ``authorized_keys`` file if you + include this key during Droplet creation. Required. + "fingerprint": "str", # Optional. A unique identifier that + differentiates this key from other keys using a format that SSH recognizes. + The fingerprint is created when the key is added to your account. + "id": 0 # Optional. A unique identification number for this key. Can + be used to embed a specific SSH key into a Droplet. } } # response body for status code(s): 404 @@ -14113,20 +14837,20 @@ async def patch_plan( """ @distributed_trace_async - async def patch_plan( - self, resource_uuid: str, body: Union[JSON, IO[bytes]], **kwargs: Any + async def update( + self, ssh_key_identifier: JSON, body: Union[JSON, IO[bytes]], **kwargs: Any ) -> JSON: # pylint: disable=line-too-long - """Update the plan for an Add-On Resource. + """Update an SSH Key's Name. - To change the plan associated with an Add-On Resource, send a PATCH request to - ``/v2/add-ons/saas/{resource_uuid}/plan``. - Replace ``{resource_uuid}`` with the UUID of the resource for which you want to change the - plan. + To update the name of an SSH key, send a PUT request to either ``/v2/account/keys/$SSH_KEY_ID`` + or ``/v2/account/keys/$SSH_KEY_FINGERPRINT``. Set the ``name`` attribute to the new name you + want to use. - :param resource_uuid: The UUID of the add-on resource to update. Required. - :type resource_uuid: str - :param body: Is either a JSON type or a IO[bytes] type. Required. + :param ssh_key_identifier: Either the ID or the fingerprint of an existing SSH key. Required. + :type ssh_key_identifier: JSON + :param body: Set the ``name`` attribute to the new name you want to use. Is either a JSON type + or a IO[bytes] type. Required. :type body: JSON or IO[bytes] :return: JSON object :rtype: JSON @@ -14137,43 +14861,23 @@ async def patch_plan( # JSON input template you can fill out and use as your body input. body = { - "plan_slug": "str" # The slug identifier for the new plan to apply to the - add-on resource. Required. + "name": "str" # Optional. A human-readable display name for this key, used + to easily identify the SSH keys when they are displayed. } # response body for status code(s): 200 response == { - "resource": { - "app_slug": "str", # The slug identifier for the application - associated with the resource. Required. - "has_config": bool, # Indicates if the resource has configuration - values set by the vendor. Required. - "name": "str", # The name of the addon resource. Required. - "plan_slug": "str", # The slug identifier for the plan associated - with the resource. Required. - "state": "str", # The state the resource is currently in. Required. - Known values are: "pending", "provisioning", "provisioned", "deprovisioning", - "deprovisioned", "provisioning-failed", "deprovisioning-failed", and - "suspended". - "uuid": "str", # The unique identifier for the addon resource. - Required. - "app_name": "str", # Optional. The name of the application - associated with the resource. - "message": "str", # Optional. A message related to the resource, if - applicable. - "metadata": [ - { - "name": "str", # The name of the metadata item to be - set. Required. - "value": {} - } - ], - "plan_name": "str", # Optional. The name of the plan associated with - the resource. - "plan_price_per_month": 0, # Optional. The price of the plan per - month in US dollars. - "sso_url": "str" # Optional. The Single Sign-On URL for the - resource, if applicable. + "ssh_key": { + "name": "str", # A human-readable display name for this key, used to + easily identify the SSH keys when they are displayed. Required. + "public_key": "str", # The entire public key string that was + uploaded. Embedded into the root user's ``authorized_keys`` file if you + include this key during Droplet creation. Required. + "fingerprint": "str", # Optional. A unique identifier that + differentiates this key from other keys using a format that SSH recognizes. + The fingerprint is created when the key is added to your account. + "id": 0 # Optional. A unique identification number for this key. Can + be used to embed a specific SSH key into a Droplet. } } # response body for status code(s): 404 @@ -14217,8 +14921,8 @@ async def patch_plan( else: _json = body - _request = build_addons_patch_plan_request( - resource_uuid=resource_uuid, + _request = build_ssh_keys_update_request( + ssh_key_identifier=ssh_key_identifier, content_type=content_type, json=_json, content=_content, @@ -14280,604 +14984,19379 @@ async def patch_plan( return cast(JSON, deserialized) # type: ignore - -class AppsOperations: # pylint: disable=too-many-public-methods - """ - .. warning:: - **DO NOT** instantiate this class directly. - - Instead, you should access the following operations through - :class:`~pydo.aio.GeneratedClient`'s - :attr:`apps` attribute. - """ - - def __init__(self, *args, **kwargs) -> None: - input_args = list(args) - self._client = input_args.pop(0) if input_args else kwargs.pop("client") - self._config = input_args.pop(0) if input_args else kwargs.pop("config") - self._serialize = input_args.pop(0) if input_args else kwargs.pop("serializer") - self._deserialize = ( - input_args.pop(0) if input_args else kwargs.pop("deserializer") - ) - @distributed_trace_async - async def list( - self, - *, - page: int = 1, - per_page: int = 20, - with_projects: Optional[bool] = None, - **kwargs: Any - ) -> JSON: + async def delete(self, ssh_key_identifier: JSON, **kwargs: Any) -> Optional[JSON]: # pylint: disable=line-too-long - """List All Apps. + """Delete an SSH Key. - List all apps on your account. Information about the current active deployment as well as any - in progress ones will also be included for each app. + To destroy a public SSH key that you have in your account, send a DELETE request to + ``/v2/account/keys/$KEY_ID`` or ``/v2/account/keys/$KEY_FINGERPRINT``. + A 204 status will be returned, indicating that the action was successful and that the response + body is empty. - :keyword page: Which 'page' of paginated results to return. Default value is 1. - :paramtype page: int - :keyword per_page: Number of items returned per page. Default value is 20. - :paramtype per_page: int - :keyword with_projects: Whether the project_id of listed apps should be fetched and included. - Default value is None. - :paramtype with_projects: bool - :return: JSON object - :rtype: JSON + :param ssh_key_identifier: Either the ID or the fingerprint of an existing SSH key. Required. + :type ssh_key_identifier: JSON + :return: JSON object or None + :rtype: JSON or None :raises ~azure.core.exceptions.HttpResponseError: Example: .. code-block:: python - # response body for status code(s): 200 + # response body for status code(s): 404 response == { - "meta": { - "total": 0 # Optional. Number of objects returned by the request. - }, - "apps": [ - { - "spec": { - "name": "str", # The name of the app. Must be unique - across all apps in the same account. Required. - "databases": [ - { - "name": "str", # The database's - name. The name must be unique across all components within - the same app and cannot use capital letters. Required. - "cluster_name": "str", # Optional. - The name of the underlying DigitalOcean DBaaS cluster. This - is required for production databases. For dev databases, if - cluster_name is not set, a new cluster will be provisioned. - "db_name": "str", # Optional. The - name of the MySQL or PostgreSQL database to configure. - "db_user": "str", # Optional. The - name of the MySQL or PostgreSQL user to configure. - "engine": "UNSET", # Optional. - Default value is "UNSET". * MYSQL: MySQL * PG: PostgreSQL * - REDIS: Caching * MONGODB: MongoDB * KAFKA: Kafka * - OPENSEARCH: OpenSearch * VALKEY: ValKey. Known values are: - "UNSET", "MYSQL", "PG", "REDIS", "MONGODB", "KAFKA", - "OPENSEARCH", and "VALKEY". - "production": bool, # Optional. - Whether this is a production or dev database. - "version": "str" # Optional. The - version of the database engine. - } + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[Optional[JSON]] = kwargs.pop("cls", None) + + _request = build_ssh_keys_delete_request( + ssh_key_identifier=ssh_key_identifier, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [204, 404]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + deserialized = None + response_headers = {} + if response.status_code == 204: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.status_code == 404: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, deserialized, response_headers) # type: ignore + + return deserialized # type: ignore + + +class ActionsOperations: + """ + .. warning:: + **DO NOT** instantiate this class directly. + + Instead, you should access the following operations through + :class:`~pydo.aio.GeneratedClient`'s + :attr:`actions` attribute. + """ + + def __init__(self, *args, **kwargs) -> None: + input_args = list(args) + self._client = input_args.pop(0) if input_args else kwargs.pop("client") + self._config = input_args.pop(0) if input_args else kwargs.pop("config") + self._serialize = input_args.pop(0) if input_args else kwargs.pop("serializer") + self._deserialize = ( + input_args.pop(0) if input_args else kwargs.pop("deserializer") + ) + + @distributed_trace_async + async def list(self, *, per_page: int = 20, page: int = 1, **kwargs: Any) -> JSON: + # pylint: disable=line-too-long + """List All Actions. + + This will be the entire list of actions taken on your account, so it will be quite large. As + with any large collection returned by the API, the results will be paginated with only 20 on + each page by default. + + :keyword per_page: Number of items returned per page. Default value is 20. + :paramtype per_page: int + :keyword page: Which 'page' of paginated results to return. Default value is 1. + :paramtype page: int + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 200 + response == { + "meta": { + "total": 0 # Optional. Number of objects returned by the request. + }, + "actions": [ + { + "completed_at": "2020-02-20 00:00:00", # Optional. A time + value given in ISO8601 combined date and time format that represents when + the action was completed. + "id": 0, # Optional. A unique numeric ID that can be used to + identify and reference an action. + "region": { + "available": bool, # This is a boolean value that + represents whether new Droplets can be created in this region. + Required. + "features": [ + "str" # This attribute is set to an array + which contains features available in this region. Required. ], - "disable_edge_cache": False, # Optional. Default - value is False. .. role:: raw-html-m2r(raw) :format: html If set - to ``true``"" , the app will **not** be cached at the edge (CDN). - Enable this option if you want to manage CDN configuration - yourself"u2014whether by using an external CDN provider or by - handling static content and caching within your app. This setting is - also recommended for apps that require real-time data or serve - dynamic content, such as those using Server-Sent Events (SSE) over - GET, or hosting an MCP (Model Context Protocol) Server that utilizes - SSE."" :raw-html-m2r:`
` **Note:** This feature is not available - for static site components."" :raw-html-m2r:`
` For more - information, see `Disable CDN Cache - `_. - "disable_email_obfuscation": False, # Optional. - Default value is False. If set to ``true``"" , email addresses in the - app will not be obfuscated. This is useful for apps that require - email addresses to be visible (in the HTML markup). - "domains": [ - { - "domain": "str", # The hostname for - the domain. Required. - "minimum_tls_version": "str", # - Optional. The minimum version of TLS a client application can - use to access resources for the domain. Must be one of the - following values wrapped within quotations: ``"1.2"`` or - ``"1.3"``. Known values are: "1.2" and "1.3". - "type": "UNSPECIFIED", # Optional. - Default value is "UNSPECIFIED". * DEFAULT: The default - ``.ondigitalocean.app`` domain assigned to this app * - PRIMARY: The primary domain for this app that is displayed as - the default in the control panel, used in bindable - environment variables, and any other places that reference an - app's live URL. Only one domain may be set as primary. * - ALIAS: A non-primary domain. Known values are: "UNSPECIFIED", - "DEFAULT", "PRIMARY", and "ALIAS". - "wildcard": bool, # Optional. - Indicates whether the domain includes all sub-domains, in - addition to the given domain. - "zone": "str" # Optional. Optional. - If the domain uses DigitalOcean DNS and you would like App - Platform to automatically manage it for you, set this to the - name of the domain on your account. For example, If the - domain you are adding is ``app.domain.com``"" , the zone - could be ``domain.com``. - } + "name": "str", # The display name of the region. + This will be a full name that is used in the control panel and other + interfaces. Required. + "sizes": [ + "str" # This attribute is set to an array + which contains the identifying slugs for the sizes available in + this region. sizes:read is required to view. Required. ], - "egress": { - "type": "AUTOASSIGN" # Optional. Default - value is "AUTOASSIGN". The app egress type. Known values are: - "AUTOASSIGN" and "DEDICATED_IP". - }, - "enhanced_threat_control_enabled": False, # - Optional. Default value is False. If set to ``true``"" , suspicious - requests will go through additional security checks to help mitigate - layer 7 DDoS attacks. - "functions": [ - { - "name": "str", # The name. Must be - unique across all components within the same app. Required. - "alerts": [ - { - "disabled": bool, # - Optional. Is the alert disabled?. - "operator": - "UNSPECIFIED_OPERATOR", # Optional. Default value is - "UNSPECIFIED_OPERATOR". Known values are: - "UNSPECIFIED_OPERATOR", "GREATER_THAN", and - "LESS_THAN". - "rule": - "UNSPECIFIED_RULE", # Optional. Default value is - "UNSPECIFIED_RULE". Known values are: - "UNSPECIFIED_RULE", "CPU_UTILIZATION", - "MEM_UTILIZATION", "RESTART_COUNT", - "DEPLOYMENT_FAILED", "DEPLOYMENT_LIVE", - "DOMAIN_FAILED", "DOMAIN_LIVE", "AUTOSCALE_FAILED", - "AUTOSCALE_SUCCEEDED", "FUNCTIONS_ACTIVATION_COUNT", - "FUNCTIONS_AVERAGE_DURATION_MS", - "FUNCTIONS_ERROR_RATE_PER_MINUTE", - "FUNCTIONS_AVERAGE_WAIT_TIME_MS", - "FUNCTIONS_ERROR_COUNT", - "FUNCTIONS_GB_RATE_PER_SECOND", - "REQUESTS_PER_SECOND", and "REQUEST_DURATION_P95_MS". - "value": 0.0, # - Optional. Threshold value for alert. - "window": - "UNSPECIFIED_WINDOW" # Optional. Default value is - "UNSPECIFIED_WINDOW". Known values are: - "UNSPECIFIED_WINDOW", "FIVE_MINUTES", "TEN_MINUTES", - "THIRTY_MINUTES", and "ONE_HOUR". - } - ], - "bitbucket": { - "branch": "str", # Optional. - The name of the branch to use. - "deploy_on_push": bool, # - Optional. Whether to automatically deploy new commits - made to the repo. - "repo": "str" # Optional. - The name of the repo in the format owner/repo. Example: - ``digitalocean/sample-golang``. - }, - "cors": { - "allow_credentials": bool, # - Optional. Whether browsers should expose the response to - the client-side JavaScript code when the request"u2019s - credentials mode is include. This configures the - ``Access-Control-Allow-Credentials`` header. - "allow_headers": [ - "str" # Optional. - The set of allowed HTTP request headers. This - configures the ``Access-Control-Allow-Headers`` - header. - ], - "allow_methods": [ - "str" # Optional. - The set of allowed HTTP methods. This configures the - ``Access-Control-Allow-Methods`` header. - ], - "allow_origins": [ + "slug": "str" # A human-readable string that is used + as a unique identifier for each region. Required. + }, + "region_slug": "str", # Optional. A human-readable string + that is used as a unique identifier for each region. + "resource_id": 0, # Optional. A unique identifier for the + resource that the action is associated with. + "resource_type": "str", # Optional. The type of resource + that the action is associated with. + "started_at": "2020-02-20 00:00:00", # Optional. A time + value given in ISO8601 combined date and time format that represents when + the action was initiated. + "status": "in-progress", # Optional. Default value is + "in-progress". The current status of the action. This can be + "in-progress", "completed", or "errored". Known values are: + "in-progress", "completed", and "errored". + "type": "str" # Optional. This is the type of action that + the object represents. For example, this could be "transfer" to represent + the state of an image transfer action. + } + ], + "links": { + "pages": {} + } + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[JSON] = kwargs.pop("cls", None) + + _request = build_actions_list_request( + per_page=per_page, + page=page, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [200]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + response_headers = {} + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore + + return cast(JSON, deserialized) # type: ignore + + @distributed_trace_async + async def get(self, action_id: int, **kwargs: Any) -> JSON: + # pylint: disable=line-too-long + """Retrieve an Existing Action. + + To retrieve a specific action object, send a GET request to ``/v2/actions/$ACTION_ID``. + + :param action_id: A unique numeric ID that can be used to identify and reference an action. + Required. + :type action_id: int + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 200 + response == { + "action": { + "completed_at": "2020-02-20 00:00:00", # Optional. A time value + given in ISO8601 combined date and time format that represents when the + action was completed. + "id": 0, # Optional. A unique numeric ID that can be used to + identify and reference an action. + "region": { + "available": bool, # This is a boolean value that represents + whether new Droplets can be created in this region. Required. + "features": [ + "str" # This attribute is set to an array which + contains features available in this region. Required. + ], + "name": "str", # The display name of the region. This will + be a full name that is used in the control panel and other interfaces. + Required. + "sizes": [ + "str" # This attribute is set to an array which + contains the identifying slugs for the sizes available in this + region. sizes:read is required to view. Required. + ], + "slug": "str" # A human-readable string that is used as a + unique identifier for each region. Required. + }, + "region_slug": "str", # Optional. A human-readable string that is + used as a unique identifier for each region. + "resource_id": 0, # Optional. A unique identifier for the resource + that the action is associated with. + "resource_type": "str", # Optional. The type of resource that the + action is associated with. + "started_at": "2020-02-20 00:00:00", # Optional. A time value given + in ISO8601 combined date and time format that represents when the action was + initiated. + "status": "in-progress", # Optional. Default value is "in-progress". + The current status of the action. This can be "in-progress", "completed", or + "errored". Known values are: "in-progress", "completed", and "errored". + "type": "str" # Optional. This is the type of action that the object + represents. For example, this could be "transfer" to represent the state of + an image transfer action. + } + } + # response body for status code(s): 404 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[JSON] = kwargs.pop("cls", None) + + _request = build_actions_get_request( + action_id=action_id, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [200, 404]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + response_headers = {} + if response.status_code == 200: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 404: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore + + return cast(JSON, deserialized) # type: ignore + + +class AddonsOperations: + """ + .. warning:: + **DO NOT** instantiate this class directly. + + Instead, you should access the following operations through + :class:`~pydo.aio.GeneratedClient`'s + :attr:`addons` attribute. + """ + + def __init__(self, *args, **kwargs) -> None: + input_args = list(args) + self._client = input_args.pop(0) if input_args else kwargs.pop("client") + self._config = input_args.pop(0) if input_args else kwargs.pop("config") + self._serialize = input_args.pop(0) if input_args else kwargs.pop("serializer") + self._deserialize = ( + input_args.pop(0) if input_args else kwargs.pop("deserializer") + ) + + @distributed_trace_async + async def get_app(self, **kwargs: Any) -> JSON: + # pylint: disable=line-too-long + """List Available Add-On Applications. + + To fetch details of all available Add-On Applications, send a GET request to + ``/v2/add-ons/apps``. + + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 200 + response == { + "apps": [ + { + "app_slug": "str", # The slug identifier for the application + associated with the resource. Required. + "eula": "str", # The End User License Agreement URL for the + resource. Required. + "plans": [ + { + "active": bool, # Indicates if the plan is + currently active. Required. + "app_id": 0, # ID of the app associated with + this plan. Required. + "available": bool, # Indicates if the plan + is available for selection. Required. + "by_default": bool, # Indicates if this plan + is the default option for the app. Required. + "created_at": "2020-02-20 00:00:00", # + Timestamp when the plan was created. Required. + "display_name": "str", # Display name for a + given plan. Required. + "id": 0, # ID of a given plan. Required. + "price_per_month": 0, # Price of a month's + usage of the plan in US dollars. Required. + "slug": "str", # Slug identifier for the + plan. Required. + "state": "str", # Current state of the plan. + Required. Known values are: "unknown", "draft", "in_review", + "approved", "suspended", and "archived". + "updated_at": "2020-02-20 00:00:00", # + Timestamp when the plan was last updated. Required. + "uuid": "str", # Unique identifier for the + plan. Required. + "description": "str", # Optional. + Description of an app plan. + "dimensions": [ + { + "display_name": "str", # + Display name for the dimension. Required. + "feature_name": "str", # + Name of the feature associated with the dimension. + Required. + "id": 0, # Unique identifier + for the dimension. Required. + "sku": "str", # Unique + string identifier for the dimension, tied to a price. + Required. + "slug": "str", # Slug + identifier for the dimension. Required. + "volumes": [ { - "exact": - "str", # Optional. Exact string match. Only 1 of - ``exact``"" , ``prefix``"" , or ``regex`` must be - set. An empty string is a valid value and will be - serialized explicitly rather than being omitted - from the payload. - "prefix": - "str", # Optional. Prefix-based match. Only 1 of - ``exact``"" , ``prefix``"" , or ``regex`` must be - set. - "regex": - "str" # Optional. RE2 style regex-based match. - Only 1 of ``exact``"" , ``prefix``"" , or - ``regex`` must be set. For more information about - RE2 syntax, see: - https://github.com/google/re2/wiki/Syntax. - } - ], - "expose_headers": [ - "str" # Optional. - The set of HTTP response headers that browsers are - allowed to access. This configures the - ``Access-Control-Expose-Headers`` header. - ], - "max_age": "str" # Optional. - An optional duration specifying how long browsers can - cache the results of a preflight request. This configures - the ``Access-Control-Max-Age`` header. - }, - "envs": [ - { - "key": "str", # The - variable name. Required. - "scope": - "RUN_AND_BUILD_TIME", # Optional. Default value is - "RUN_AND_BUILD_TIME". * RUN_TIME: Made available only - at run-time * BUILD_TIME: Made available only at - build-time * RUN_AND_BUILD_TIME: Made available at - both build and run-time. Known values are: "UNSET", - "RUN_TIME", "BUILD_TIME", and "RUN_AND_BUILD_TIME". - "type": "GENERAL", # - Optional. Default value is "GENERAL". * GENERAL: A - plain-text environment variable * SECRET: A secret - encrypted environment variable. Known values are: - "GENERAL" and "SECRET". - "value": "str" # - Optional. The value. If the type is ``SECRET``"" , - the value will be encrypted on first submission. On - following submissions, the encrypted value should be - used. - } - ], - "git": { - "branch": "str", # Optional. - The name of the branch to use. - "repo_clone_url": "str" # - Optional. The clone URL of the repo. Example: - ``https://github.com/digitalocean/sample-golang.git``. - }, - "github": { - "branch": "str", # Optional. - The name of the branch to use. - "deploy_on_push": bool, # - Optional. Whether to automatically deploy new commits - made to the repo. - "repo": "str" # Optional. - The name of the repo in the format owner/repo. Example: - ``digitalocean/sample-golang``. - }, - "gitlab": { - "branch": "str", # Optional. - The name of the branch to use. - "deploy_on_push": bool, # - Optional. Whether to automatically deploy new commits - made to the repo. - "repo": "str" # Optional. - The name of the repo in the format owner/repo. Example: - ``digitalocean/sample-golang``. - }, - "log_destinations": [ - { - "name": "str", # - Required. - "datadog": { - "api_key": - "str", # Datadog API key. Required. - "endpoint": - "str" # Optional. Datadog HTTP log intake - endpoint. - }, - "logtail": { - "token": - "str" # Optional. Logtail token. - }, - "open_search": { - "basic_auth": - { - "password": "str", # Optional. Password for - user defined in User. Is required when - ``endpoint`` is set. Cannot be set if using a - DigitalOcean DBaaS OpenSearch cluster. - "user": "str" # Optional. Username to - authenticate with. Only required when - ``endpoint`` is set. Defaults to ``doadmin`` - when ``cluster_name`` is set. - }, - "cluster_name": "str", # Optional. The name of a - DigitalOcean DBaaS OpenSearch cluster to use as a - log forwarding destination. Cannot be specified - if ``endpoint`` is also specified. - "endpoint": - "str", # Optional. OpenSearch API Endpoint. Only - HTTPS is supported. Format: - https://:code:``::code:``. Cannot be - specified if ``cluster_name`` is also specified. - "index_name": - "logs" # Optional. Default value is "logs". The - index name to use for the logs. If not set, the - default index name is "logs". - }, - "papertrail": { - "endpoint": - "str" # Papertrail syslog endpoint. Required. + "id": 0, # + Unique identifier for the addon. Required. + "low_volume": + 0, # The minimum volume for the volume pricing + tier. Required. + "max_volume": + 0, # The maximum volume for the volume pricing + tier. Required. + "price_per_unit": "str" # The price per unit for + the volume tier in US dollars. Required. } - } - ], - "routes": [ - { - "path": "str", # - Optional. (Deprecated - Use Ingress Rules instead). - An HTTP path prefix. Paths must start with / and must - be unique across all components within an app. - "preserve_path_prefix": bool # Optional. An optional - flag to preserve the path that is forwarded to the - backend service. By default, the HTTP request path - will be trimmed from the left when forwarded to the - component. For example, a component with - ``path=/api`` will have requests to ``/api/list`` - trimmed to ``/list``. If this value is ``true``"" , - the path will remain ``/api/list``. - } - ], - "source_dir": "str" # Optional. An - optional path to the working directory to use for the build. - For Dockerfile builds, this will be used as the build - context. Must be relative to the root of the repo. - } - ], - "ingress": { - "custom_error_page_url": "str", # Optional. - Optional HTTPS URL of a custom error page to display when the app - is unreachable. The page is shown in a full-viewport iframe. The - target must allow framing: avoid ``X-Frame-Options: DENY`` and a - restrictive ``Content-Security-Policy`` ``frame-ancestors`` that - blocks the platform. If omitted, the default platform error page - is used. - "rules": [ + ] + } + ], + "features": [ { - "component": { - "name": "str", # The - name of the component to route to. Required. - "preserve_path_prefix": "str", # Optional. An - optional flag to preserve the path that is forwarded - to the backend service. By default, the HTTP request - path will be trimmed from the left when forwarded to - the component. For example, a component with - ``path=/api`` will have requests to ``/api/list`` - trimmed to ``/list``. If this value is ``true``"" , - the path will remain ``/api/list``. Note: this is not - applicable for Functions Components and is mutually - exclusive with ``rewrite``. - "rewrite": "str" # - Optional. An optional field that will rewrite the - path of the component to be what is specified here. - By default, the HTTP request path will be trimmed - from the left when forwarded to the component. For - example, a component with ``path=/api`` will have - requests to ``/api/list`` trimmed to ``/list``. If - you specified the rewrite to be ``/v1/``"" , requests - to ``/api/list`` would be rewritten to ``/v1/list``. - Note: this is mutually exclusive with - ``preserve_path_prefix``. - }, - "cors": { - "allow_credentials": - bool, # Optional. Whether browsers should expose the - response to the client-side JavaScript code when the - request"u2019s credentials mode is include. This - configures the ``Access-Control-Allow-Credentials`` - header. - "allow_headers": [ - "str" # - Optional. The set of allowed HTTP request - headers. This configures the - ``Access-Control-Allow-Headers`` header. - ], - "allow_methods": [ - "str" # - Optional. The set of allowed HTTP methods. This - configures the ``Access-Control-Allow-Methods`` - header. - ], - "allow_origins": [ - { - "exact": "str", # Optional. Exact string - match. Only 1 of ``exact``"" , ``prefix``"" , - or ``regex`` must be set. An empty string is - a valid value and will be serialized - explicitly rather than being omitted from the - payload. - "prefix": "str", # Optional. Prefix-based - match. Only 1 of ``exact``"" , ``prefix``"" , - or ``regex`` must be set. - "regex": "str" # Optional. RE2 style - regex-based match. Only 1 of ``exact``"" , - ``prefix``"" , or ``regex`` must be set. For - more information about RE2 syntax, see: - https://github.com/google/re2/wiki/Syntax. - } - ], - "expose_headers": [ - "str" # - Optional. The set of HTTP response headers that - browsers are allowed to access. This configures - the ``Access-Control-Expose-Headers`` header. - ], - "max_age": "str" # - Optional. An optional duration specifying how long - browsers can cache the results of a preflight - request. This configures the - ``Access-Control-Max-Age`` header. - }, - "match": { - "authority": { - "exact": - "str" # Exact string match. An empty string is a - valid value and will be serialized explicitly - rather than being omitted from the payload. - Required. - }, - "path": { - "prefix": - "str" # Prefix-based match. For example, - ``/api`` will match ``/api``"" , ``/api/``"" , - and any nested paths such as - ``/api/v1/endpoint``. Required. - } - }, - "redirect": { - "authority": "str", - # Optional. The authority/host to redirect to. This - can be a hostname or IP address. Note: use ``port`` - to set the port. - "port": 0, # - Optional. The port to redirect to. - "redirect_code": 0, - # Optional. The redirect code to use. Defaults to - ``302``. Supported values are 300, 301, 302, 303, - 304, 307, 308. - "scheme": "str", # - Optional. The scheme to redirect to. Supported values - are ``http`` or ``https``. Default: ``https``. - "uri": "str" # - Optional. An optional URI path to redirect to. Note: - if this is specified the whole URI of the original - request will be overwritten to this value, - irrespective of the original request URI being - matched. - } + "created_at": "2020-02-20 + 00:00:00", # Timestamp when the feature was created. + Required. + "id": 0, # Unique identifier + for the app feature. Required. + "name": "str", # Name of the + feature. Required. + "type": "str", # Feature + type, indicating the kind of data it holds. Required. + Known values are: "unknown", "string", "boolean", and + "allowance". + "updated_at": "2020-02-20 + 00:00:00", # Timestamp when the feature was last + updated. Required. + "value": {}, + "unit": "str" # Optional. + Unit of measurement for the feature, if applicable. Units + apply to allowance features. Known values are: + "unit_unknown", "GB", "GIB", "count", "byte", and + "byte_second". } ] - }, - "jobs": [ - { - "bitbucket": { - "branch": "str", # Optional. - The name of the branch to use. - "deploy_on_push": bool, # - Optional. Whether to automatically deploy new commits - made to the repo. - "repo": "str" # Optional. - The name of the repo in the format owner/repo. Example: - ``digitalocean/sample-golang``. - }, - "build_command": "str", # Optional. - An optional build command to run while building this - component from source. - "dockerfile_path": "str", # - Optional. The path to the Dockerfile relative to the root of - the repo. If set, it will be used to build this component. - Otherwise, App Platform will attempt to build it using - buildpacks. - "environment_slug": "str", # - Optional. An environment slug describing the type of this - app. For a full list, please refer to `the product - documentation - `_. - "envs": [ - { - "key": "str", # The - variable name. Required. - "scope": - "RUN_AND_BUILD_TIME", # Optional. Default value is - "RUN_AND_BUILD_TIME". * RUN_TIME: Made available only - at run-time * BUILD_TIME: Made available only at - build-time * RUN_AND_BUILD_TIME: Made available at - both build and run-time. Known values are: "UNSET", - "RUN_TIME", "BUILD_TIME", and "RUN_AND_BUILD_TIME". - "type": "GENERAL", # - Optional. Default value is "GENERAL". * GENERAL: A - plain-text environment variable * SECRET: A secret - encrypted environment variable. Known values are: - "GENERAL" and "SECRET". - "value": "str" # - Optional. The value. If the type is ``SECRET``"" , - the value will be encrypted on first submission. On - following submissions, the encrypted value should be - used. - } - ], - "git": { - "branch": "str", # Optional. - The name of the branch to use. - "repo_clone_url": "str" # - Optional. The clone URL of the repo. Example: - ``https://github.com/digitalocean/sample-golang.git``. - }, - "github": { - "branch": "str", # Optional. - The name of the branch to use. - "deploy_on_push": bool, # - Optional. Whether to automatically deploy new commits - made to the repo. - "repo": "str" # Optional. - The name of the repo in the format owner/repo. Example: - ``digitalocean/sample-golang``. - }, - "gitlab": { - "branch": "str", # Optional. - The name of the branch to use. - "deploy_on_push": bool, # - Optional. Whether to automatically deploy new commits - made to the repo. - "repo": "str" # Optional. - The name of the repo in the format owner/repo. Example: - ``digitalocean/sample-golang``. - }, - "image": { - "deploy_on_push": { - "enabled": bool # - Optional. Whether to automatically deploy new images. - Can only be used for images hosted in DOCR and can - only be used with an image tag, not a specific - digest. - }, - "digest": "str", # Optional. - The image digest. Cannot be specified if tag is provided. - "registry": "str", # - Optional. The registry name. Must be left empty for the - ``DOCR`` registry type. - "registry_credentials": - "str", # Optional. The credentials to be able to pull - the image. The value will be encrypted on first - submission. On following submissions, the encrypted value - should be used. * "$username:$access_token" for - registries of type ``DOCKER_HUB``. * - "$username:$access_token" for registries of type - ``GHCR``. - "registry_type": "str", # - Optional. * DOCKER_HUB: The DockerHub container registry - type. * DOCR: The DigitalOcean container registry type. * - GHCR: The Github container registry type. Known values - are: "DOCKER_HUB", "DOCR", and "GHCR". - "repository": "str", # - Optional. The repository name. - "tag": "latest" # Optional. - Default value is "latest". The repository tag. Defaults - to ``latest`` if not provided and no digest is provided. - Cannot be specified if digest is provided. - }, - "instance_count": 1, # Optional. - Default value is 1. The amount of instances that this - component should be scaled to. Default: 1. Must not be set if - autoscaling is used. - "instance_size_slug": {}, - "kind": "UNSPECIFIED", # Optional. - Default value is "UNSPECIFIED". * UNSPECIFIED: Default job - type, will auto-complete to POST_DEPLOY kind. * PRE_DEPLOY: - Indicates a job that runs before an app deployment. * - POST_DEPLOY: Indicates a job that runs after an app - deployment. * FAILED_DEPLOY: Indicates a job that runs after - a component fails to deploy. Known values are: "UNSPECIFIED", - "PRE_DEPLOY", "POST_DEPLOY", and "FAILED_DEPLOY". - "log_destinations": [ + } + ], + "tos": "str" # The Terms of Service URL for the resource. + Required. + } + ] + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[JSON] = kwargs.pop("cls", None) + + _request = build_addons_get_app_request( + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [200]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + response_headers = {} + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore + + return cast(JSON, deserialized) # type: ignore + + @distributed_trace_async + async def get_app_metadata(self, app_slug: str, **kwargs: Any) -> JSON: + # pylint: disable=line-too-long + """Get Metadata for an Add-On Application. + + To find out what metadata is required for a specific add-on, send a GET request to + ``/v2/add-ons/apps/{app_slug}/metadata``. + Metadata varies by application. + + :param app_slug: The slug identifier for the application whose metadata is being requested. + Required. + :type app_slug: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 200 + response == { + "metadata": [ + { + "description": "str", # A brief description of the metadata + item. Required. + "display_name": "str", # The display name of the metadata + item. Required. + "id": 0, # Unique identifier for the addon metadata item. + Required. + "name": "str", # The name of the metadata item. Required. + "type": "str", # The data type of the metadata value. + Required. Known values are: "string" and "boolean". + "options": [ + "str" # Optional. options. + ] + } + ] + } + # response body for status code(s): 404 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[JSON] = kwargs.pop("cls", None) + + _request = build_addons_get_app_metadata_request( + app_slug=app_slug, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [200, 404]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + response_headers = {} + if response.status_code == 200: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 404: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore + + return cast(JSON, deserialized) # type: ignore + + @distributed_trace_async + async def list(self, **kwargs: Any) -> JSON: + # pylint: disable=line-too-long + """List all Add-On Resources. + + To fetch all Add-On Resources under your team, send a GET request to ``/v2/add-ons/saas``. + + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 200 + response == { + "resources": [ + { + "app_slug": "str", # The slug identifier for the application + associated with the resource. Required. + "has_config": bool, # Indicates if the resource has + configuration values set by the vendor. Required. + "name": "str", # The name of the addon resource. Required. + "plan_slug": "str", # The slug identifier for the plan + associated with the resource. Required. + "state": "str", # The state the resource is currently in. + Required. Known values are: "pending", "provisioning", "provisioned", + "deprovisioning", "deprovisioned", "provisioning-failed", + "deprovisioning-failed", and "suspended". + "uuid": "str", # The unique identifier for the addon + resource. Required. + "app_name": "str", # Optional. The name of the application + associated with the resource. + "message": "str", # Optional. A message related to the + resource, if applicable. + "metadata": [ + { + "name": "str", # The name of the metadata + item to be set. Required. + "value": {} + } + ], + "plan_name": "str", # Optional. The name of the plan + associated with the resource. + "plan_price_per_month": 0, # Optional. The price of the plan + per month in US dollars. + "sso_url": "str" # Optional. The Single Sign-On URL for the + resource, if applicable. + } + ] + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[JSON] = kwargs.pop("cls", None) + + _request = build_addons_list_request( + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [200]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + response_headers = {} + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore + + return cast(JSON, deserialized) # type: ignore + + @overload + async def create( + self, body: JSON, *, content_type: str = "application/json", **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """Create/Provision a New Add-on Resource. + + To create an add-on resource, send a POST request to ``/v2/add-ons/saas`` with required + parameters. + Some add-ons require additional metadata to be provided in the request body. To find out + what metadata is required for a specific add-on, send a GET request to + ``/v2/add-ons/apps/{app_slug}/metadata``. + + :param body: Required. + :type body: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # JSON input template you can fill out and use as your body input. + body = { + "app_slug": "str", # The slug identifier for the application associated with + the resource. Required. + "metadata": [ + { + "name": "str", # The name of the metadata item to be set. + Required. + "value": {} + } + ], + "name": "str", # The name of the addon resource. Required. + "plan_slug": "str", # The slug identifier for the plan associated with the + resource. Required. + "fleet_uuid": "str", # Optional. UUID of the fleet/project to which this + resource will belong. + "linked_droplet_id": 0 # Optional. ID of the droplet to be linked to this + resource, if applicable. + } + + # response body for status code(s): 200 + response == { + "resource": { + "app_slug": "str", # The slug identifier for the application + associated with the resource. Required. + "has_config": bool, # Indicates if the resource has configuration + values set by the vendor. Required. + "name": "str", # The name of the addon resource. Required. + "plan_slug": "str", # The slug identifier for the plan associated + with the resource. Required. + "state": "str", # The state the resource is currently in. Required. + Known values are: "pending", "provisioning", "provisioned", "deprovisioning", + "deprovisioned", "provisioning-failed", "deprovisioning-failed", and + "suspended". + "uuid": "str", # The unique identifier for the addon resource. + Required. + "app_name": "str", # Optional. The name of the application + associated with the resource. + "message": "str", # Optional. A message related to the resource, if + applicable. + "metadata": [ + { + "name": "str", # The name of the metadata item to be + set. Required. + "value": {} + } + ], + "plan_name": "str", # Optional. The name of the plan associated with + the resource. + "plan_price_per_month": 0, # Optional. The price of the plan per + month in US dollars. + "sso_url": "str" # Optional. The Single Sign-On URL for the + resource, if applicable. + } + } + """ + + @overload + async def create( + self, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """Create/Provision a New Add-on Resource. + + To create an add-on resource, send a POST request to ``/v2/add-ons/saas`` with required + parameters. + Some add-ons require additional metadata to be provided in the request body. To find out + what metadata is required for a specific add-on, send a GET request to + ``/v2/add-ons/apps/{app_slug}/metadata``. + + :param body: Required. + :type body: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 200 + response == { + "resource": { + "app_slug": "str", # The slug identifier for the application + associated with the resource. Required. + "has_config": bool, # Indicates if the resource has configuration + values set by the vendor. Required. + "name": "str", # The name of the addon resource. Required. + "plan_slug": "str", # The slug identifier for the plan associated + with the resource. Required. + "state": "str", # The state the resource is currently in. Required. + Known values are: "pending", "provisioning", "provisioned", "deprovisioning", + "deprovisioned", "provisioning-failed", "deprovisioning-failed", and + "suspended". + "uuid": "str", # The unique identifier for the addon resource. + Required. + "app_name": "str", # Optional. The name of the application + associated with the resource. + "message": "str", # Optional. A message related to the resource, if + applicable. + "metadata": [ + { + "name": "str", # The name of the metadata item to be + set. Required. + "value": {} + } + ], + "plan_name": "str", # Optional. The name of the plan associated with + the resource. + "plan_price_per_month": 0, # Optional. The price of the plan per + month in US dollars. + "sso_url": "str" # Optional. The Single Sign-On URL for the + resource, if applicable. + } + } + """ + + @distributed_trace_async + async def create(self, body: Union[JSON, IO[bytes]], **kwargs: Any) -> JSON: + # pylint: disable=line-too-long + """Create/Provision a New Add-on Resource. + + To create an add-on resource, send a POST request to ``/v2/add-ons/saas`` with required + parameters. + Some add-ons require additional metadata to be provided in the request body. To find out + what metadata is required for a specific add-on, send a GET request to + ``/v2/add-ons/apps/{app_slug}/metadata``. + + :param body: Is either a JSON type or a IO[bytes] type. Required. + :type body: JSON or IO[bytes] + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # JSON input template you can fill out and use as your body input. + body = { + "app_slug": "str", # The slug identifier for the application associated with + the resource. Required. + "metadata": [ + { + "name": "str", # The name of the metadata item to be set. + Required. + "value": {} + } + ], + "name": "str", # The name of the addon resource. Required. + "plan_slug": "str", # The slug identifier for the plan associated with the + resource. Required. + "fleet_uuid": "str", # Optional. UUID of the fleet/project to which this + resource will belong. + "linked_droplet_id": 0 # Optional. ID of the droplet to be linked to this + resource, if applicable. + } + + # response body for status code(s): 200 + response == { + "resource": { + "app_slug": "str", # The slug identifier for the application + associated with the resource. Required. + "has_config": bool, # Indicates if the resource has configuration + values set by the vendor. Required. + "name": "str", # The name of the addon resource. Required. + "plan_slug": "str", # The slug identifier for the plan associated + with the resource. Required. + "state": "str", # The state the resource is currently in. Required. + Known values are: "pending", "provisioning", "provisioned", "deprovisioning", + "deprovisioned", "provisioning-failed", "deprovisioning-failed", and + "suspended". + "uuid": "str", # The unique identifier for the addon resource. + Required. + "app_name": "str", # Optional. The name of the application + associated with the resource. + "message": "str", # Optional. A message related to the resource, if + applicable. + "metadata": [ + { + "name": "str", # The name of the metadata item to be + set. Required. + "value": {} + } + ], + "plan_name": "str", # Optional. The name of the plan associated with + the resource. + "plan_price_per_month": 0, # Optional. The price of the plan per + month in US dollars. + "sso_url": "str" # Optional. The Single Sign-On URL for the + resource, if applicable. + } + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop( + "content_type", _headers.pop("Content-Type", None) + ) + cls: ClsType[JSON] = kwargs.pop("cls", None) + + content_type = content_type or "application/json" + _json = None + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _json = body + + _request = build_addons_create_request( + content_type=content_type, + json=_json, + content=_content, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [200]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + response_headers = {} + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore + + return cast(JSON, deserialized) # type: ignore + + @distributed_trace_async + async def get(self, resource_uuid: str, **kwargs: Any) -> JSON: + # pylint: disable=line-too-long + """Get details on an Add-On Resource. + + To fetch details of a specific Add-On Resource, send a GET request to + ``/v2/add-ons/saas/{resource_uuid}``. + Replace ``{resource_uuid}`` with the UUID of the resource you want to retrieve. + + :param resource_uuid: The UUID of the add-on resource to retrieve. Required. + :type resource_uuid: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 200 + response == { + "resource": { + "app_slug": "str", # The slug identifier for the application + associated with the resource. Required. + "has_config": bool, # Indicates if the resource has configuration + values set by the vendor. Required. + "name": "str", # The name of the addon resource. Required. + "plan_slug": "str", # The slug identifier for the plan associated + with the resource. Required. + "state": "str", # The state the resource is currently in. Required. + Known values are: "pending", "provisioning", "provisioned", "deprovisioning", + "deprovisioned", "provisioning-failed", "deprovisioning-failed", and + "suspended". + "uuid": "str", # The unique identifier for the addon resource. + Required. + "app_name": "str", # Optional. The name of the application + associated with the resource. + "message": "str", # Optional. A message related to the resource, if + applicable. + "metadata": [ + { + "name": "str", # The name of the metadata item to be + set. Required. + "value": {} + } + ], + "plan_name": "str", # Optional. The name of the plan associated with + the resource. + "plan_price_per_month": 0, # Optional. The price of the plan per + month in US dollars. + "sso_url": "str" # Optional. The Single Sign-On URL for the + resource, if applicable. + } + } + # response body for status code(s): 404 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[JSON] = kwargs.pop("cls", None) + + _request = build_addons_get_request( + resource_uuid=resource_uuid, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [200, 404]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + response_headers = {} + if response.status_code == 200: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 404: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore + + return cast(JSON, deserialized) # type: ignore + + @distributed_trace_async + async def delete(self, resource_uuid: str, **kwargs: Any) -> Optional[JSON]: + # pylint: disable=line-too-long + """Delete/Deprovision an Add-on Resource. + + To delete an add-on resource, send a DELETE request to ``/v2/add-ons/saas/{resource_uuid}`` + with the UUID of the resource to delete. + You cannot retrieve the resource after it has been deleted. The response indicates a request + was sent to the 3rd party add-on provider to delete the resource. + You will no longer be billed for this resource. + + :param resource_uuid: A unique identifier for the add-on resource. Required. + :type resource_uuid: str + :return: JSON object or None + :rtype: JSON or None + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 404 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[Optional[JSON]] = kwargs.pop("cls", None) + + _request = build_addons_delete_request( + resource_uuid=resource_uuid, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [200, 404]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + deserialized = None + response_headers = {} + if response.status_code == 200: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.status_code == 404: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, deserialized, response_headers) # type: ignore + + return deserialized # type: ignore + + @overload + async def patch( + self, + resource_uuid: str, + body: JSON, + *, + content_type: str = "application/json", + **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """Update the name for an Add-On Resource. + + To change the name of an Add-On Resource, send a PATCH request to + ``/v2/add-ons/saas/{resource_uuid}``. + Replace ``{resource_uuid}`` with the UUID of the resource for which you want to change the + name. + + :param resource_uuid: The UUID of the add-on resource to rename. Required. + :type resource_uuid: str + :param body: Required. + :type body: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # JSON input template you can fill out and use as your body input. + body = { + "name": "str" # The new name for the add-on resource. Required. + } + + # response body for status code(s): 200 + response == { + "resource": { + "app_slug": "str", # The slug identifier for the application + associated with the resource. Required. + "has_config": bool, # Indicates if the resource has configuration + values set by the vendor. Required. + "name": "str", # The name of the addon resource. Required. + "plan_slug": "str", # The slug identifier for the plan associated + with the resource. Required. + "state": "str", # The state the resource is currently in. Required. + Known values are: "pending", "provisioning", "provisioned", "deprovisioning", + "deprovisioned", "provisioning-failed", "deprovisioning-failed", and + "suspended". + "uuid": "str", # The unique identifier for the addon resource. + Required. + "app_name": "str", # Optional. The name of the application + associated with the resource. + "message": "str", # Optional. A message related to the resource, if + applicable. + "metadata": [ + { + "name": "str", # The name of the metadata item to be + set. Required. + "value": {} + } + ], + "plan_name": "str", # Optional. The name of the plan associated with + the resource. + "plan_price_per_month": 0, # Optional. The price of the plan per + month in US dollars. + "sso_url": "str" # Optional. The Single Sign-On URL for the + resource, if applicable. + } + } + # response body for status code(s): 404 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + + @overload + async def patch( + self, + resource_uuid: str, + body: IO[bytes], + *, + content_type: str = "application/json", + **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """Update the name for an Add-On Resource. + + To change the name of an Add-On Resource, send a PATCH request to + ``/v2/add-ons/saas/{resource_uuid}``. + Replace ``{resource_uuid}`` with the UUID of the resource for which you want to change the + name. + + :param resource_uuid: The UUID of the add-on resource to rename. Required. + :type resource_uuid: str + :param body: Required. + :type body: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 200 + response == { + "resource": { + "app_slug": "str", # The slug identifier for the application + associated with the resource. Required. + "has_config": bool, # Indicates if the resource has configuration + values set by the vendor. Required. + "name": "str", # The name of the addon resource. Required. + "plan_slug": "str", # The slug identifier for the plan associated + with the resource. Required. + "state": "str", # The state the resource is currently in. Required. + Known values are: "pending", "provisioning", "provisioned", "deprovisioning", + "deprovisioned", "provisioning-failed", "deprovisioning-failed", and + "suspended". + "uuid": "str", # The unique identifier for the addon resource. + Required. + "app_name": "str", # Optional. The name of the application + associated with the resource. + "message": "str", # Optional. A message related to the resource, if + applicable. + "metadata": [ + { + "name": "str", # The name of the metadata item to be + set. Required. + "value": {} + } + ], + "plan_name": "str", # Optional. The name of the plan associated with + the resource. + "plan_price_per_month": 0, # Optional. The price of the plan per + month in US dollars. + "sso_url": "str" # Optional. The Single Sign-On URL for the + resource, if applicable. + } + } + # response body for status code(s): 404 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + + @distributed_trace_async + async def patch( + self, resource_uuid: str, body: Union[JSON, IO[bytes]], **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """Update the name for an Add-On Resource. + + To change the name of an Add-On Resource, send a PATCH request to + ``/v2/add-ons/saas/{resource_uuid}``. + Replace ``{resource_uuid}`` with the UUID of the resource for which you want to change the + name. + + :param resource_uuid: The UUID of the add-on resource to rename. Required. + :type resource_uuid: str + :param body: Is either a JSON type or a IO[bytes] type. Required. + :type body: JSON or IO[bytes] + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # JSON input template you can fill out and use as your body input. + body = { + "name": "str" # The new name for the add-on resource. Required. + } + + # response body for status code(s): 200 + response == { + "resource": { + "app_slug": "str", # The slug identifier for the application + associated with the resource. Required. + "has_config": bool, # Indicates if the resource has configuration + values set by the vendor. Required. + "name": "str", # The name of the addon resource. Required. + "plan_slug": "str", # The slug identifier for the plan associated + with the resource. Required. + "state": "str", # The state the resource is currently in. Required. + Known values are: "pending", "provisioning", "provisioned", "deprovisioning", + "deprovisioned", "provisioning-failed", "deprovisioning-failed", and + "suspended". + "uuid": "str", # The unique identifier for the addon resource. + Required. + "app_name": "str", # Optional. The name of the application + associated with the resource. + "message": "str", # Optional. A message related to the resource, if + applicable. + "metadata": [ + { + "name": "str", # The name of the metadata item to be + set. Required. + "value": {} + } + ], + "plan_name": "str", # Optional. The name of the plan associated with + the resource. + "plan_price_per_month": 0, # Optional. The price of the plan per + month in US dollars. + "sso_url": "str" # Optional. The Single Sign-On URL for the + resource, if applicable. + } + } + # response body for status code(s): 404 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop( + "content_type", _headers.pop("Content-Type", None) + ) + cls: ClsType[JSON] = kwargs.pop("cls", None) + + content_type = content_type or "application/json" + _json = None + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _json = body + + _request = build_addons_patch_request( + resource_uuid=resource_uuid, + content_type=content_type, + json=_json, + content=_content, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [200, 404]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + response_headers = {} + if response.status_code == 200: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 404: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore + + return cast(JSON, deserialized) # type: ignore + + @overload + async def patch_plan( + self, + resource_uuid: str, + body: JSON, + *, + content_type: str = "application/json", + **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """Update the plan for an Add-On Resource. + + To change the plan associated with an Add-On Resource, send a PATCH request to + ``/v2/add-ons/saas/{resource_uuid}/plan``. + Replace ``{resource_uuid}`` with the UUID of the resource for which you want to change the + plan. + + :param resource_uuid: The UUID of the add-on resource to update. Required. + :type resource_uuid: str + :param body: Required. + :type body: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # JSON input template you can fill out and use as your body input. + body = { + "plan_slug": "str" # The slug identifier for the new plan to apply to the + add-on resource. Required. + } + + # response body for status code(s): 200 + response == { + "resource": { + "app_slug": "str", # The slug identifier for the application + associated with the resource. Required. + "has_config": bool, # Indicates if the resource has configuration + values set by the vendor. Required. + "name": "str", # The name of the addon resource. Required. + "plan_slug": "str", # The slug identifier for the plan associated + with the resource. Required. + "state": "str", # The state the resource is currently in. Required. + Known values are: "pending", "provisioning", "provisioned", "deprovisioning", + "deprovisioned", "provisioning-failed", "deprovisioning-failed", and + "suspended". + "uuid": "str", # The unique identifier for the addon resource. + Required. + "app_name": "str", # Optional. The name of the application + associated with the resource. + "message": "str", # Optional. A message related to the resource, if + applicable. + "metadata": [ + { + "name": "str", # The name of the metadata item to be + set. Required. + "value": {} + } + ], + "plan_name": "str", # Optional. The name of the plan associated with + the resource. + "plan_price_per_month": 0, # Optional. The price of the plan per + month in US dollars. + "sso_url": "str" # Optional. The Single Sign-On URL for the + resource, if applicable. + } + } + # response body for status code(s): 404 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + + @overload + async def patch_plan( + self, + resource_uuid: str, + body: IO[bytes], + *, + content_type: str = "application/json", + **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """Update the plan for an Add-On Resource. + + To change the plan associated with an Add-On Resource, send a PATCH request to + ``/v2/add-ons/saas/{resource_uuid}/plan``. + Replace ``{resource_uuid}`` with the UUID of the resource for which you want to change the + plan. + + :param resource_uuid: The UUID of the add-on resource to update. Required. + :type resource_uuid: str + :param body: Required. + :type body: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 200 + response == { + "resource": { + "app_slug": "str", # The slug identifier for the application + associated with the resource. Required. + "has_config": bool, # Indicates if the resource has configuration + values set by the vendor. Required. + "name": "str", # The name of the addon resource. Required. + "plan_slug": "str", # The slug identifier for the plan associated + with the resource. Required. + "state": "str", # The state the resource is currently in. Required. + Known values are: "pending", "provisioning", "provisioned", "deprovisioning", + "deprovisioned", "provisioning-failed", "deprovisioning-failed", and + "suspended". + "uuid": "str", # The unique identifier for the addon resource. + Required. + "app_name": "str", # Optional. The name of the application + associated with the resource. + "message": "str", # Optional. A message related to the resource, if + applicable. + "metadata": [ + { + "name": "str", # The name of the metadata item to be + set. Required. + "value": {} + } + ], + "plan_name": "str", # Optional. The name of the plan associated with + the resource. + "plan_price_per_month": 0, # Optional. The price of the plan per + month in US dollars. + "sso_url": "str" # Optional. The Single Sign-On URL for the + resource, if applicable. + } + } + # response body for status code(s): 404 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + + @distributed_trace_async + async def patch_plan( + self, resource_uuid: str, body: Union[JSON, IO[bytes]], **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """Update the plan for an Add-On Resource. + + To change the plan associated with an Add-On Resource, send a PATCH request to + ``/v2/add-ons/saas/{resource_uuid}/plan``. + Replace ``{resource_uuid}`` with the UUID of the resource for which you want to change the + plan. + + :param resource_uuid: The UUID of the add-on resource to update. Required. + :type resource_uuid: str + :param body: Is either a JSON type or a IO[bytes] type. Required. + :type body: JSON or IO[bytes] + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # JSON input template you can fill out and use as your body input. + body = { + "plan_slug": "str" # The slug identifier for the new plan to apply to the + add-on resource. Required. + } + + # response body for status code(s): 200 + response == { + "resource": { + "app_slug": "str", # The slug identifier for the application + associated with the resource. Required. + "has_config": bool, # Indicates if the resource has configuration + values set by the vendor. Required. + "name": "str", # The name of the addon resource. Required. + "plan_slug": "str", # The slug identifier for the plan associated + with the resource. Required. + "state": "str", # The state the resource is currently in. Required. + Known values are: "pending", "provisioning", "provisioned", "deprovisioning", + "deprovisioned", "provisioning-failed", "deprovisioning-failed", and + "suspended". + "uuid": "str", # The unique identifier for the addon resource. + Required. + "app_name": "str", # Optional. The name of the application + associated with the resource. + "message": "str", # Optional. A message related to the resource, if + applicable. + "metadata": [ + { + "name": "str", # The name of the metadata item to be + set. Required. + "value": {} + } + ], + "plan_name": "str", # Optional. The name of the plan associated with + the resource. + "plan_price_per_month": 0, # Optional. The price of the plan per + month in US dollars. + "sso_url": "str" # Optional. The Single Sign-On URL for the + resource, if applicable. + } + } + # response body for status code(s): 404 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop( + "content_type", _headers.pop("Content-Type", None) + ) + cls: ClsType[JSON] = kwargs.pop("cls", None) + + content_type = content_type or "application/json" + _json = None + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _json = body + + _request = build_addons_patch_plan_request( + resource_uuid=resource_uuid, + content_type=content_type, + json=_json, + content=_content, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [200, 404]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + response_headers = {} + if response.status_code == 200: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 404: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore + + return cast(JSON, deserialized) # type: ignore + + +class HarnessOperations: # pylint: disable=too-many-public-methods + """ + .. warning:: + **DO NOT** instantiate this class directly. + + Instead, you should access the following operations through + :class:`~pydo.aio.GeneratedClient`'s + :attr:`harness` attribute. + """ + + def __init__(self, *args, **kwargs) -> None: + input_args = list(args) + self._client = input_args.pop(0) if input_args else kwargs.pop("client") + self._config = input_args.pop(0) if input_args else kwargs.pop("config") + self._serialize = input_args.pop(0) if input_args else kwargs.pop("serializer") + self._deserialize = ( + input_args.pop(0) if input_args else kwargs.pop("deserializer") + ) + + @distributed_trace_async + async def list_configs( + self, + *, + page_size: int = 50, + page_token: Optional[str] = None, + search: Optional[str] = None, + **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """List active Environment Configs for the caller's team. + + Returns lightweight team-scoped config metadata without loading or returning environment-spec + JSON. Soft-deleted configs are excluded. + + :keyword page_size: Page size. Defaults to 50 when omitted. Values above 200 are rejected. + Default value is 50. + :paramtype page_size: int + :keyword page_token: Opaque keyset cursor returned by the previous page. Default value is None. + :paramtype page_token: str + :keyword search: Optional case-insensitive substring filter on config name (contains). Empty or + omitted returns the unfiltered page. At most 64 characters. Default value is None. + :paramtype search: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 200 + response == { + "configs": [ + { + "agentspec_schema_version": "str", # The manifest contract + the server accepted at create time: the manifest's own apiVersion for + legacy envelope parses (currently agents.digitalocean.com/v1alpha1), or + agents.digitalocean.com/flat.v1 for flat-format parses. Required. + "content_hash": "str", # Lowercase SHA-256 hex digest of the + canonical sanitized manifest JSON. Required. + "created_at": "2020-02-20 00:00:00", # Required. + "created_by": "str", # Authenticated user identity that + created the config. Required. + "id": "str", # Required. + "name": "str", # Required. + "updated_at": "2020-02-20 00:00:00" # Required. + } + ], + "next_page_token": "str" # Optional. + } + # response body for status code(s): 400 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[JSON] = kwargs.pop("cls", None) + + _request = build_harness_list_configs_request( + page_size=page_size, + page_token=page_token, + search=search, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [200, 400]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + response_headers = {} + if response.status_code == 200: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 400: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore + + return cast(JSON, deserialized) # type: ignore + + @overload + async def create_config( + self, body: JSON, *, content_type: str = "application/json", **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """Create an Environment Config (environment spec + optional Insights opt-in). + + Accepts name, environment-spec YAML text, and optional Insights telemetry opt-in. Tenant + secrets are declared inside the environment spec (write-only values), not as sibling request + fields. Responses never echo secret values; GET/create may include credential slot metadata + (name/source/provider only). Configs are immutable after create. + + :param body: Environment Config create body. Secret values live in the environment-spec YAML + and are never returned. Required. + :type body: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # JSON input template you can fill out and use as your body input. + body = { + "manifest_yaml": "str", # Customer environment-spec YAML text. The HTTP JSON + body is capped at 1 MiB and this field is capped at 512 KiB. Required. + "name": "str", # Config name, case-insensitively unique among the team's + active configs. Required. + "insights": { + "logs": bool, # Optional. Whether the agent's logs are sent to + Insights. + "metrics": bool, # Optional. Accepted and stored for parity with the + Insights schema, but metrics are always collected: unregistered metrics are + dropped at ingest rather than opted out of here. + "traces": bool # Optional. Whether the agent's traces are sent to + Insights. + } + } + + # response body for status code(s): 201 + response == { + "config": { + "agentspec_schema_version": "str", # The manifest contract the + server accepted at create time: the manifest's own apiVersion for legacy + envelope parses (currently agents.digitalocean.com/v1alpha1), or + agents.digitalocean.com/flat.v1 for flat-format parses. Required. + "content_hash": "str", # Lowercase SHA-256 hex digest of the + canonical sanitized manifest JSON. Required. + "created_at": "2020-02-20 00:00:00", # Required. + "created_by": "str", # Authenticated user identity that created the + config. Required. + "id": "str", # Required. + "manifest": { + "apiVersion": "str", # Optional. Legacy envelope only - must + be "agents.digitalocean.com/v1alpha1", and its presence is what routes + the document to the deprecated legacy parser. Flat-format manifests carry + no version key (the endpoint owns the contract); required, with kind and + spec, when authoring the legacy format. + "agents.digitalocean.com/v1alpha1" + "kind": "str", # Optional. Must be "Agent". "Agent" + "metadata": { + "description": "str", # Optional. Free-form + description (max 1024 chars), stored and echoed back for Console / + catalog surfaces. + "labels": { + "str": "str" # Optional. Dictionary of + :code:``. + }, + "name": "str" # Optional. Optional team-unique + session name: 1-64 chars of letters, digits, '-', '.', '_', starting + and ending alphanumeric; case-insensitive; must not be UUID-shaped. + Omit to have the control plane generate one + ("session-:code:``-:code:``"). Unique among + the team's ACTIVE (non-terminal) sessions; destroying a session frees + its name for reuse. + }, + "spec": { + "runtime": { + "adapter": "str", # Required. Known values + are: "claude-code", "opencode", "codex", "cursor", "hermes", + "codex-agentapi", "custom", "codex-cli", "cursor-cli", + "openai-agents", "claude-agent-sdk", "crewai", and "langgraph". + "config": { + "str": {} # Optional. OPAQUE, + provider-specific runtime config. Only for adapter: + codex-agentapi today, where it is the OpenAI Agents API + create-session body (agent/environment/input) that doctl + forwards verbatim to OpenAI's POST /v1/agents/sessions; + harness-api stores and echoes it but never parses or + validates its contents (so DO stays forward-compatible as + OpenAI's schema evolves). The OpenAI session_id is passed on + the create call (the openai_session_id query parameter), not + in this block. + }, + "custom": { + "image": "str", # Required. + "entrypoint": [ + "str" # Optional. BYO + container; requires adapter: custom. image is required + and should be digest-pinned. + ] + } + }, + "defaults": { + "budget": { + "maxCostUSD": 0.0, # Optional. + Accepted, not yet enforced (returns a warning). Advisory + per-run ceilings, clamped server-side to tenant policy. + "maxSteps": 0, # Optional. Accepted, + not yet enforced (returns a warning). Advisory per-run + ceilings, clamped server-side to tenant policy. + "maxWallClockSeconds": 0, # + Optional. Accepted, not yet enforced (returns a warning). + Advisory per-run ceilings, clamped server-side to tenant + policy. + "onBreach": "str" # Optional. Known + values are: "pause", "humanInput", and "fail". + } + }, + "env": { + "str": "str" # Optional. Non-secret guest + env (debug-readable - never put secrets here; use spec.secrets). + Reserved platform keys (TEAM_ID, SESSION_ID, SANDBOX_ID, + CONTROL_PLANE_CALLBACK_URL, SANDBOX_TEMPLATE, + SANDBOX_AGENT_WORKDIR, SIZE_SLUG, SANDBOX_IMAGE_ID, + SANDBOX_OCI_REF, PLANO"" *PERMISSION*"" *) are rejected. + Credential-looking values (dop"" *v1*..., sk-..., ghp_...) + trigger a create-time warning. + }, + "memory": { + "backend": "str", # Required. + "scope": "str" # Optional. Known values are: + "tenant", "user", "agent", and "session". + }, + "mode": "str", # Optional. Explicit lifecycle + discriminator. Defaults to "interactive" (spec.serving forbidden); + "served" requires spec.serving. Known values are: "interactive" and + "served". + "model": { + "default": "str", # Optional. Accepted, not + yet enforced (returns a warning). Model routing (Plano); + catalog-validated when enforcement lands. + "profile": "str", # Optional. Accepted, not + yet enforced (returns a warning). Model routing (Plano); + catalog-validated when enforcement lands. + "routing": "str" # Optional. Accepted, not + yet enforced (returns a warning). Model routing (Plano); + catalog-validated when enforcement lands. + }, + "permissions": { + "defaultAction": "str", # Optional. + Disposition for tool calls that match no rule. Defaults to "ask" + when omitted. Known values are: "allow", "ask", and "deny". + "filesystem": { + "allowWrite": [ + "str" # Optional. Additional + writable paths beyond the workspace. Values must not + contain shell-style ${VAR}/$VAR expansions. + ], + "mode": "str" # Optional. Known + values are: "read-only" and "workspace-write". + }, + "network": { + "allow": [ + "str" # Optional. Network + boundary. Narrows (subtract-only) spec.sandbox.egress; + link-local/internal addresses stay blocked by the broader + egress policy. + ], + "default": "str" # Optional. Known + values are: "allow" and "deny". + }, + "rules": [ + { + "action": "str", # Required. + Known values are: "allow", "ask", and "deny". + "tool": "str", # Tool + taxonomy this rule matches (e.g. bash, file.read, + file.write, web.fetch, git.*, mcp). Required. + "enforcement": "str", # + Optional. What happens when the agent cannot enforce the + rule exactly. "strict" rejects the session; "best-effort" + degrades to the closest more-restrictive form and warns + (never widens). Defaults to strict for deny rules, + best-effort otherwise. Known values are: "strict" and + "best-effort". + "match": { + "str": "str" # + Optional. Optional keys narrowing the rule (e.g. + {"command": "npm run deploy"} for bash). Values are + globs and must not contain shell-style ${VAR}/$VAR + expansions. + } + } + ] + }, + "sandbox": { + "egress": { + "allow": [ + { + "host": "str" # + Required. + } + ], + "allowIps": [ + "str" # Optional. Exact + IPv4/IPv6 literals (not CIDRs). + ], + "subnetUuid": "str", # Optional. + Optional subnet within vpcUuid. + "unrestricted": bool, # Optional. + When true, opts out of the platform-default host allowlist + union. Mutually exclusive with allow / allowIps / vpcUuid / + subnetUuid. + "vpcUuid": "str" # Optional. + Customer VPC UUID. Create-time immutable. + }, + "idleTimeoutSeconds": 0, # Optional. + Inactivity suspend timeout. 0 means tenant policy default. + "maxLifetimeSeconds": 0, # Optional. Hard + wall-clock ceiling for the whole session, independent of + activity. Accepted, not yet enforced (returns a warning). + "mounts": [ + { + "accessKeyRef": "str", # + Names a declared tenantSecret slot in spec.secrets. + Required. + "bucket": "str", # Required. + "path": "str", # Absolute + guest path the mount appears at. Required. + "secretKeyRef": "str", # + Names a declared tenantSecret slot in spec.secrets. + Required. + "type": "str", # Required. + "s3fuse" + "endpoint": "str", # + Optional. Optional storage endpoint override; https:// + only. + "readOnly": bool # Optional. + Requested data mounts. Accepted, not yet enforced + (returns a warning). accessKeyRef/secretKeyRef name + declared tenantSecret slots in spec.secrets, validated at + create; declare those slots with env: false so the keys + stay mount-scoped and out of the guest env. + } + ], + "persistentWorkspace": bool, # Optional. + Canonical runnable environment spec JSON, immutable after create. + Validated non-secret spec.env and spec.permissions remain; + spec.secrets retains name/source/provider but omits ref and the + write-only value; metadata.name is cleared while safe metadata + such as labels remains. Required. + "sizeSlug": "str", # Optional. Requested + sandbox size (catalog: GET /v2/agents/sessions/sandbox/sizes). + Empty means the service default. + "template": "str" # Optional. Optional + sandbox template. Empty (or the legacy placeholder "coding") + means "no preference": the template derives from the adapter. + }, + "secrets": [ + { + "name": "str", # Required. + "source": "str", # Required. Known + values are: "oauth" and "tenantSecret". + "env": bool, # Optional. Whether the + resolved credential is injected into the guest env. Omitted + means true; false is for mount-only slots (tenantSecret + only). + "provider": "str", # Optional. + Declared credential requirements. source tenantSecret is + enforced today: value is write-only plaintext, extracted into + Secrets Manager at create and never persisted or returned. + source oauth requires provider, carries no value, and is + accepted but not yet enforced. ref is legacy and ignored. env + false keeps the resolved value out of the guest environment + (mount-only slots). + "ref": "str", # Optional. Declared + credential requirements. source tenantSecret is enforced + today: value is write-only plaintext, extracted into Secrets + Manager at create and never persisted or returned. source + oauth requires provider, carries no value, and is accepted + but not yet enforced. ref is legacy and ignored. env false + keeps the resolved value out of the guest environment + (mount-only slots). + "value": "str" # Optional. + Write-only tenantSecret plaintext; never persisted or + returned. + } + ], + "serving": { + "maxReplicas": 0, # Optional. Present only + when spec.mode is "served" (autoscaled fleet). + "minReplicas": 0, # Optional. Present only + when spec.mode is "served" (autoscaled fleet). + "scaleToZero": { + "idleSeconds": 0 # Optional. Present + only when spec.mode is "served" (autoscaled fleet). + }, + "targetConcurrency": 0 # Optional. Present + only when spec.mode is "served" (autoscaled fleet). + }, + "skills": [ + { + "description": "str", # + Load-bearing: the agent reads it to decide WHEN to load the + skill (progressive disclosure); write it as "Use when ...". + Max 1024 chars. Required. + "instructions": "str", # The + markdown body (the content below the SKILL.md frontmatter). + Max 32 KiB. Required. + "name": "str", # Unique across the + list; 1-64 chars of lowercase letters, digits, and single + hyphen separators - no consecutive hyphens, per the Agent + Skills spec (safe as a directory name at materialization). + Required. + "allowedTools": [ + "str" # Optional. Advisory + scoping of which tools the skill expects to use + (experimental in the skills standard). NOT a security + boundary - spec.permissions remains the only enforcement + surface. + ], + "license": "str", # Optional. + Optional standard passthrough (e.g. Apache-2.0). + "metadata": { + "str": "str" # Optional. + Optional string-map passthrough (e.g. author), rendered + into the frontmatter verbatim. + } + } + ], + "tools": { + "bundle": "str", # Optional. Accepted, not + yet enforced (returns a warning). Exactly one of mcpServers or + bundle. + "mcpServers": [ + { + "auth": { + "secretRef": "str", + # Tenant-vault secret name. Required. + "header": "str" # + Optional. HTTP header the credential is sent in. + Defaults to Authorization. + }, + "name": "str", # Optional. + Inline server name (required with url): 1-64 chars of + lowercase letters, digits, '-', '.', '_', starting/ending + alphanumeric. Unique across the list. Must not start with + do. (reserved for catalog refs). + "preloadTools": [ + "str" # Optional. + Catalog form only. Individual tools (optional integer + pin) advertised directly in the session MCP + tools/list next to the + action_search/action_invoke/action_code meta tools. + Each entry must appear in tools when tools is set; + toolbelt references are rejected. Every preloaded + schema is in the model's context on every turn, so + keep this to a handful of tools. Omitted means + nothing is preloaded. + ], + "ref": "str", # Optional. + DO-owned catalog reference (catalog form); do.* only, + e.g. do.actions. Mutually exclusive with name/url/auth. + "tools": [ + "str" # Optional. + Optional selection allowlist; omitted means all tools + the server advertises. Each entry is an individual + tool name with an optional integer version pin (e.g. + web_search, execute_code@3) or a toolbelt reference - + a server-defined named tool group written + toolbelt:[@:code:``] (e.g. + toolbelt:read-only@1; catalog refs only). Versions + are integer pins (no semver). The server owns belt + expansion; a pinned tool or belt attaches exactly + that version, an unpinned one resolves at session + create and is frozen for the session (no floating + latest). Entries non-empty, unique per server. The + selection bounds the session and is never preloaded + into the session MCP tools/list; see preloadTools. + ], + "url": "str" # Optional. + Inline server endpoint; https:// only. + } + ] + }, + "workspace": { + "repos": [ + "str" # Optional. GitHub OWNER/REPO + entries, unique. The first entry becomes the session's + repo_hint; the full list is the forward shape for multi-repo + sessions. + ] + } + } + }, + "name": "str", # Required. + "updated_at": "2020-02-20 00:00:00", # Required. + "credentials": [ + { + "name": "str", # Required. + "provider": "str", # Optional. Redacted credential + slot metadata (name/source/provider). Secret values are never + included. + "source": "str" # Optional. Known values are: + "tenantSecret" and "oauth". + } + ], + "insights": { + "logs": bool, # Optional. Whether the agent's logs are sent + to Insights. + "metrics": bool, # Optional. Accepted and stored for parity + with the Insights schema, but metrics are always collected: unregistered + metrics are dropped at ingest rather than opted out of here. + "traces": bool # Optional. Whether the agent's traces are + sent to Insights. + } + } + } + # response body for status code(s): 400, 409, 413, 415 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + + @overload + async def create_config( + self, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """Create an Environment Config (environment spec + optional Insights opt-in). + + Accepts name, environment-spec YAML text, and optional Insights telemetry opt-in. Tenant + secrets are declared inside the environment spec (write-only values), not as sibling request + fields. Responses never echo secret values; GET/create may include credential slot metadata + (name/source/provider only). Configs are immutable after create. + + :param body: Environment Config create body. Secret values live in the environment-spec YAML + and are never returned. Required. + :type body: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 201 + response == { + "config": { + "agentspec_schema_version": "str", # The manifest contract the + server accepted at create time: the manifest's own apiVersion for legacy + envelope parses (currently agents.digitalocean.com/v1alpha1), or + agents.digitalocean.com/flat.v1 for flat-format parses. Required. + "content_hash": "str", # Lowercase SHA-256 hex digest of the + canonical sanitized manifest JSON. Required. + "created_at": "2020-02-20 00:00:00", # Required. + "created_by": "str", # Authenticated user identity that created the + config. Required. + "id": "str", # Required. + "manifest": { + "apiVersion": "str", # Optional. Legacy envelope only - must + be "agents.digitalocean.com/v1alpha1", and its presence is what routes + the document to the deprecated legacy parser. Flat-format manifests carry + no version key (the endpoint owns the contract); required, with kind and + spec, when authoring the legacy format. + "agents.digitalocean.com/v1alpha1" + "kind": "str", # Optional. Must be "Agent". "Agent" + "metadata": { + "description": "str", # Optional. Free-form + description (max 1024 chars), stored and echoed back for Console / + catalog surfaces. + "labels": { + "str": "str" # Optional. Dictionary of + :code:``. + }, + "name": "str" # Optional. Optional team-unique + session name: 1-64 chars of letters, digits, '-', '.', '_', starting + and ending alphanumeric; case-insensitive; must not be UUID-shaped. + Omit to have the control plane generate one + ("session-:code:``-:code:``"). Unique among + the team's ACTIVE (non-terminal) sessions; destroying a session frees + its name for reuse. + }, + "spec": { + "runtime": { + "adapter": "str", # Required. Known values + are: "claude-code", "opencode", "codex", "cursor", "hermes", + "codex-agentapi", "custom", "codex-cli", "cursor-cli", + "openai-agents", "claude-agent-sdk", "crewai", and "langgraph". + "config": { + "str": {} # Optional. OPAQUE, + provider-specific runtime config. Only for adapter: + codex-agentapi today, where it is the OpenAI Agents API + create-session body (agent/environment/input) that doctl + forwards verbatim to OpenAI's POST /v1/agents/sessions; + harness-api stores and echoes it but never parses or + validates its contents (so DO stays forward-compatible as + OpenAI's schema evolves). The OpenAI session_id is passed on + the create call (the openai_session_id query parameter), not + in this block. + }, + "custom": { + "image": "str", # Required. + "entrypoint": [ + "str" # Optional. BYO + container; requires adapter: custom. image is required + and should be digest-pinned. + ] + } + }, + "defaults": { + "budget": { + "maxCostUSD": 0.0, # Optional. + Accepted, not yet enforced (returns a warning). Advisory + per-run ceilings, clamped server-side to tenant policy. + "maxSteps": 0, # Optional. Accepted, + not yet enforced (returns a warning). Advisory per-run + ceilings, clamped server-side to tenant policy. + "maxWallClockSeconds": 0, # + Optional. Accepted, not yet enforced (returns a warning). + Advisory per-run ceilings, clamped server-side to tenant + policy. + "onBreach": "str" # Optional. Known + values are: "pause", "humanInput", and "fail". + } + }, + "env": { + "str": "str" # Optional. Non-secret guest + env (debug-readable - never put secrets here; use spec.secrets). + Reserved platform keys (TEAM_ID, SESSION_ID, SANDBOX_ID, + CONTROL_PLANE_CALLBACK_URL, SANDBOX_TEMPLATE, + SANDBOX_AGENT_WORKDIR, SIZE_SLUG, SANDBOX_IMAGE_ID, + SANDBOX_OCI_REF, PLANO"" *PERMISSION*"" *) are rejected. + Credential-looking values (dop"" *v1*..., sk-..., ghp_...) + trigger a create-time warning. + }, + "memory": { + "backend": "str", # Required. + "scope": "str" # Optional. Known values are: + "tenant", "user", "agent", and "session". + }, + "mode": "str", # Optional. Explicit lifecycle + discriminator. Defaults to "interactive" (spec.serving forbidden); + "served" requires spec.serving. Known values are: "interactive" and + "served". + "model": { + "default": "str", # Optional. Accepted, not + yet enforced (returns a warning). Model routing (Plano); + catalog-validated when enforcement lands. + "profile": "str", # Optional. Accepted, not + yet enforced (returns a warning). Model routing (Plano); + catalog-validated when enforcement lands. + "routing": "str" # Optional. Accepted, not + yet enforced (returns a warning). Model routing (Plano); + catalog-validated when enforcement lands. + }, + "permissions": { + "defaultAction": "str", # Optional. + Disposition for tool calls that match no rule. Defaults to "ask" + when omitted. Known values are: "allow", "ask", and "deny". + "filesystem": { + "allowWrite": [ + "str" # Optional. Additional + writable paths beyond the workspace. Values must not + contain shell-style ${VAR}/$VAR expansions. + ], + "mode": "str" # Optional. Known + values are: "read-only" and "workspace-write". + }, + "network": { + "allow": [ + "str" # Optional. Network + boundary. Narrows (subtract-only) spec.sandbox.egress; + link-local/internal addresses stay blocked by the broader + egress policy. + ], + "default": "str" # Optional. Known + values are: "allow" and "deny". + }, + "rules": [ + { + "action": "str", # Required. + Known values are: "allow", "ask", and "deny". + "tool": "str", # Tool + taxonomy this rule matches (e.g. bash, file.read, + file.write, web.fetch, git.*, mcp). Required. + "enforcement": "str", # + Optional. What happens when the agent cannot enforce the + rule exactly. "strict" rejects the session; "best-effort" + degrades to the closest more-restrictive form and warns + (never widens). Defaults to strict for deny rules, + best-effort otherwise. Known values are: "strict" and + "best-effort". + "match": { + "str": "str" # + Optional. Optional keys narrowing the rule (e.g. + {"command": "npm run deploy"} for bash). Values are + globs and must not contain shell-style ${VAR}/$VAR + expansions. + } + } + ] + }, + "sandbox": { + "egress": { + "allow": [ + { + "host": "str" # + Required. + } + ], + "allowIps": [ + "str" # Optional. Exact + IPv4/IPv6 literals (not CIDRs). + ], + "subnetUuid": "str", # Optional. + Optional subnet within vpcUuid. + "unrestricted": bool, # Optional. + When true, opts out of the platform-default host allowlist + union. Mutually exclusive with allow / allowIps / vpcUuid / + subnetUuid. + "vpcUuid": "str" # Optional. + Customer VPC UUID. Create-time immutable. + }, + "idleTimeoutSeconds": 0, # Optional. + Inactivity suspend timeout. 0 means tenant policy default. + "maxLifetimeSeconds": 0, # Optional. Hard + wall-clock ceiling for the whole session, independent of + activity. Accepted, not yet enforced (returns a warning). + "mounts": [ + { + "accessKeyRef": "str", # + Names a declared tenantSecret slot in spec.secrets. + Required. + "bucket": "str", # Required. + "path": "str", # Absolute + guest path the mount appears at. Required. + "secretKeyRef": "str", # + Names a declared tenantSecret slot in spec.secrets. + Required. + "type": "str", # Required. + "s3fuse" + "endpoint": "str", # + Optional. Optional storage endpoint override; https:// + only. + "readOnly": bool # Optional. + Requested data mounts. Accepted, not yet enforced + (returns a warning). accessKeyRef/secretKeyRef name + declared tenantSecret slots in spec.secrets, validated at + create; declare those slots with env: false so the keys + stay mount-scoped and out of the guest env. + } + ], + "persistentWorkspace": bool, # Optional. + Canonical runnable environment spec JSON, immutable after create. + Validated non-secret spec.env and spec.permissions remain; + spec.secrets retains name/source/provider but omits ref and the + write-only value; metadata.name is cleared while safe metadata + such as labels remains. Required. + "sizeSlug": "str", # Optional. Requested + sandbox size (catalog: GET /v2/agents/sessions/sandbox/sizes). + Empty means the service default. + "template": "str" # Optional. Optional + sandbox template. Empty (or the legacy placeholder "coding") + means "no preference": the template derives from the adapter. + }, + "secrets": [ + { + "name": "str", # Required. + "source": "str", # Required. Known + values are: "oauth" and "tenantSecret". + "env": bool, # Optional. Whether the + resolved credential is injected into the guest env. Omitted + means true; false is for mount-only slots (tenantSecret + only). + "provider": "str", # Optional. + Declared credential requirements. source tenantSecret is + enforced today: value is write-only plaintext, extracted into + Secrets Manager at create and never persisted or returned. + source oauth requires provider, carries no value, and is + accepted but not yet enforced. ref is legacy and ignored. env + false keeps the resolved value out of the guest environment + (mount-only slots). + "ref": "str", # Optional. Declared + credential requirements. source tenantSecret is enforced + today: value is write-only plaintext, extracted into Secrets + Manager at create and never persisted or returned. source + oauth requires provider, carries no value, and is accepted + but not yet enforced. ref is legacy and ignored. env false + keeps the resolved value out of the guest environment + (mount-only slots). + "value": "str" # Optional. + Write-only tenantSecret plaintext; never persisted or + returned. + } + ], + "serving": { + "maxReplicas": 0, # Optional. Present only + when spec.mode is "served" (autoscaled fleet). + "minReplicas": 0, # Optional. Present only + when spec.mode is "served" (autoscaled fleet). + "scaleToZero": { + "idleSeconds": 0 # Optional. Present + only when spec.mode is "served" (autoscaled fleet). + }, + "targetConcurrency": 0 # Optional. Present + only when spec.mode is "served" (autoscaled fleet). + }, + "skills": [ + { + "description": "str", # + Load-bearing: the agent reads it to decide WHEN to load the + skill (progressive disclosure); write it as "Use when ...". + Max 1024 chars. Required. + "instructions": "str", # The + markdown body (the content below the SKILL.md frontmatter). + Max 32 KiB. Required. + "name": "str", # Unique across the + list; 1-64 chars of lowercase letters, digits, and single + hyphen separators - no consecutive hyphens, per the Agent + Skills spec (safe as a directory name at materialization). + Required. + "allowedTools": [ + "str" # Optional. Advisory + scoping of which tools the skill expects to use + (experimental in the skills standard). NOT a security + boundary - spec.permissions remains the only enforcement + surface. + ], + "license": "str", # Optional. + Optional standard passthrough (e.g. Apache-2.0). + "metadata": { + "str": "str" # Optional. + Optional string-map passthrough (e.g. author), rendered + into the frontmatter verbatim. + } + } + ], + "tools": { + "bundle": "str", # Optional. Accepted, not + yet enforced (returns a warning). Exactly one of mcpServers or + bundle. + "mcpServers": [ + { + "auth": { + "secretRef": "str", + # Tenant-vault secret name. Required. + "header": "str" # + Optional. HTTP header the credential is sent in. + Defaults to Authorization. + }, + "name": "str", # Optional. + Inline server name (required with url): 1-64 chars of + lowercase letters, digits, '-', '.', '_', starting/ending + alphanumeric. Unique across the list. Must not start with + do. (reserved for catalog refs). + "preloadTools": [ + "str" # Optional. + Catalog form only. Individual tools (optional integer + pin) advertised directly in the session MCP + tools/list next to the + action_search/action_invoke/action_code meta tools. + Each entry must appear in tools when tools is set; + toolbelt references are rejected. Every preloaded + schema is in the model's context on every turn, so + keep this to a handful of tools. Omitted means + nothing is preloaded. + ], + "ref": "str", # Optional. + DO-owned catalog reference (catalog form); do.* only, + e.g. do.actions. Mutually exclusive with name/url/auth. + "tools": [ + "str" # Optional. + Optional selection allowlist; omitted means all tools + the server advertises. Each entry is an individual + tool name with an optional integer version pin (e.g. + web_search, execute_code@3) or a toolbelt reference - + a server-defined named tool group written + toolbelt:[@:code:``] (e.g. + toolbelt:read-only@1; catalog refs only). Versions + are integer pins (no semver). The server owns belt + expansion; a pinned tool or belt attaches exactly + that version, an unpinned one resolves at session + create and is frozen for the session (no floating + latest). Entries non-empty, unique per server. The + selection bounds the session and is never preloaded + into the session MCP tools/list; see preloadTools. + ], + "url": "str" # Optional. + Inline server endpoint; https:// only. + } + ] + }, + "workspace": { + "repos": [ + "str" # Optional. GitHub OWNER/REPO + entries, unique. The first entry becomes the session's + repo_hint; the full list is the forward shape for multi-repo + sessions. + ] + } + } + }, + "name": "str", # Required. + "updated_at": "2020-02-20 00:00:00", # Required. + "credentials": [ + { + "name": "str", # Required. + "provider": "str", # Optional. Redacted credential + slot metadata (name/source/provider). Secret values are never + included. + "source": "str" # Optional. Known values are: + "tenantSecret" and "oauth". + } + ], + "insights": { + "logs": bool, # Optional. Whether the agent's logs are sent + to Insights. + "metrics": bool, # Optional. Accepted and stored for parity + with the Insights schema, but metrics are always collected: unregistered + metrics are dropped at ingest rather than opted out of here. + "traces": bool # Optional. Whether the agent's traces are + sent to Insights. + } + } + } + # response body for status code(s): 400, 409, 413, 415 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + + @distributed_trace_async + async def create_config(self, body: Union[JSON, IO[bytes]], **kwargs: Any) -> JSON: + # pylint: disable=line-too-long + """Create an Environment Config (environment spec + optional Insights opt-in). + + Accepts name, environment-spec YAML text, and optional Insights telemetry opt-in. Tenant + secrets are declared inside the environment spec (write-only values), not as sibling request + fields. Responses never echo secret values; GET/create may include credential slot metadata + (name/source/provider only). Configs are immutable after create. + + :param body: Environment Config create body. Secret values live in the environment-spec YAML + and are never returned. Is either a JSON type or a IO[bytes] type. Required. + :type body: JSON or IO[bytes] + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # JSON input template you can fill out and use as your body input. + body = { + "manifest_yaml": "str", # Customer environment-spec YAML text. The HTTP JSON + body is capped at 1 MiB and this field is capped at 512 KiB. Required. + "name": "str", # Config name, case-insensitively unique among the team's + active configs. Required. + "insights": { + "logs": bool, # Optional. Whether the agent's logs are sent to + Insights. + "metrics": bool, # Optional. Accepted and stored for parity with the + Insights schema, but metrics are always collected: unregistered metrics are + dropped at ingest rather than opted out of here. + "traces": bool # Optional. Whether the agent's traces are sent to + Insights. + } + } + + # response body for status code(s): 201 + response == { + "config": { + "agentspec_schema_version": "str", # The manifest contract the + server accepted at create time: the manifest's own apiVersion for legacy + envelope parses (currently agents.digitalocean.com/v1alpha1), or + agents.digitalocean.com/flat.v1 for flat-format parses. Required. + "content_hash": "str", # Lowercase SHA-256 hex digest of the + canonical sanitized manifest JSON. Required. + "created_at": "2020-02-20 00:00:00", # Required. + "created_by": "str", # Authenticated user identity that created the + config. Required. + "id": "str", # Required. + "manifest": { + "apiVersion": "str", # Optional. Legacy envelope only - must + be "agents.digitalocean.com/v1alpha1", and its presence is what routes + the document to the deprecated legacy parser. Flat-format manifests carry + no version key (the endpoint owns the contract); required, with kind and + spec, when authoring the legacy format. + "agents.digitalocean.com/v1alpha1" + "kind": "str", # Optional. Must be "Agent". "Agent" + "metadata": { + "description": "str", # Optional. Free-form + description (max 1024 chars), stored and echoed back for Console / + catalog surfaces. + "labels": { + "str": "str" # Optional. Dictionary of + :code:``. + }, + "name": "str" # Optional. Optional team-unique + session name: 1-64 chars of letters, digits, '-', '.', '_', starting + and ending alphanumeric; case-insensitive; must not be UUID-shaped. + Omit to have the control plane generate one + ("session-:code:``-:code:``"). Unique among + the team's ACTIVE (non-terminal) sessions; destroying a session frees + its name for reuse. + }, + "spec": { + "runtime": { + "adapter": "str", # Required. Known values + are: "claude-code", "opencode", "codex", "cursor", "hermes", + "codex-agentapi", "custom", "codex-cli", "cursor-cli", + "openai-agents", "claude-agent-sdk", "crewai", and "langgraph". + "config": { + "str": {} # Optional. OPAQUE, + provider-specific runtime config. Only for adapter: + codex-agentapi today, where it is the OpenAI Agents API + create-session body (agent/environment/input) that doctl + forwards verbatim to OpenAI's POST /v1/agents/sessions; + harness-api stores and echoes it but never parses or + validates its contents (so DO stays forward-compatible as + OpenAI's schema evolves). The OpenAI session_id is passed on + the create call (the openai_session_id query parameter), not + in this block. + }, + "custom": { + "image": "str", # Required. + "entrypoint": [ + "str" # Optional. BYO + container; requires adapter: custom. image is required + and should be digest-pinned. + ] + } + }, + "defaults": { + "budget": { + "maxCostUSD": 0.0, # Optional. + Accepted, not yet enforced (returns a warning). Advisory + per-run ceilings, clamped server-side to tenant policy. + "maxSteps": 0, # Optional. Accepted, + not yet enforced (returns a warning). Advisory per-run + ceilings, clamped server-side to tenant policy. + "maxWallClockSeconds": 0, # + Optional. Accepted, not yet enforced (returns a warning). + Advisory per-run ceilings, clamped server-side to tenant + policy. + "onBreach": "str" # Optional. Known + values are: "pause", "humanInput", and "fail". + } + }, + "env": { + "str": "str" # Optional. Non-secret guest + env (debug-readable - never put secrets here; use spec.secrets). + Reserved platform keys (TEAM_ID, SESSION_ID, SANDBOX_ID, + CONTROL_PLANE_CALLBACK_URL, SANDBOX_TEMPLATE, + SANDBOX_AGENT_WORKDIR, SIZE_SLUG, SANDBOX_IMAGE_ID, + SANDBOX_OCI_REF, PLANO"" *PERMISSION*"" *) are rejected. + Credential-looking values (dop"" *v1*..., sk-..., ghp_...) + trigger a create-time warning. + }, + "memory": { + "backend": "str", # Required. + "scope": "str" # Optional. Known values are: + "tenant", "user", "agent", and "session". + }, + "mode": "str", # Optional. Explicit lifecycle + discriminator. Defaults to "interactive" (spec.serving forbidden); + "served" requires spec.serving. Known values are: "interactive" and + "served". + "model": { + "default": "str", # Optional. Accepted, not + yet enforced (returns a warning). Model routing (Plano); + catalog-validated when enforcement lands. + "profile": "str", # Optional. Accepted, not + yet enforced (returns a warning). Model routing (Plano); + catalog-validated when enforcement lands. + "routing": "str" # Optional. Accepted, not + yet enforced (returns a warning). Model routing (Plano); + catalog-validated when enforcement lands. + }, + "permissions": { + "defaultAction": "str", # Optional. + Disposition for tool calls that match no rule. Defaults to "ask" + when omitted. Known values are: "allow", "ask", and "deny". + "filesystem": { + "allowWrite": [ + "str" # Optional. Additional + writable paths beyond the workspace. Values must not + contain shell-style ${VAR}/$VAR expansions. + ], + "mode": "str" # Optional. Known + values are: "read-only" and "workspace-write". + }, + "network": { + "allow": [ + "str" # Optional. Network + boundary. Narrows (subtract-only) spec.sandbox.egress; + link-local/internal addresses stay blocked by the broader + egress policy. + ], + "default": "str" # Optional. Known + values are: "allow" and "deny". + }, + "rules": [ + { + "action": "str", # Required. + Known values are: "allow", "ask", and "deny". + "tool": "str", # Tool + taxonomy this rule matches (e.g. bash, file.read, + file.write, web.fetch, git.*, mcp). Required. + "enforcement": "str", # + Optional. What happens when the agent cannot enforce the + rule exactly. "strict" rejects the session; "best-effort" + degrades to the closest more-restrictive form and warns + (never widens). Defaults to strict for deny rules, + best-effort otherwise. Known values are: "strict" and + "best-effort". + "match": { + "str": "str" # + Optional. Optional keys narrowing the rule (e.g. + {"command": "npm run deploy"} for bash). Values are + globs and must not contain shell-style ${VAR}/$VAR + expansions. + } + } + ] + }, + "sandbox": { + "egress": { + "allow": [ + { + "host": "str" # + Required. + } + ], + "allowIps": [ + "str" # Optional. Exact + IPv4/IPv6 literals (not CIDRs). + ], + "subnetUuid": "str", # Optional. + Optional subnet within vpcUuid. + "unrestricted": bool, # Optional. + When true, opts out of the platform-default host allowlist + union. Mutually exclusive with allow / allowIps / vpcUuid / + subnetUuid. + "vpcUuid": "str" # Optional. + Customer VPC UUID. Create-time immutable. + }, + "idleTimeoutSeconds": 0, # Optional. + Inactivity suspend timeout. 0 means tenant policy default. + "maxLifetimeSeconds": 0, # Optional. Hard + wall-clock ceiling for the whole session, independent of + activity. Accepted, not yet enforced (returns a warning). + "mounts": [ + { + "accessKeyRef": "str", # + Names a declared tenantSecret slot in spec.secrets. + Required. + "bucket": "str", # Required. + "path": "str", # Absolute + guest path the mount appears at. Required. + "secretKeyRef": "str", # + Names a declared tenantSecret slot in spec.secrets. + Required. + "type": "str", # Required. + "s3fuse" + "endpoint": "str", # + Optional. Optional storage endpoint override; https:// + only. + "readOnly": bool # Optional. + Requested data mounts. Accepted, not yet enforced + (returns a warning). accessKeyRef/secretKeyRef name + declared tenantSecret slots in spec.secrets, validated at + create; declare those slots with env: false so the keys + stay mount-scoped and out of the guest env. + } + ], + "persistentWorkspace": bool, # Optional. + Canonical runnable environment spec JSON, immutable after create. + Validated non-secret spec.env and spec.permissions remain; + spec.secrets retains name/source/provider but omits ref and the + write-only value; metadata.name is cleared while safe metadata + such as labels remains. Required. + "sizeSlug": "str", # Optional. Requested + sandbox size (catalog: GET /v2/agents/sessions/sandbox/sizes). + Empty means the service default. + "template": "str" # Optional. Optional + sandbox template. Empty (or the legacy placeholder "coding") + means "no preference": the template derives from the adapter. + }, + "secrets": [ + { + "name": "str", # Required. + "source": "str", # Required. Known + values are: "oauth" and "tenantSecret". + "env": bool, # Optional. Whether the + resolved credential is injected into the guest env. Omitted + means true; false is for mount-only slots (tenantSecret + only). + "provider": "str", # Optional. + Declared credential requirements. source tenantSecret is + enforced today: value is write-only plaintext, extracted into + Secrets Manager at create and never persisted or returned. + source oauth requires provider, carries no value, and is + accepted but not yet enforced. ref is legacy and ignored. env + false keeps the resolved value out of the guest environment + (mount-only slots). + "ref": "str", # Optional. Declared + credential requirements. source tenantSecret is enforced + today: value is write-only plaintext, extracted into Secrets + Manager at create and never persisted or returned. source + oauth requires provider, carries no value, and is accepted + but not yet enforced. ref is legacy and ignored. env false + keeps the resolved value out of the guest environment + (mount-only slots). + "value": "str" # Optional. + Write-only tenantSecret plaintext; never persisted or + returned. + } + ], + "serving": { + "maxReplicas": 0, # Optional. Present only + when spec.mode is "served" (autoscaled fleet). + "minReplicas": 0, # Optional. Present only + when spec.mode is "served" (autoscaled fleet). + "scaleToZero": { + "idleSeconds": 0 # Optional. Present + only when spec.mode is "served" (autoscaled fleet). + }, + "targetConcurrency": 0 # Optional. Present + only when spec.mode is "served" (autoscaled fleet). + }, + "skills": [ + { + "description": "str", # + Load-bearing: the agent reads it to decide WHEN to load the + skill (progressive disclosure); write it as "Use when ...". + Max 1024 chars. Required. + "instructions": "str", # The + markdown body (the content below the SKILL.md frontmatter). + Max 32 KiB. Required. + "name": "str", # Unique across the + list; 1-64 chars of lowercase letters, digits, and single + hyphen separators - no consecutive hyphens, per the Agent + Skills spec (safe as a directory name at materialization). + Required. + "allowedTools": [ + "str" # Optional. Advisory + scoping of which tools the skill expects to use + (experimental in the skills standard). NOT a security + boundary - spec.permissions remains the only enforcement + surface. + ], + "license": "str", # Optional. + Optional standard passthrough (e.g. Apache-2.0). + "metadata": { + "str": "str" # Optional. + Optional string-map passthrough (e.g. author), rendered + into the frontmatter verbatim. + } + } + ], + "tools": { + "bundle": "str", # Optional. Accepted, not + yet enforced (returns a warning). Exactly one of mcpServers or + bundle. + "mcpServers": [ + { + "auth": { + "secretRef": "str", + # Tenant-vault secret name. Required. + "header": "str" # + Optional. HTTP header the credential is sent in. + Defaults to Authorization. + }, + "name": "str", # Optional. + Inline server name (required with url): 1-64 chars of + lowercase letters, digits, '-', '.', '_', starting/ending + alphanumeric. Unique across the list. Must not start with + do. (reserved for catalog refs). + "preloadTools": [ + "str" # Optional. + Catalog form only. Individual tools (optional integer + pin) advertised directly in the session MCP + tools/list next to the + action_search/action_invoke/action_code meta tools. + Each entry must appear in tools when tools is set; + toolbelt references are rejected. Every preloaded + schema is in the model's context on every turn, so + keep this to a handful of tools. Omitted means + nothing is preloaded. + ], + "ref": "str", # Optional. + DO-owned catalog reference (catalog form); do.* only, + e.g. do.actions. Mutually exclusive with name/url/auth. + "tools": [ + "str" # Optional. + Optional selection allowlist; omitted means all tools + the server advertises. Each entry is an individual + tool name with an optional integer version pin (e.g. + web_search, execute_code@3) or a toolbelt reference - + a server-defined named tool group written + toolbelt:[@:code:``] (e.g. + toolbelt:read-only@1; catalog refs only). Versions + are integer pins (no semver). The server owns belt + expansion; a pinned tool or belt attaches exactly + that version, an unpinned one resolves at session + create and is frozen for the session (no floating + latest). Entries non-empty, unique per server. The + selection bounds the session and is never preloaded + into the session MCP tools/list; see preloadTools. + ], + "url": "str" # Optional. + Inline server endpoint; https:// only. + } + ] + }, + "workspace": { + "repos": [ + "str" # Optional. GitHub OWNER/REPO + entries, unique. The first entry becomes the session's + repo_hint; the full list is the forward shape for multi-repo + sessions. + ] + } + } + }, + "name": "str", # Required. + "updated_at": "2020-02-20 00:00:00", # Required. + "credentials": [ + { + "name": "str", # Required. + "provider": "str", # Optional. Redacted credential + slot metadata (name/source/provider). Secret values are never + included. + "source": "str" # Optional. Known values are: + "tenantSecret" and "oauth". + } + ], + "insights": { + "logs": bool, # Optional. Whether the agent's logs are sent + to Insights. + "metrics": bool, # Optional. Accepted and stored for parity + with the Insights schema, but metrics are always collected: unregistered + metrics are dropped at ingest rather than opted out of here. + "traces": bool # Optional. Whether the agent's traces are + sent to Insights. + } + } + } + # response body for status code(s): 400, 409, 413, 415 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop( + "content_type", _headers.pop("Content-Type", None) + ) + cls: ClsType[JSON] = kwargs.pop("cls", None) + + content_type = content_type or "application/json" + _json = None + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _json = body + + _request = build_harness_create_config_request( + content_type=content_type, + json=_json, + content=_content, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [201, 400, 409, 413, 415]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + response_headers = {} + if response.status_code == 201: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 400: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 409: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 413: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 415: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore + + return cast(JSON, deserialized) # type: ignore + + @distributed_trace_async + async def get_config(self, config_id: str, **kwargs: Any) -> JSON: + # pylint: disable=line-too-long + """Get an active Environment Config and its environment spec. + + Returns one team-scoped Environment Config including the sanitized environment spec and + redacted credential slot metadata. Secret values are never returned. + + :param config_id: The Environment Config UUID. Required. + :type config_id: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 200 + response == { + "config": { + "agentspec_schema_version": "str", # The manifest contract the + server accepted at create time: the manifest's own apiVersion for legacy + envelope parses (currently agents.digitalocean.com/v1alpha1), or + agents.digitalocean.com/flat.v1 for flat-format parses. Required. + "content_hash": "str", # Lowercase SHA-256 hex digest of the + canonical sanitized manifest JSON. Required. + "created_at": "2020-02-20 00:00:00", # Required. + "created_by": "str", # Authenticated user identity that created the + config. Required. + "id": "str", # Required. + "manifest": { + "apiVersion": "str", # Optional. Legacy envelope only - must + be "agents.digitalocean.com/v1alpha1", and its presence is what routes + the document to the deprecated legacy parser. Flat-format manifests carry + no version key (the endpoint owns the contract); required, with kind and + spec, when authoring the legacy format. + "agents.digitalocean.com/v1alpha1" + "kind": "str", # Optional. Must be "Agent". "Agent" + "metadata": { + "description": "str", # Optional. Free-form + description (max 1024 chars), stored and echoed back for Console / + catalog surfaces. + "labels": { + "str": "str" # Optional. Dictionary of + :code:``. + }, + "name": "str" # Optional. Optional team-unique + session name: 1-64 chars of letters, digits, '-', '.', '_', starting + and ending alphanumeric; case-insensitive; must not be UUID-shaped. + Omit to have the control plane generate one + ("session-:code:``-:code:``"). Unique among + the team's ACTIVE (non-terminal) sessions; destroying a session frees + its name for reuse. + }, + "spec": { + "runtime": { + "adapter": "str", # Required. Known values + are: "claude-code", "opencode", "codex", "cursor", "hermes", + "codex-agentapi", "custom", "codex-cli", "cursor-cli", + "openai-agents", "claude-agent-sdk", "crewai", and "langgraph". + "config": { + "str": {} # Optional. OPAQUE, + provider-specific runtime config. Only for adapter: + codex-agentapi today, where it is the OpenAI Agents API + create-session body (agent/environment/input) that doctl + forwards verbatim to OpenAI's POST /v1/agents/sessions; + harness-api stores and echoes it but never parses or + validates its contents (so DO stays forward-compatible as + OpenAI's schema evolves). The OpenAI session_id is passed on + the create call (the openai_session_id query parameter), not + in this block. + }, + "custom": { + "image": "str", # Required. + "entrypoint": [ + "str" # Optional. BYO + container; requires adapter: custom. image is required + and should be digest-pinned. + ] + } + }, + "defaults": { + "budget": { + "maxCostUSD": 0.0, # Optional. + Accepted, not yet enforced (returns a warning). Advisory + per-run ceilings, clamped server-side to tenant policy. + "maxSteps": 0, # Optional. Accepted, + not yet enforced (returns a warning). Advisory per-run + ceilings, clamped server-side to tenant policy. + "maxWallClockSeconds": 0, # + Optional. Accepted, not yet enforced (returns a warning). + Advisory per-run ceilings, clamped server-side to tenant + policy. + "onBreach": "str" # Optional. Known + values are: "pause", "humanInput", and "fail". + } + }, + "env": { + "str": "str" # Optional. Non-secret guest + env (debug-readable - never put secrets here; use spec.secrets). + Reserved platform keys (TEAM_ID, SESSION_ID, SANDBOX_ID, + CONTROL_PLANE_CALLBACK_URL, SANDBOX_TEMPLATE, + SANDBOX_AGENT_WORKDIR, SIZE_SLUG, SANDBOX_IMAGE_ID, + SANDBOX_OCI_REF, PLANO"" *PERMISSION*"" *) are rejected. + Credential-looking values (dop"" *v1*..., sk-..., ghp_...) + trigger a create-time warning. + }, + "memory": { + "backend": "str", # Required. + "scope": "str" # Optional. Known values are: + "tenant", "user", "agent", and "session". + }, + "mode": "str", # Optional. Explicit lifecycle + discriminator. Defaults to "interactive" (spec.serving forbidden); + "served" requires spec.serving. Known values are: "interactive" and + "served". + "model": { + "default": "str", # Optional. Accepted, not + yet enforced (returns a warning). Model routing (Plano); + catalog-validated when enforcement lands. + "profile": "str", # Optional. Accepted, not + yet enforced (returns a warning). Model routing (Plano); + catalog-validated when enforcement lands. + "routing": "str" # Optional. Accepted, not + yet enforced (returns a warning). Model routing (Plano); + catalog-validated when enforcement lands. + }, + "permissions": { + "defaultAction": "str", # Optional. + Disposition for tool calls that match no rule. Defaults to "ask" + when omitted. Known values are: "allow", "ask", and "deny". + "filesystem": { + "allowWrite": [ + "str" # Optional. Additional + writable paths beyond the workspace. Values must not + contain shell-style ${VAR}/$VAR expansions. + ], + "mode": "str" # Optional. Known + values are: "read-only" and "workspace-write". + }, + "network": { + "allow": [ + "str" # Optional. Network + boundary. Narrows (subtract-only) spec.sandbox.egress; + link-local/internal addresses stay blocked by the broader + egress policy. + ], + "default": "str" # Optional. Known + values are: "allow" and "deny". + }, + "rules": [ + { + "action": "str", # Required. + Known values are: "allow", "ask", and "deny". + "tool": "str", # Tool + taxonomy this rule matches (e.g. bash, file.read, + file.write, web.fetch, git.*, mcp). Required. + "enforcement": "str", # + Optional. What happens when the agent cannot enforce the + rule exactly. "strict" rejects the session; "best-effort" + degrades to the closest more-restrictive form and warns + (never widens). Defaults to strict for deny rules, + best-effort otherwise. Known values are: "strict" and + "best-effort". + "match": { + "str": "str" # + Optional. Optional keys narrowing the rule (e.g. + {"command": "npm run deploy"} for bash). Values are + globs and must not contain shell-style ${VAR}/$VAR + expansions. + } + } + ] + }, + "sandbox": { + "egress": { + "allow": [ + { + "host": "str" # + Required. + } + ], + "allowIps": [ + "str" # Optional. Exact + IPv4/IPv6 literals (not CIDRs). + ], + "subnetUuid": "str", # Optional. + Optional subnet within vpcUuid. + "unrestricted": bool, # Optional. + When true, opts out of the platform-default host allowlist + union. Mutually exclusive with allow / allowIps / vpcUuid / + subnetUuid. + "vpcUuid": "str" # Optional. + Customer VPC UUID. Create-time immutable. + }, + "idleTimeoutSeconds": 0, # Optional. + Inactivity suspend timeout. 0 means tenant policy default. + "maxLifetimeSeconds": 0, # Optional. Hard + wall-clock ceiling for the whole session, independent of + activity. Accepted, not yet enforced (returns a warning). + "mounts": [ + { + "accessKeyRef": "str", # + Names a declared tenantSecret slot in spec.secrets. + Required. + "bucket": "str", # Required. + "path": "str", # Absolute + guest path the mount appears at. Required. + "secretKeyRef": "str", # + Names a declared tenantSecret slot in spec.secrets. + Required. + "type": "str", # Required. + "s3fuse" + "endpoint": "str", # + Optional. Optional storage endpoint override; https:// + only. + "readOnly": bool # Optional. + Requested data mounts. Accepted, not yet enforced + (returns a warning). accessKeyRef/secretKeyRef name + declared tenantSecret slots in spec.secrets, validated at + create; declare those slots with env: false so the keys + stay mount-scoped and out of the guest env. + } + ], + "persistentWorkspace": bool, # Optional. + Canonical runnable environment spec JSON, immutable after create. + Validated non-secret spec.env and spec.permissions remain; + spec.secrets retains name/source/provider but omits ref and the + write-only value; metadata.name is cleared while safe metadata + such as labels remains. Required. + "sizeSlug": "str", # Optional. Requested + sandbox size (catalog: GET /v2/agents/sessions/sandbox/sizes). + Empty means the service default. + "template": "str" # Optional. Optional + sandbox template. Empty (or the legacy placeholder "coding") + means "no preference": the template derives from the adapter. + }, + "secrets": [ + { + "name": "str", # Required. + "source": "str", # Required. Known + values are: "oauth" and "tenantSecret". + "env": bool, # Optional. Whether the + resolved credential is injected into the guest env. Omitted + means true; false is for mount-only slots (tenantSecret + only). + "provider": "str", # Optional. + Declared credential requirements. source tenantSecret is + enforced today: value is write-only plaintext, extracted into + Secrets Manager at create and never persisted or returned. + source oauth requires provider, carries no value, and is + accepted but not yet enforced. ref is legacy and ignored. env + false keeps the resolved value out of the guest environment + (mount-only slots). + "ref": "str", # Optional. Declared + credential requirements. source tenantSecret is enforced + today: value is write-only plaintext, extracted into Secrets + Manager at create and never persisted or returned. source + oauth requires provider, carries no value, and is accepted + but not yet enforced. ref is legacy and ignored. env false + keeps the resolved value out of the guest environment + (mount-only slots). + "value": "str" # Optional. + Write-only tenantSecret plaintext; never persisted or + returned. + } + ], + "serving": { + "maxReplicas": 0, # Optional. Present only + when spec.mode is "served" (autoscaled fleet). + "minReplicas": 0, # Optional. Present only + when spec.mode is "served" (autoscaled fleet). + "scaleToZero": { + "idleSeconds": 0 # Optional. Present + only when spec.mode is "served" (autoscaled fleet). + }, + "targetConcurrency": 0 # Optional. Present + only when spec.mode is "served" (autoscaled fleet). + }, + "skills": [ + { + "description": "str", # + Load-bearing: the agent reads it to decide WHEN to load the + skill (progressive disclosure); write it as "Use when ...". + Max 1024 chars. Required. + "instructions": "str", # The + markdown body (the content below the SKILL.md frontmatter). + Max 32 KiB. Required. + "name": "str", # Unique across the + list; 1-64 chars of lowercase letters, digits, and single + hyphen separators - no consecutive hyphens, per the Agent + Skills spec (safe as a directory name at materialization). + Required. + "allowedTools": [ + "str" # Optional. Advisory + scoping of which tools the skill expects to use + (experimental in the skills standard). NOT a security + boundary - spec.permissions remains the only enforcement + surface. + ], + "license": "str", # Optional. + Optional standard passthrough (e.g. Apache-2.0). + "metadata": { + "str": "str" # Optional. + Optional string-map passthrough (e.g. author), rendered + into the frontmatter verbatim. + } + } + ], + "tools": { + "bundle": "str", # Optional. Accepted, not + yet enforced (returns a warning). Exactly one of mcpServers or + bundle. + "mcpServers": [ + { + "auth": { + "secretRef": "str", + # Tenant-vault secret name. Required. + "header": "str" # + Optional. HTTP header the credential is sent in. + Defaults to Authorization. + }, + "name": "str", # Optional. + Inline server name (required with url): 1-64 chars of + lowercase letters, digits, '-', '.', '_', starting/ending + alphanumeric. Unique across the list. Must not start with + do. (reserved for catalog refs). + "preloadTools": [ + "str" # Optional. + Catalog form only. Individual tools (optional integer + pin) advertised directly in the session MCP + tools/list next to the + action_search/action_invoke/action_code meta tools. + Each entry must appear in tools when tools is set; + toolbelt references are rejected. Every preloaded + schema is in the model's context on every turn, so + keep this to a handful of tools. Omitted means + nothing is preloaded. + ], + "ref": "str", # Optional. + DO-owned catalog reference (catalog form); do.* only, + e.g. do.actions. Mutually exclusive with name/url/auth. + "tools": [ + "str" # Optional. + Optional selection allowlist; omitted means all tools + the server advertises. Each entry is an individual + tool name with an optional integer version pin (e.g. + web_search, execute_code@3) or a toolbelt reference - + a server-defined named tool group written + toolbelt:[@:code:``] (e.g. + toolbelt:read-only@1; catalog refs only). Versions + are integer pins (no semver). The server owns belt + expansion; a pinned tool or belt attaches exactly + that version, an unpinned one resolves at session + create and is frozen for the session (no floating + latest). Entries non-empty, unique per server. The + selection bounds the session and is never preloaded + into the session MCP tools/list; see preloadTools. + ], + "url": "str" # Optional. + Inline server endpoint; https:// only. + } + ] + }, + "workspace": { + "repos": [ + "str" # Optional. GitHub OWNER/REPO + entries, unique. The first entry becomes the session's + repo_hint; the full list is the forward shape for multi-repo + sessions. + ] + } + } + }, + "name": "str", # Required. + "updated_at": "2020-02-20 00:00:00", # Required. + "credentials": [ + { + "name": "str", # Required. + "provider": "str", # Optional. Redacted credential + slot metadata (name/source/provider). Secret values are never + included. + "source": "str" # Optional. Known values are: + "tenantSecret" and "oauth". + } + ], + "insights": { + "logs": bool, # Optional. Whether the agent's logs are sent + to Insights. + "metrics": bool, # Optional. Accepted and stored for parity + with the Insights schema, but metrics are always collected: unregistered + metrics are dropped at ingest rather than opted out of here. + "traces": bool # Optional. Whether the agent's traces are + sent to Insights. + } + } + } + # response body for status code(s): 400, 404 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[JSON] = kwargs.pop("cls", None) + + _request = build_harness_get_config_request( + config_id=config_id, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [200, 400, 404]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + response_headers = {} + if response.status_code == 200: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 400: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 404: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore + + return cast(JSON, deserialized) # type: ignore + + @distributed_trace_async + async def delete_config(self, config_id: str, **kwargs: Any) -> Optional[JSON]: + # pylint: disable=line-too-long + """Soft-delete an Environment Config. + + Releases the active team-scoped name; the config row is retained (soft delete), so sessions + created from it keep a resolvable config_id. + + :param config_id: The Environment Config UUID. Required. + :type config_id: str + :return: JSON object or None + :rtype: JSON or None + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 400, 404, 409 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[Optional[JSON]] = kwargs.pop("cls", None) + + _request = build_harness_delete_config_request( + config_id=config_id, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [204, 400, 404, 409]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + deserialized = None + response_headers = {} + if response.status_code == 204: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.status_code == 400: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 404: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 409: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, deserialized, response_headers) # type: ignore + + return deserialized # type: ignore + + @overload + async def create_config_clone( + self, + config_id: str, + body: JSON, + *, + content_type: str = "application/json", + **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """Clone an Environment Config under a new name. + + Copies the source config's sanitized manifest to a new UUID. Source and clone are independent + (no lineage). Credential bindings are never copied: if the manifest declares secret slots, + supply secrets/oauth_assignments again (write-only). content_hash matches the source when the + manifest is unchanged. + + :param config_id: The Environment Config UUID. Required. + :type config_id: str + :param body: Clone body with required new name plus write-only credentials for any declared + slots. Required. + :type body: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # JSON input template you can fill out and use as your body input. + body = { + "name": "str", # New config name, unique among the team's active configs. + Required. + "oauth_assignments": { + "str": "str" # Optional. Write-only map from oauth logical slot to + an OAuth assignment UUID. Required when the source declares those slots; + never copied from the source. + }, + "secrets": { + "str": "str" # Optional. Write-only map from tenantSecret logical + slot to plaintext. Required when the source declares those slots; never + copied from the source. + } + } + + # response body for status code(s): 201 + response == { + "config": { + "agentspec_schema_version": "str", # The manifest contract the + server accepted at create time: the manifest's own apiVersion for legacy + envelope parses (currently agents.digitalocean.com/v1alpha1), or + agents.digitalocean.com/flat.v1 for flat-format parses. Required. + "content_hash": "str", # Lowercase SHA-256 hex digest of the + canonical sanitized manifest JSON. Required. + "created_at": "2020-02-20 00:00:00", # Required. + "created_by": "str", # Authenticated user identity that created the + config. Required. + "id": "str", # Required. + "manifest": { + "apiVersion": "str", # Optional. Legacy envelope only - must + be "agents.digitalocean.com/v1alpha1", and its presence is what routes + the document to the deprecated legacy parser. Flat-format manifests carry + no version key (the endpoint owns the contract); required, with kind and + spec, when authoring the legacy format. + "agents.digitalocean.com/v1alpha1" + "kind": "str", # Optional. Must be "Agent". "Agent" + "metadata": { + "description": "str", # Optional. Free-form + description (max 1024 chars), stored and echoed back for Console / + catalog surfaces. + "labels": { + "str": "str" # Optional. Dictionary of + :code:``. + }, + "name": "str" # Optional. Optional team-unique + session name: 1-64 chars of letters, digits, '-', '.', '_', starting + and ending alphanumeric; case-insensitive; must not be UUID-shaped. + Omit to have the control plane generate one + ("session-:code:``-:code:``"). Unique among + the team's ACTIVE (non-terminal) sessions; destroying a session frees + its name for reuse. + }, + "spec": { + "runtime": { + "adapter": "str", # Required. Known values + are: "claude-code", "opencode", "codex", "cursor", "hermes", + "codex-agentapi", "custom", "codex-cli", "cursor-cli", + "openai-agents", "claude-agent-sdk", "crewai", and "langgraph". + "config": { + "str": {} # Optional. OPAQUE, + provider-specific runtime config. Only for adapter: + codex-agentapi today, where it is the OpenAI Agents API + create-session body (agent/environment/input) that doctl + forwards verbatim to OpenAI's POST /v1/agents/sessions; + harness-api stores and echoes it but never parses or + validates its contents (so DO stays forward-compatible as + OpenAI's schema evolves). The OpenAI session_id is passed on + the create call (the openai_session_id query parameter), not + in this block. + }, + "custom": { + "image": "str", # Required. + "entrypoint": [ + "str" # Optional. BYO + container; requires adapter: custom. image is required + and should be digest-pinned. + ] + } + }, + "defaults": { + "budget": { + "maxCostUSD": 0.0, # Optional. + Accepted, not yet enforced (returns a warning). Advisory + per-run ceilings, clamped server-side to tenant policy. + "maxSteps": 0, # Optional. Accepted, + not yet enforced (returns a warning). Advisory per-run + ceilings, clamped server-side to tenant policy. + "maxWallClockSeconds": 0, # + Optional. Accepted, not yet enforced (returns a warning). + Advisory per-run ceilings, clamped server-side to tenant + policy. + "onBreach": "str" # Optional. Known + values are: "pause", "humanInput", and "fail". + } + }, + "env": { + "str": "str" # Optional. Non-secret guest + env (debug-readable - never put secrets here; use spec.secrets). + Reserved platform keys (TEAM_ID, SESSION_ID, SANDBOX_ID, + CONTROL_PLANE_CALLBACK_URL, SANDBOX_TEMPLATE, + SANDBOX_AGENT_WORKDIR, SIZE_SLUG, SANDBOX_IMAGE_ID, + SANDBOX_OCI_REF, PLANO"" *PERMISSION*"" *) are rejected. + Credential-looking values (dop"" *v1*..., sk-..., ghp_...) + trigger a create-time warning. + }, + "memory": { + "backend": "str", # Required. + "scope": "str" # Optional. Known values are: + "tenant", "user", "agent", and "session". + }, + "mode": "str", # Optional. Explicit lifecycle + discriminator. Defaults to "interactive" (spec.serving forbidden); + "served" requires spec.serving. Known values are: "interactive" and + "served". + "model": { + "default": "str", # Optional. Accepted, not + yet enforced (returns a warning). Model routing (Plano); + catalog-validated when enforcement lands. + "profile": "str", # Optional. Accepted, not + yet enforced (returns a warning). Model routing (Plano); + catalog-validated when enforcement lands. + "routing": "str" # Optional. Accepted, not + yet enforced (returns a warning). Model routing (Plano); + catalog-validated when enforcement lands. + }, + "permissions": { + "defaultAction": "str", # Optional. + Disposition for tool calls that match no rule. Defaults to "ask" + when omitted. Known values are: "allow", "ask", and "deny". + "filesystem": { + "allowWrite": [ + "str" # Optional. Additional + writable paths beyond the workspace. Values must not + contain shell-style ${VAR}/$VAR expansions. + ], + "mode": "str" # Optional. Known + values are: "read-only" and "workspace-write". + }, + "network": { + "allow": [ + "str" # Optional. Network + boundary. Narrows (subtract-only) spec.sandbox.egress; + link-local/internal addresses stay blocked by the broader + egress policy. + ], + "default": "str" # Optional. Known + values are: "allow" and "deny". + }, + "rules": [ + { + "action": "str", # Required. + Known values are: "allow", "ask", and "deny". + "tool": "str", # Tool + taxonomy this rule matches (e.g. bash, file.read, + file.write, web.fetch, git.*, mcp). Required. + "enforcement": "str", # + Optional. What happens when the agent cannot enforce the + rule exactly. "strict" rejects the session; "best-effort" + degrades to the closest more-restrictive form and warns + (never widens). Defaults to strict for deny rules, + best-effort otherwise. Known values are: "strict" and + "best-effort". + "match": { + "str": "str" # + Optional. Optional keys narrowing the rule (e.g. + {"command": "npm run deploy"} for bash). Values are + globs and must not contain shell-style ${VAR}/$VAR + expansions. + } + } + ] + }, + "sandbox": { + "egress": { + "allow": [ + { + "host": "str" # + Required. + } + ], + "allowIps": [ + "str" # Optional. Exact + IPv4/IPv6 literals (not CIDRs). + ], + "subnetUuid": "str", # Optional. + Optional subnet within vpcUuid. + "unrestricted": bool, # Optional. + When true, opts out of the platform-default host allowlist + union. Mutually exclusive with allow / allowIps / vpcUuid / + subnetUuid. + "vpcUuid": "str" # Optional. + Customer VPC UUID. Create-time immutable. + }, + "idleTimeoutSeconds": 0, # Optional. + Inactivity suspend timeout. 0 means tenant policy default. + "maxLifetimeSeconds": 0, # Optional. Hard + wall-clock ceiling for the whole session, independent of + activity. Accepted, not yet enforced (returns a warning). + "mounts": [ + { + "accessKeyRef": "str", # + Names a declared tenantSecret slot in spec.secrets. + Required. + "bucket": "str", # Required. + "path": "str", # Absolute + guest path the mount appears at. Required. + "secretKeyRef": "str", # + Names a declared tenantSecret slot in spec.secrets. + Required. + "type": "str", # Required. + "s3fuse" + "endpoint": "str", # + Optional. Optional storage endpoint override; https:// + only. + "readOnly": bool # Optional. + Requested data mounts. Accepted, not yet enforced + (returns a warning). accessKeyRef/secretKeyRef name + declared tenantSecret slots in spec.secrets, validated at + create; declare those slots with env: false so the keys + stay mount-scoped and out of the guest env. + } + ], + "persistentWorkspace": bool, # Optional. + Canonical runnable environment spec JSON, immutable after create. + Validated non-secret spec.env and spec.permissions remain; + spec.secrets retains name/source/provider but omits ref and the + write-only value; metadata.name is cleared while safe metadata + such as labels remains. Required. + "sizeSlug": "str", # Optional. Requested + sandbox size (catalog: GET /v2/agents/sessions/sandbox/sizes). + Empty means the service default. + "template": "str" # Optional. Optional + sandbox template. Empty (or the legacy placeholder "coding") + means "no preference": the template derives from the adapter. + }, + "secrets": [ + { + "name": "str", # Required. + "source": "str", # Required. Known + values are: "oauth" and "tenantSecret". + "env": bool, # Optional. Whether the + resolved credential is injected into the guest env. Omitted + means true; false is for mount-only slots (tenantSecret + only). + "provider": "str", # Optional. + Declared credential requirements. source tenantSecret is + enforced today: value is write-only plaintext, extracted into + Secrets Manager at create and never persisted or returned. + source oauth requires provider, carries no value, and is + accepted but not yet enforced. ref is legacy and ignored. env + false keeps the resolved value out of the guest environment + (mount-only slots). + "ref": "str", # Optional. Declared + credential requirements. source tenantSecret is enforced + today: value is write-only plaintext, extracted into Secrets + Manager at create and never persisted or returned. source + oauth requires provider, carries no value, and is accepted + but not yet enforced. ref is legacy and ignored. env false + keeps the resolved value out of the guest environment + (mount-only slots). + "value": "str" # Optional. + Write-only tenantSecret plaintext; never persisted or + returned. + } + ], + "serving": { + "maxReplicas": 0, # Optional. Present only + when spec.mode is "served" (autoscaled fleet). + "minReplicas": 0, # Optional. Present only + when spec.mode is "served" (autoscaled fleet). + "scaleToZero": { + "idleSeconds": 0 # Optional. Present + only when spec.mode is "served" (autoscaled fleet). + }, + "targetConcurrency": 0 # Optional. Present + only when spec.mode is "served" (autoscaled fleet). + }, + "skills": [ + { + "description": "str", # + Load-bearing: the agent reads it to decide WHEN to load the + skill (progressive disclosure); write it as "Use when ...". + Max 1024 chars. Required. + "instructions": "str", # The + markdown body (the content below the SKILL.md frontmatter). + Max 32 KiB. Required. + "name": "str", # Unique across the + list; 1-64 chars of lowercase letters, digits, and single + hyphen separators - no consecutive hyphens, per the Agent + Skills spec (safe as a directory name at materialization). + Required. + "allowedTools": [ + "str" # Optional. Advisory + scoping of which tools the skill expects to use + (experimental in the skills standard). NOT a security + boundary - spec.permissions remains the only enforcement + surface. + ], + "license": "str", # Optional. + Optional standard passthrough (e.g. Apache-2.0). + "metadata": { + "str": "str" # Optional. + Optional string-map passthrough (e.g. author), rendered + into the frontmatter verbatim. + } + } + ], + "tools": { + "bundle": "str", # Optional. Accepted, not + yet enforced (returns a warning). Exactly one of mcpServers or + bundle. + "mcpServers": [ + { + "auth": { + "secretRef": "str", + # Tenant-vault secret name. Required. + "header": "str" # + Optional. HTTP header the credential is sent in. + Defaults to Authorization. + }, + "name": "str", # Optional. + Inline server name (required with url): 1-64 chars of + lowercase letters, digits, '-', '.', '_', starting/ending + alphanumeric. Unique across the list. Must not start with + do. (reserved for catalog refs). + "preloadTools": [ + "str" # Optional. + Catalog form only. Individual tools (optional integer + pin) advertised directly in the session MCP + tools/list next to the + action_search/action_invoke/action_code meta tools. + Each entry must appear in tools when tools is set; + toolbelt references are rejected. Every preloaded + schema is in the model's context on every turn, so + keep this to a handful of tools. Omitted means + nothing is preloaded. + ], + "ref": "str", # Optional. + DO-owned catalog reference (catalog form); do.* only, + e.g. do.actions. Mutually exclusive with name/url/auth. + "tools": [ + "str" # Optional. + Optional selection allowlist; omitted means all tools + the server advertises. Each entry is an individual + tool name with an optional integer version pin (e.g. + web_search, execute_code@3) or a toolbelt reference - + a server-defined named tool group written + toolbelt:[@:code:``] (e.g. + toolbelt:read-only@1; catalog refs only). Versions + are integer pins (no semver). The server owns belt + expansion; a pinned tool or belt attaches exactly + that version, an unpinned one resolves at session + create and is frozen for the session (no floating + latest). Entries non-empty, unique per server. The + selection bounds the session and is never preloaded + into the session MCP tools/list; see preloadTools. + ], + "url": "str" # Optional. + Inline server endpoint; https:// only. + } + ] + }, + "workspace": { + "repos": [ + "str" # Optional. GitHub OWNER/REPO + entries, unique. The first entry becomes the session's + repo_hint; the full list is the forward shape for multi-repo + sessions. + ] + } + } + }, + "name": "str", # Required. + "updated_at": "2020-02-20 00:00:00", # Required. + "credentials": [ + { + "name": "str", # Required. + "provider": "str", # Optional. Redacted credential + slot metadata (name/source/provider). Secret values are never + included. + "source": "str" # Optional. Known values are: + "tenantSecret" and "oauth". + } + ], + "insights": { + "logs": bool, # Optional. Whether the agent's logs are sent + to Insights. + "metrics": bool, # Optional. Accepted and stored for parity + with the Insights schema, but metrics are always collected: unregistered + metrics are dropped at ingest rather than opted out of here. + "traces": bool # Optional. Whether the agent's traces are + sent to Insights. + } + } + } + # response body for status code(s): 400, 404, 409, 413, 415 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + + @overload + async def create_config_clone( + self, + config_id: str, + body: IO[bytes], + *, + content_type: str = "application/json", + **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """Clone an Environment Config under a new name. + + Copies the source config's sanitized manifest to a new UUID. Source and clone are independent + (no lineage). Credential bindings are never copied: if the manifest declares secret slots, + supply secrets/oauth_assignments again (write-only). content_hash matches the source when the + manifest is unchanged. + + :param config_id: The Environment Config UUID. Required. + :type config_id: str + :param body: Clone body with required new name plus write-only credentials for any declared + slots. Required. + :type body: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 201 + response == { + "config": { + "agentspec_schema_version": "str", # The manifest contract the + server accepted at create time: the manifest's own apiVersion for legacy + envelope parses (currently agents.digitalocean.com/v1alpha1), or + agents.digitalocean.com/flat.v1 for flat-format parses. Required. + "content_hash": "str", # Lowercase SHA-256 hex digest of the + canonical sanitized manifest JSON. Required. + "created_at": "2020-02-20 00:00:00", # Required. + "created_by": "str", # Authenticated user identity that created the + config. Required. + "id": "str", # Required. + "manifest": { + "apiVersion": "str", # Optional. Legacy envelope only - must + be "agents.digitalocean.com/v1alpha1", and its presence is what routes + the document to the deprecated legacy parser. Flat-format manifests carry + no version key (the endpoint owns the contract); required, with kind and + spec, when authoring the legacy format. + "agents.digitalocean.com/v1alpha1" + "kind": "str", # Optional. Must be "Agent". "Agent" + "metadata": { + "description": "str", # Optional. Free-form + description (max 1024 chars), stored and echoed back for Console / + catalog surfaces. + "labels": { + "str": "str" # Optional. Dictionary of + :code:``. + }, + "name": "str" # Optional. Optional team-unique + session name: 1-64 chars of letters, digits, '-', '.', '_', starting + and ending alphanumeric; case-insensitive; must not be UUID-shaped. + Omit to have the control plane generate one + ("session-:code:``-:code:``"). Unique among + the team's ACTIVE (non-terminal) sessions; destroying a session frees + its name for reuse. + }, + "spec": { + "runtime": { + "adapter": "str", # Required. Known values + are: "claude-code", "opencode", "codex", "cursor", "hermes", + "codex-agentapi", "custom", "codex-cli", "cursor-cli", + "openai-agents", "claude-agent-sdk", "crewai", and "langgraph". + "config": { + "str": {} # Optional. OPAQUE, + provider-specific runtime config. Only for adapter: + codex-agentapi today, where it is the OpenAI Agents API + create-session body (agent/environment/input) that doctl + forwards verbatim to OpenAI's POST /v1/agents/sessions; + harness-api stores and echoes it but never parses or + validates its contents (so DO stays forward-compatible as + OpenAI's schema evolves). The OpenAI session_id is passed on + the create call (the openai_session_id query parameter), not + in this block. + }, + "custom": { + "image": "str", # Required. + "entrypoint": [ + "str" # Optional. BYO + container; requires adapter: custom. image is required + and should be digest-pinned. + ] + } + }, + "defaults": { + "budget": { + "maxCostUSD": 0.0, # Optional. + Accepted, not yet enforced (returns a warning). Advisory + per-run ceilings, clamped server-side to tenant policy. + "maxSteps": 0, # Optional. Accepted, + not yet enforced (returns a warning). Advisory per-run + ceilings, clamped server-side to tenant policy. + "maxWallClockSeconds": 0, # + Optional. Accepted, not yet enforced (returns a warning). + Advisory per-run ceilings, clamped server-side to tenant + policy. + "onBreach": "str" # Optional. Known + values are: "pause", "humanInput", and "fail". + } + }, + "env": { + "str": "str" # Optional. Non-secret guest + env (debug-readable - never put secrets here; use spec.secrets). + Reserved platform keys (TEAM_ID, SESSION_ID, SANDBOX_ID, + CONTROL_PLANE_CALLBACK_URL, SANDBOX_TEMPLATE, + SANDBOX_AGENT_WORKDIR, SIZE_SLUG, SANDBOX_IMAGE_ID, + SANDBOX_OCI_REF, PLANO"" *PERMISSION*"" *) are rejected. + Credential-looking values (dop"" *v1*..., sk-..., ghp_...) + trigger a create-time warning. + }, + "memory": { + "backend": "str", # Required. + "scope": "str" # Optional. Known values are: + "tenant", "user", "agent", and "session". + }, + "mode": "str", # Optional. Explicit lifecycle + discriminator. Defaults to "interactive" (spec.serving forbidden); + "served" requires spec.serving. Known values are: "interactive" and + "served". + "model": { + "default": "str", # Optional. Accepted, not + yet enforced (returns a warning). Model routing (Plano); + catalog-validated when enforcement lands. + "profile": "str", # Optional. Accepted, not + yet enforced (returns a warning). Model routing (Plano); + catalog-validated when enforcement lands. + "routing": "str" # Optional. Accepted, not + yet enforced (returns a warning). Model routing (Plano); + catalog-validated when enforcement lands. + }, + "permissions": { + "defaultAction": "str", # Optional. + Disposition for tool calls that match no rule. Defaults to "ask" + when omitted. Known values are: "allow", "ask", and "deny". + "filesystem": { + "allowWrite": [ + "str" # Optional. Additional + writable paths beyond the workspace. Values must not + contain shell-style ${VAR}/$VAR expansions. + ], + "mode": "str" # Optional. Known + values are: "read-only" and "workspace-write". + }, + "network": { + "allow": [ + "str" # Optional. Network + boundary. Narrows (subtract-only) spec.sandbox.egress; + link-local/internal addresses stay blocked by the broader + egress policy. + ], + "default": "str" # Optional. Known + values are: "allow" and "deny". + }, + "rules": [ + { + "action": "str", # Required. + Known values are: "allow", "ask", and "deny". + "tool": "str", # Tool + taxonomy this rule matches (e.g. bash, file.read, + file.write, web.fetch, git.*, mcp). Required. + "enforcement": "str", # + Optional. What happens when the agent cannot enforce the + rule exactly. "strict" rejects the session; "best-effort" + degrades to the closest more-restrictive form and warns + (never widens). Defaults to strict for deny rules, + best-effort otherwise. Known values are: "strict" and + "best-effort". + "match": { + "str": "str" # + Optional. Optional keys narrowing the rule (e.g. + {"command": "npm run deploy"} for bash). Values are + globs and must not contain shell-style ${VAR}/$VAR + expansions. + } + } + ] + }, + "sandbox": { + "egress": { + "allow": [ + { + "host": "str" # + Required. + } + ], + "allowIps": [ + "str" # Optional. Exact + IPv4/IPv6 literals (not CIDRs). + ], + "subnetUuid": "str", # Optional. + Optional subnet within vpcUuid. + "unrestricted": bool, # Optional. + When true, opts out of the platform-default host allowlist + union. Mutually exclusive with allow / allowIps / vpcUuid / + subnetUuid. + "vpcUuid": "str" # Optional. + Customer VPC UUID. Create-time immutable. + }, + "idleTimeoutSeconds": 0, # Optional. + Inactivity suspend timeout. 0 means tenant policy default. + "maxLifetimeSeconds": 0, # Optional. Hard + wall-clock ceiling for the whole session, independent of + activity. Accepted, not yet enforced (returns a warning). + "mounts": [ + { + "accessKeyRef": "str", # + Names a declared tenantSecret slot in spec.secrets. + Required. + "bucket": "str", # Required. + "path": "str", # Absolute + guest path the mount appears at. Required. + "secretKeyRef": "str", # + Names a declared tenantSecret slot in spec.secrets. + Required. + "type": "str", # Required. + "s3fuse" + "endpoint": "str", # + Optional. Optional storage endpoint override; https:// + only. + "readOnly": bool # Optional. + Requested data mounts. Accepted, not yet enforced + (returns a warning). accessKeyRef/secretKeyRef name + declared tenantSecret slots in spec.secrets, validated at + create; declare those slots with env: false so the keys + stay mount-scoped and out of the guest env. + } + ], + "persistentWorkspace": bool, # Optional. + Canonical runnable environment spec JSON, immutable after create. + Validated non-secret spec.env and spec.permissions remain; + spec.secrets retains name/source/provider but omits ref and the + write-only value; metadata.name is cleared while safe metadata + such as labels remains. Required. + "sizeSlug": "str", # Optional. Requested + sandbox size (catalog: GET /v2/agents/sessions/sandbox/sizes). + Empty means the service default. + "template": "str" # Optional. Optional + sandbox template. Empty (or the legacy placeholder "coding") + means "no preference": the template derives from the adapter. + }, + "secrets": [ + { + "name": "str", # Required. + "source": "str", # Required. Known + values are: "oauth" and "tenantSecret". + "env": bool, # Optional. Whether the + resolved credential is injected into the guest env. Omitted + means true; false is for mount-only slots (tenantSecret + only). + "provider": "str", # Optional. + Declared credential requirements. source tenantSecret is + enforced today: value is write-only plaintext, extracted into + Secrets Manager at create and never persisted or returned. + source oauth requires provider, carries no value, and is + accepted but not yet enforced. ref is legacy and ignored. env + false keeps the resolved value out of the guest environment + (mount-only slots). + "ref": "str", # Optional. Declared + credential requirements. source tenantSecret is enforced + today: value is write-only plaintext, extracted into Secrets + Manager at create and never persisted or returned. source + oauth requires provider, carries no value, and is accepted + but not yet enforced. ref is legacy and ignored. env false + keeps the resolved value out of the guest environment + (mount-only slots). + "value": "str" # Optional. + Write-only tenantSecret plaintext; never persisted or + returned. + } + ], + "serving": { + "maxReplicas": 0, # Optional. Present only + when spec.mode is "served" (autoscaled fleet). + "minReplicas": 0, # Optional. Present only + when spec.mode is "served" (autoscaled fleet). + "scaleToZero": { + "idleSeconds": 0 # Optional. Present + only when spec.mode is "served" (autoscaled fleet). + }, + "targetConcurrency": 0 # Optional. Present + only when spec.mode is "served" (autoscaled fleet). + }, + "skills": [ + { + "description": "str", # + Load-bearing: the agent reads it to decide WHEN to load the + skill (progressive disclosure); write it as "Use when ...". + Max 1024 chars. Required. + "instructions": "str", # The + markdown body (the content below the SKILL.md frontmatter). + Max 32 KiB. Required. + "name": "str", # Unique across the + list; 1-64 chars of lowercase letters, digits, and single + hyphen separators - no consecutive hyphens, per the Agent + Skills spec (safe as a directory name at materialization). + Required. + "allowedTools": [ + "str" # Optional. Advisory + scoping of which tools the skill expects to use + (experimental in the skills standard). NOT a security + boundary - spec.permissions remains the only enforcement + surface. + ], + "license": "str", # Optional. + Optional standard passthrough (e.g. Apache-2.0). + "metadata": { + "str": "str" # Optional. + Optional string-map passthrough (e.g. author), rendered + into the frontmatter verbatim. + } + } + ], + "tools": { + "bundle": "str", # Optional. Accepted, not + yet enforced (returns a warning). Exactly one of mcpServers or + bundle. + "mcpServers": [ + { + "auth": { + "secretRef": "str", + # Tenant-vault secret name. Required. + "header": "str" # + Optional. HTTP header the credential is sent in. + Defaults to Authorization. + }, + "name": "str", # Optional. + Inline server name (required with url): 1-64 chars of + lowercase letters, digits, '-', '.', '_', starting/ending + alphanumeric. Unique across the list. Must not start with + do. (reserved for catalog refs). + "preloadTools": [ + "str" # Optional. + Catalog form only. Individual tools (optional integer + pin) advertised directly in the session MCP + tools/list next to the + action_search/action_invoke/action_code meta tools. + Each entry must appear in tools when tools is set; + toolbelt references are rejected. Every preloaded + schema is in the model's context on every turn, so + keep this to a handful of tools. Omitted means + nothing is preloaded. + ], + "ref": "str", # Optional. + DO-owned catalog reference (catalog form); do.* only, + e.g. do.actions. Mutually exclusive with name/url/auth. + "tools": [ + "str" # Optional. + Optional selection allowlist; omitted means all tools + the server advertises. Each entry is an individual + tool name with an optional integer version pin (e.g. + web_search, execute_code@3) or a toolbelt reference - + a server-defined named tool group written + toolbelt:[@:code:``] (e.g. + toolbelt:read-only@1; catalog refs only). Versions + are integer pins (no semver). The server owns belt + expansion; a pinned tool or belt attaches exactly + that version, an unpinned one resolves at session + create and is frozen for the session (no floating + latest). Entries non-empty, unique per server. The + selection bounds the session and is never preloaded + into the session MCP tools/list; see preloadTools. + ], + "url": "str" # Optional. + Inline server endpoint; https:// only. + } + ] + }, + "workspace": { + "repos": [ + "str" # Optional. GitHub OWNER/REPO + entries, unique. The first entry becomes the session's + repo_hint; the full list is the forward shape for multi-repo + sessions. + ] + } + } + }, + "name": "str", # Required. + "updated_at": "2020-02-20 00:00:00", # Required. + "credentials": [ + { + "name": "str", # Required. + "provider": "str", # Optional. Redacted credential + slot metadata (name/source/provider). Secret values are never + included. + "source": "str" # Optional. Known values are: + "tenantSecret" and "oauth". + } + ], + "insights": { + "logs": bool, # Optional. Whether the agent's logs are sent + to Insights. + "metrics": bool, # Optional. Accepted and stored for parity + with the Insights schema, but metrics are always collected: unregistered + metrics are dropped at ingest rather than opted out of here. + "traces": bool # Optional. Whether the agent's traces are + sent to Insights. + } + } + } + # response body for status code(s): 400, 404, 409, 413, 415 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + + @distributed_trace_async + async def create_config_clone( + self, config_id: str, body: Union[JSON, IO[bytes]], **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """Clone an Environment Config under a new name. + + Copies the source config's sanitized manifest to a new UUID. Source and clone are independent + (no lineage). Credential bindings are never copied: if the manifest declares secret slots, + supply secrets/oauth_assignments again (write-only). content_hash matches the source when the + manifest is unchanged. + + :param config_id: The Environment Config UUID. Required. + :type config_id: str + :param body: Clone body with required new name plus write-only credentials for any declared + slots. Is either a JSON type or a IO[bytes] type. Required. + :type body: JSON or IO[bytes] + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # JSON input template you can fill out and use as your body input. + body = { + "name": "str", # New config name, unique among the team's active configs. + Required. + "oauth_assignments": { + "str": "str" # Optional. Write-only map from oauth logical slot to + an OAuth assignment UUID. Required when the source declares those slots; + never copied from the source. + }, + "secrets": { + "str": "str" # Optional. Write-only map from tenantSecret logical + slot to plaintext. Required when the source declares those slots; never + copied from the source. + } + } + + # response body for status code(s): 201 + response == { + "config": { + "agentspec_schema_version": "str", # The manifest contract the + server accepted at create time: the manifest's own apiVersion for legacy + envelope parses (currently agents.digitalocean.com/v1alpha1), or + agents.digitalocean.com/flat.v1 for flat-format parses. Required. + "content_hash": "str", # Lowercase SHA-256 hex digest of the + canonical sanitized manifest JSON. Required. + "created_at": "2020-02-20 00:00:00", # Required. + "created_by": "str", # Authenticated user identity that created the + config. Required. + "id": "str", # Required. + "manifest": { + "apiVersion": "str", # Optional. Legacy envelope only - must + be "agents.digitalocean.com/v1alpha1", and its presence is what routes + the document to the deprecated legacy parser. Flat-format manifests carry + no version key (the endpoint owns the contract); required, with kind and + spec, when authoring the legacy format. + "agents.digitalocean.com/v1alpha1" + "kind": "str", # Optional. Must be "Agent". "Agent" + "metadata": { + "description": "str", # Optional. Free-form + description (max 1024 chars), stored and echoed back for Console / + catalog surfaces. + "labels": { + "str": "str" # Optional. Dictionary of + :code:``. + }, + "name": "str" # Optional. Optional team-unique + session name: 1-64 chars of letters, digits, '-', '.', '_', starting + and ending alphanumeric; case-insensitive; must not be UUID-shaped. + Omit to have the control plane generate one + ("session-:code:``-:code:``"). Unique among + the team's ACTIVE (non-terminal) sessions; destroying a session frees + its name for reuse. + }, + "spec": { + "runtime": { + "adapter": "str", # Required. Known values + are: "claude-code", "opencode", "codex", "cursor", "hermes", + "codex-agentapi", "custom", "codex-cli", "cursor-cli", + "openai-agents", "claude-agent-sdk", "crewai", and "langgraph". + "config": { + "str": {} # Optional. OPAQUE, + provider-specific runtime config. Only for adapter: + codex-agentapi today, where it is the OpenAI Agents API + create-session body (agent/environment/input) that doctl + forwards verbatim to OpenAI's POST /v1/agents/sessions; + harness-api stores and echoes it but never parses or + validates its contents (so DO stays forward-compatible as + OpenAI's schema evolves). The OpenAI session_id is passed on + the create call (the openai_session_id query parameter), not + in this block. + }, + "custom": { + "image": "str", # Required. + "entrypoint": [ + "str" # Optional. BYO + container; requires adapter: custom. image is required + and should be digest-pinned. + ] + } + }, + "defaults": { + "budget": { + "maxCostUSD": 0.0, # Optional. + Accepted, not yet enforced (returns a warning). Advisory + per-run ceilings, clamped server-side to tenant policy. + "maxSteps": 0, # Optional. Accepted, + not yet enforced (returns a warning). Advisory per-run + ceilings, clamped server-side to tenant policy. + "maxWallClockSeconds": 0, # + Optional. Accepted, not yet enforced (returns a warning). + Advisory per-run ceilings, clamped server-side to tenant + policy. + "onBreach": "str" # Optional. Known + values are: "pause", "humanInput", and "fail". + } + }, + "env": { + "str": "str" # Optional. Non-secret guest + env (debug-readable - never put secrets here; use spec.secrets). + Reserved platform keys (TEAM_ID, SESSION_ID, SANDBOX_ID, + CONTROL_PLANE_CALLBACK_URL, SANDBOX_TEMPLATE, + SANDBOX_AGENT_WORKDIR, SIZE_SLUG, SANDBOX_IMAGE_ID, + SANDBOX_OCI_REF, PLANO"" *PERMISSION*"" *) are rejected. + Credential-looking values (dop"" *v1*..., sk-..., ghp_...) + trigger a create-time warning. + }, + "memory": { + "backend": "str", # Required. + "scope": "str" # Optional. Known values are: + "tenant", "user", "agent", and "session". + }, + "mode": "str", # Optional. Explicit lifecycle + discriminator. Defaults to "interactive" (spec.serving forbidden); + "served" requires spec.serving. Known values are: "interactive" and + "served". + "model": { + "default": "str", # Optional. Accepted, not + yet enforced (returns a warning). Model routing (Plano); + catalog-validated when enforcement lands. + "profile": "str", # Optional. Accepted, not + yet enforced (returns a warning). Model routing (Plano); + catalog-validated when enforcement lands. + "routing": "str" # Optional. Accepted, not + yet enforced (returns a warning). Model routing (Plano); + catalog-validated when enforcement lands. + }, + "permissions": { + "defaultAction": "str", # Optional. + Disposition for tool calls that match no rule. Defaults to "ask" + when omitted. Known values are: "allow", "ask", and "deny". + "filesystem": { + "allowWrite": [ + "str" # Optional. Additional + writable paths beyond the workspace. Values must not + contain shell-style ${VAR}/$VAR expansions. + ], + "mode": "str" # Optional. Known + values are: "read-only" and "workspace-write". + }, + "network": { + "allow": [ + "str" # Optional. Network + boundary. Narrows (subtract-only) spec.sandbox.egress; + link-local/internal addresses stay blocked by the broader + egress policy. + ], + "default": "str" # Optional. Known + values are: "allow" and "deny". + }, + "rules": [ + { + "action": "str", # Required. + Known values are: "allow", "ask", and "deny". + "tool": "str", # Tool + taxonomy this rule matches (e.g. bash, file.read, + file.write, web.fetch, git.*, mcp). Required. + "enforcement": "str", # + Optional. What happens when the agent cannot enforce the + rule exactly. "strict" rejects the session; "best-effort" + degrades to the closest more-restrictive form and warns + (never widens). Defaults to strict for deny rules, + best-effort otherwise. Known values are: "strict" and + "best-effort". + "match": { + "str": "str" # + Optional. Optional keys narrowing the rule (e.g. + {"command": "npm run deploy"} for bash). Values are + globs and must not contain shell-style ${VAR}/$VAR + expansions. + } + } + ] + }, + "sandbox": { + "egress": { + "allow": [ + { + "host": "str" # + Required. + } + ], + "allowIps": [ + "str" # Optional. Exact + IPv4/IPv6 literals (not CIDRs). + ], + "subnetUuid": "str", # Optional. + Optional subnet within vpcUuid. + "unrestricted": bool, # Optional. + When true, opts out of the platform-default host allowlist + union. Mutually exclusive with allow / allowIps / vpcUuid / + subnetUuid. + "vpcUuid": "str" # Optional. + Customer VPC UUID. Create-time immutable. + }, + "idleTimeoutSeconds": 0, # Optional. + Inactivity suspend timeout. 0 means tenant policy default. + "maxLifetimeSeconds": 0, # Optional. Hard + wall-clock ceiling for the whole session, independent of + activity. Accepted, not yet enforced (returns a warning). + "mounts": [ + { + "accessKeyRef": "str", # + Names a declared tenantSecret slot in spec.secrets. + Required. + "bucket": "str", # Required. + "path": "str", # Absolute + guest path the mount appears at. Required. + "secretKeyRef": "str", # + Names a declared tenantSecret slot in spec.secrets. + Required. + "type": "str", # Required. + "s3fuse" + "endpoint": "str", # + Optional. Optional storage endpoint override; https:// + only. + "readOnly": bool # Optional. + Requested data mounts. Accepted, not yet enforced + (returns a warning). accessKeyRef/secretKeyRef name + declared tenantSecret slots in spec.secrets, validated at + create; declare those slots with env: false so the keys + stay mount-scoped and out of the guest env. + } + ], + "persistentWorkspace": bool, # Optional. + Canonical runnable environment spec JSON, immutable after create. + Validated non-secret spec.env and spec.permissions remain; + spec.secrets retains name/source/provider but omits ref and the + write-only value; metadata.name is cleared while safe metadata + such as labels remains. Required. + "sizeSlug": "str", # Optional. Requested + sandbox size (catalog: GET /v2/agents/sessions/sandbox/sizes). + Empty means the service default. + "template": "str" # Optional. Optional + sandbox template. Empty (or the legacy placeholder "coding") + means "no preference": the template derives from the adapter. + }, + "secrets": [ + { + "name": "str", # Required. + "source": "str", # Required. Known + values are: "oauth" and "tenantSecret". + "env": bool, # Optional. Whether the + resolved credential is injected into the guest env. Omitted + means true; false is for mount-only slots (tenantSecret + only). + "provider": "str", # Optional. + Declared credential requirements. source tenantSecret is + enforced today: value is write-only plaintext, extracted into + Secrets Manager at create and never persisted or returned. + source oauth requires provider, carries no value, and is + accepted but not yet enforced. ref is legacy and ignored. env + false keeps the resolved value out of the guest environment + (mount-only slots). + "ref": "str", # Optional. Declared + credential requirements. source tenantSecret is enforced + today: value is write-only plaintext, extracted into Secrets + Manager at create and never persisted or returned. source + oauth requires provider, carries no value, and is accepted + but not yet enforced. ref is legacy and ignored. env false + keeps the resolved value out of the guest environment + (mount-only slots). + "value": "str" # Optional. + Write-only tenantSecret plaintext; never persisted or + returned. + } + ], + "serving": { + "maxReplicas": 0, # Optional. Present only + when spec.mode is "served" (autoscaled fleet). + "minReplicas": 0, # Optional. Present only + when spec.mode is "served" (autoscaled fleet). + "scaleToZero": { + "idleSeconds": 0 # Optional. Present + only when spec.mode is "served" (autoscaled fleet). + }, + "targetConcurrency": 0 # Optional. Present + only when spec.mode is "served" (autoscaled fleet). + }, + "skills": [ + { + "description": "str", # + Load-bearing: the agent reads it to decide WHEN to load the + skill (progressive disclosure); write it as "Use when ...". + Max 1024 chars. Required. + "instructions": "str", # The + markdown body (the content below the SKILL.md frontmatter). + Max 32 KiB. Required. + "name": "str", # Unique across the + list; 1-64 chars of lowercase letters, digits, and single + hyphen separators - no consecutive hyphens, per the Agent + Skills spec (safe as a directory name at materialization). + Required. + "allowedTools": [ + "str" # Optional. Advisory + scoping of which tools the skill expects to use + (experimental in the skills standard). NOT a security + boundary - spec.permissions remains the only enforcement + surface. + ], + "license": "str", # Optional. + Optional standard passthrough (e.g. Apache-2.0). + "metadata": { + "str": "str" # Optional. + Optional string-map passthrough (e.g. author), rendered + into the frontmatter verbatim. + } + } + ], + "tools": { + "bundle": "str", # Optional. Accepted, not + yet enforced (returns a warning). Exactly one of mcpServers or + bundle. + "mcpServers": [ + { + "auth": { + "secretRef": "str", + # Tenant-vault secret name. Required. + "header": "str" # + Optional. HTTP header the credential is sent in. + Defaults to Authorization. + }, + "name": "str", # Optional. + Inline server name (required with url): 1-64 chars of + lowercase letters, digits, '-', '.', '_', starting/ending + alphanumeric. Unique across the list. Must not start with + do. (reserved for catalog refs). + "preloadTools": [ + "str" # Optional. + Catalog form only. Individual tools (optional integer + pin) advertised directly in the session MCP + tools/list next to the + action_search/action_invoke/action_code meta tools. + Each entry must appear in tools when tools is set; + toolbelt references are rejected. Every preloaded + schema is in the model's context on every turn, so + keep this to a handful of tools. Omitted means + nothing is preloaded. + ], + "ref": "str", # Optional. + DO-owned catalog reference (catalog form); do.* only, + e.g. do.actions. Mutually exclusive with name/url/auth. + "tools": [ + "str" # Optional. + Optional selection allowlist; omitted means all tools + the server advertises. Each entry is an individual + tool name with an optional integer version pin (e.g. + web_search, execute_code@3) or a toolbelt reference - + a server-defined named tool group written + toolbelt:[@:code:``] (e.g. + toolbelt:read-only@1; catalog refs only). Versions + are integer pins (no semver). The server owns belt + expansion; a pinned tool or belt attaches exactly + that version, an unpinned one resolves at session + create and is frozen for the session (no floating + latest). Entries non-empty, unique per server. The + selection bounds the session and is never preloaded + into the session MCP tools/list; see preloadTools. + ], + "url": "str" # Optional. + Inline server endpoint; https:// only. + } + ] + }, + "workspace": { + "repos": [ + "str" # Optional. GitHub OWNER/REPO + entries, unique. The first entry becomes the session's + repo_hint; the full list is the forward shape for multi-repo + sessions. + ] + } + } + }, + "name": "str", # Required. + "updated_at": "2020-02-20 00:00:00", # Required. + "credentials": [ + { + "name": "str", # Required. + "provider": "str", # Optional. Redacted credential + slot metadata (name/source/provider). Secret values are never + included. + "source": "str" # Optional. Known values are: + "tenantSecret" and "oauth". + } + ], + "insights": { + "logs": bool, # Optional. Whether the agent's logs are sent + to Insights. + "metrics": bool, # Optional. Accepted and stored for parity + with the Insights schema, but metrics are always collected: unregistered + metrics are dropped at ingest rather than opted out of here. + "traces": bool # Optional. Whether the agent's traces are + sent to Insights. + } + } + } + # response body for status code(s): 400, 404, 409, 413, 415 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop( + "content_type", _headers.pop("Content-Type", None) + ) + cls: ClsType[JSON] = kwargs.pop("cls", None) + + content_type = content_type or "application/json" + _json = None + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _json = body + + _request = build_harness_create_config_clone_request( + config_id=config_id, + content_type=content_type, + json=_json, + content=_content, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [201, 400, 404, 409, 413, 415]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + response_headers = {} + if response.status_code == 201: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 400: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 404: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 409: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 413: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 415: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore + + return cast(JSON, deserialized) # type: ignore + + @distributed_trace_async + async def list_config_sessions( + self, + config_id: str, + *, + page_size: int = 50, + page_token: Optional[str] = None, + status: Optional[str] = None, + **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """List the sessions created from one Environment Config. + + Enumerates the caller's team sessions whose session was created from this durable Environment + Config. Keyset-paginated on an opaque cursor, newest first, same shape and status semantics as + GET /v2/agents/sessions. + + :param config_id: The Environment Config UUID. Required. + :type config_id: str + :keyword page_size: Page size. Defaults to 50 when omitted or non-positive; values above 200 + are clamped to 200. Default value is 50. + :paramtype page_size: int + :keyword page_token: Opaque keyset cursor returned by the previous page. Empty starts from the + beginning. Default value is None. + :paramtype page_token: str + :keyword status: Optional filter on session status. Empty / SESSION_STATUS_UNSPECIFIED still + hides destroyed and failed sessions by default; SESSION_STATUS_ALL opts out and returns every + session regardless of status. Known values are: "SESSION_STATUS_UNSPECIFIED", + "SESSION_STATUS_PROVISIONING", "SESSION_STATUS_READY", "SESSION_STATUS_DETACHED", + "SESSION_STATUS_DESTROYING", "SESSION_STATUS_DESTROYED", "SESSION_STATUS_FAILED", + "SESSION_STATUS_PAUSED", and "SESSION_STATUS_ALL". Default value is None. + :paramtype status: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 200 + response == { + "next_page_token": "str", # Optional. Opaque cursor for the next page. Empty + when there are no further pages. + "sessions": [ + { + "agent_kind": "AGENT_KIND_UNSPECIFIED", # Optional. Default + value is "AGENT_KIND_UNSPECIFIED". AgentKind identifies what runs inside + the session sandbox. CreateSession accepts CLAUDE_CODE, OPENCODE, + CODEX_CLI, CURSOR_CLI, OPENAI_CODEX, LANGGRAPH, and CREWAI (the supported + set is overridable via HARNESS_SUPPORTED_AGENT_KINDS); other values are + rejected with 400. AGENT_KIND_OPENAI_CODEX is the OpenAI sandbox-provider + kind (DO runs codex exec-server; the agent loop lives at OpenAI). + AGENT_KIND_LANGGRAPH is the LangGraph framework adapter. + AGENT_KIND_CREWAI is the CrewAI Crew framework adapter: like LANGGRAPH + the template bakes no agent code, so spec.env must supply FRAMEWORK_REPO + and FRAMEWORK_REPO_SHA; a Crew kickoff is one-shot and exposes no HITL + surface. CREWAI is additionally gated per team and returns 400 for teams + it is not enabled for. Known values are: "AGENT_KIND_UNSPECIFIED", + "AGENT_KIND_CLAUDE_CODE", "AGENT_KIND_OPENCODE", "AGENT_KIND_CODEX_CLI", + "AGENT_KIND_CURSOR_CLI", "AGENT_KIND_NONE", "AGENT_KIND_CUSTOM", + "AGENT_KIND_HERMES", "AGENT_KIND_OPENAI_CODEX", "AGENT_KIND_LANGGRAPH", + and "AGENT_KIND_CREWAI". + "config_id": "str", # Optional. Durable Environment Config + UUID the session was created from (inline manifests persist a config too, + named after the session). Persisted and returned on create/get/list; + absent only for sessions created before config references were stored. + "created_at": "2020-02-20 00:00:00", # Optional. + "fork_from_checkpoint_id": "str", # Optional. Lineage: + harness-api checkpoint id (cp_:code:``) the child booted from at + fork time. Drives OHP inherited-history replay on attach. Omitted/empty + for root sessions and legacy forks created before this field was + persisted. + "fork_id": "str", # Optional. Lineage: branch label assigned + when this session was created by ForkSession (or rollback's internal + fork). Omitted/empty for root sessions. + "idle_timeout_seconds": 0, # Optional. This session's own + idle-pause timeout (manifest idle_timeout). Omitted when the session uses + the deployment-wide default. + "keep_warm": bool, # Optional. Session is exempt from idle + auto-pause (manifest keep_warm, or the agent-kind default: hermes + sessions keep warm). Pause requests against such a session are rejected + with 409. Omitted when false. + "last_event_at": "2020-02-20 00:00:00", # Optional. + Timestamp of the most recent event in this session's canonical stream. + Drives the inactivity policy that destroys idle sessions. + "name": "str", # Optional. User-assignable display name, + unique per team among non-terminal sessions. Usable in place of + session_id by clients (doctl / Console resolve it to the id via the + ListSessions ?name= filter; the {session_id} path stays UUID-only). + Supplied via the environment-spec metadata.name, or auto-generated + ("session-:code:``-:code:``") when omitted. + "openai_environment_id": "str", # Optional. OpenAI Agents + API environment id (env_"u2026), captured from the resolved + CODEX_ENVIRONMENT_ID guest env value. Populated only for + AGENT_KIND_OPENAI_CODEX sessions. Non-secret correlation metadata. + "openai_session_id": "str", # Optional. OpenAI Agents API + session id (sess_"u2026) this DO sandbox hosts. Populated only for + AGENT_KIND_OPENAI_CODEX sessions (empty otherwise). Non-secret + correlation metadata; a client (doctl agents attach) reads it to bridge + to the OpenAI session. See docs/design/openai-sandbox-provider.md. + "parent_session_id": "str", # Optional. Lineage: id of the + session this one was forked from. Omitted/empty for root (non-fork) + sessions. + "pause_reason": "str", # Optional. Why this session is + paused. Present only when status is SESSION_STATUS_PAUSED; omitted + otherwise. Known values: manual (PauseSession RPC), idle (inactivity + auto-pause), low_balance (prepay gate). Clients should treat an + unrecognized value as an opaque reason string. + "repo_hint": "str", # Optional. Optional repository hint + supplied at session creation (first spec.workspace.repos entry of the + manifest). Not authoritative; the agent may change repos in-session. + "resume_on_topoff": bool, # Optional. Session consents to + being resumed automatically when the team's prepayment balance is + restored after a low-balance pause. Defaults to false at create time; may + be toggled later via PATCH /v2/agents/sessions/{session_id} + (resume_on_topoff only). Only a session paused with reason low_balance + whose latest run did not complete is ever resumed automatically - a + session the customer paused themselves, or one that idled out, stays + paused regardless. Not an environment-spec field, and forks do not + inherit it. Omitted when false. + "sandbox_id": "str", # Optional. Sandbox identity for + support / debugging. Informational; omitted until a sandbox is allocated. + "session_id": "str", # Optional. + "status": "SESSION_STATUS_UNSPECIFIED", # Optional. Default + value is "SESSION_STATUS_UNSPECIFIED". SessionStatus is the lifecycle + state of the sandbox + agent pair. SESSION_STATUS_ALL is a ListSessions + status-filter value only -- no session is ever actually in this status. + SESSION_STATUS_LOCKED is reserved for internal use and never appears in + an end-user-facing response. Known values are: + "SESSION_STATUS_UNSPECIFIED", "SESSION_STATUS_PROVISIONING", + "SESSION_STATUS_READY", "SESSION_STATUS_DETACHED", + "SESSION_STATUS_DESTROYING", "SESSION_STATUS_DESTROYED", + "SESSION_STATUS_FAILED", "SESSION_STATUS_PAUSED", "SESSION_STATUS_ALL", + and "SESSION_STATUS_LOCKED". + "warnings": [ + "str" # Optional. Non-fatal advisories computed from + the manifest at create time: one entry per populated + accepted-but-not-yet-enforced section (flat names: secrets, model, + tools, memory, budget, max_lifetime, mounts; legacy manifests get the + corresponding spec.* paths), a deprecation entry when a + legacy-envelope manifest or an adapter alias (codex-cli, cursor-cli) + is used, and a heuristic entry per credential-looking spec.env value. + Populated on the CreateSession response only. + ] + } + ] + } + # response body for status code(s): 400, 404 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[JSON] = kwargs.pop("cls", None) + + _request = build_harness_list_config_sessions_request( + config_id=config_id, + page_size=page_size, + page_token=page_token, + status=status, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [200, 400, 404]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + response_headers = {} + if response.status_code == 200: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 400: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 404: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore + + return cast(JSON, deserialized) # type: ignore + + @distributed_trace_async + async def list_sessions( + self, + *, + page_size: int = 50, + page_token: Optional[str] = None, + status: str = "SESSION_STATUS_UNSPECIFIED", + name: Optional[str] = None, + search: Optional[str] = None, + **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """ListSessions enumerates sessions for the caller's tenant. + + Keyset-paginated on an opaque cursor. Results are scoped to the authenticated team. Destroyed + and failed sessions are omitted by default (matching the Cloud Console); pass + ``status=SESSION_STATUS_ALL`` to list every session regardless of status, or an explicit status + such as ``status=SESSION_STATUS_FAILED`` or ``status=SESSION_STATUS_DESTROYED`` to list only + those. + + :keyword page_size: Page size. Defaults to 50 when omitted or non-positive; values above 200 + are clamped to 200 (not rejected). Default value is 50. + :paramtype page_size: int + :keyword page_token: Opaque keyset cursor returned by the previous page. Empty starts from the + beginning. Default value is None. + :paramtype page_token: str + :keyword status: Optional filter on session status. Empty / SESSION_STATUS_UNSPECIFIED means no + explicit filter, which still hides destroyed and failed sessions by default; SESSION_STATUS_ALL + opts out and returns every session regardless of status (no session is ever actually in this + status; it only means something as a filter value here); any other explicit status overrides + the default. Known values are: "SESSION_STATUS_UNSPECIFIED", "SESSION_STATUS_PROVISIONING", + "SESSION_STATUS_READY", "SESSION_STATUS_DETACHED", "SESSION_STATUS_DESTROYING", + "SESSION_STATUS_DESTROYED", "SESSION_STATUS_FAILED", "SESSION_STATUS_PAUSED", + "SESSION_STATUS_ALL", and "SESSION_STATUS_LOCKED". Default value is + "SESSION_STATUS_UNSPECIFIED". + :paramtype status: str + :keyword name: Optional exact-name filter (case-insensitive). Returns the single live + (non-terminal) session that currently holds this name for the caller's team, or an empty list + if none does. Matches the live holder only, so historical terminal rows and reused names never + make it ambiguous (per-team live-name uniqueness bounds it to at most one). This is how clients + (doctl / Console) resolve a session name to its id; the {session_id} path stays UUID-only. + Default value is None. + :paramtype name: str + :keyword search: Optional case-insensitive substring filter on session name (contains), for + callers that don't know the exact name (e.g. a UI search box). Unlike ``name``\\ , this may + return zero, one, or many sessions, including terminal ones. Independent of ``name``\\ ; at + most 64 characters. Default value is None. + :paramtype search: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 200 + response == { + "next_page_token": "str", # Optional. Opaque cursor for the next page. Empty + when there are no further pages. + "sessions": [ + { + "agent_kind": "AGENT_KIND_UNSPECIFIED", # Optional. Default + value is "AGENT_KIND_UNSPECIFIED". AgentKind identifies what runs inside + the session sandbox. CreateSession accepts CLAUDE_CODE, OPENCODE, + CODEX_CLI, CURSOR_CLI, OPENAI_CODEX, LANGGRAPH, and CREWAI (the supported + set is overridable via HARNESS_SUPPORTED_AGENT_KINDS); other values are + rejected with 400. AGENT_KIND_OPENAI_CODEX is the OpenAI sandbox-provider + kind (DO runs codex exec-server; the agent loop lives at OpenAI). + AGENT_KIND_LANGGRAPH is the LangGraph framework adapter. + AGENT_KIND_CREWAI is the CrewAI Crew framework adapter: like LANGGRAPH + the template bakes no agent code, so spec.env must supply FRAMEWORK_REPO + and FRAMEWORK_REPO_SHA; a Crew kickoff is one-shot and exposes no HITL + surface. CREWAI is additionally gated per team and returns 400 for teams + it is not enabled for. Known values are: "AGENT_KIND_UNSPECIFIED", + "AGENT_KIND_CLAUDE_CODE", "AGENT_KIND_OPENCODE", "AGENT_KIND_CODEX_CLI", + "AGENT_KIND_CURSOR_CLI", "AGENT_KIND_NONE", "AGENT_KIND_CUSTOM", + "AGENT_KIND_HERMES", "AGENT_KIND_OPENAI_CODEX", "AGENT_KIND_LANGGRAPH", + and "AGENT_KIND_CREWAI". + "config_id": "str", # Optional. Durable Environment Config + UUID the session was created from (inline manifests persist a config too, + named after the session). Persisted and returned on create/get/list; + absent only for sessions created before config references were stored. + "created_at": "2020-02-20 00:00:00", # Optional. + "fork_from_checkpoint_id": "str", # Optional. Lineage: + harness-api checkpoint id (cp_:code:``) the child booted from at + fork time. Drives OHP inherited-history replay on attach. Omitted/empty + for root sessions and legacy forks created before this field was + persisted. + "fork_id": "str", # Optional. Lineage: branch label assigned + when this session was created by ForkSession (or rollback's internal + fork). Omitted/empty for root sessions. + "idle_timeout_seconds": 0, # Optional. This session's own + idle-pause timeout (manifest idle_timeout). Omitted when the session uses + the deployment-wide default. + "keep_warm": bool, # Optional. Session is exempt from idle + auto-pause (manifest keep_warm, or the agent-kind default: hermes + sessions keep warm). Pause requests against such a session are rejected + with 409. Omitted when false. + "last_event_at": "2020-02-20 00:00:00", # Optional. + Timestamp of the most recent event in this session's canonical stream. + Drives the inactivity policy that destroys idle sessions. + "name": "str", # Optional. User-assignable display name, + unique per team among non-terminal sessions. Usable in place of + session_id by clients (doctl / Console resolve it to the id via the + ListSessions ?name= filter; the {session_id} path stays UUID-only). + Supplied via the environment-spec metadata.name, or auto-generated + ("session-:code:``-:code:``") when omitted. + "openai_environment_id": "str", # Optional. OpenAI Agents + API environment id (env_"u2026), captured from the resolved + CODEX_ENVIRONMENT_ID guest env value. Populated only for + AGENT_KIND_OPENAI_CODEX sessions. Non-secret correlation metadata. + "openai_session_id": "str", # Optional. OpenAI Agents API + session id (sess_"u2026) this DO sandbox hosts. Populated only for + AGENT_KIND_OPENAI_CODEX sessions (empty otherwise). Non-secret + correlation metadata; a client (doctl agents attach) reads it to bridge + to the OpenAI session. See docs/design/openai-sandbox-provider.md. + "parent_session_id": "str", # Optional. Lineage: id of the + session this one was forked from. Omitted/empty for root (non-fork) + sessions. + "pause_reason": "str", # Optional. Why this session is + paused. Present only when status is SESSION_STATUS_PAUSED; omitted + otherwise. Known values: manual (PauseSession RPC), idle (inactivity + auto-pause), low_balance (prepay gate). Clients should treat an + unrecognized value as an opaque reason string. + "repo_hint": "str", # Optional. Optional repository hint + supplied at session creation (first spec.workspace.repos entry of the + manifest). Not authoritative; the agent may change repos in-session. + "resume_on_topoff": bool, # Optional. Session consents to + being resumed automatically when the team's prepayment balance is + restored after a low-balance pause. Defaults to false at create time; may + be toggled later via PATCH /v2/agents/sessions/{session_id} + (resume_on_topoff only). Only a session paused with reason low_balance + whose latest run did not complete is ever resumed automatically - a + session the customer paused themselves, or one that idled out, stays + paused regardless. Not an environment-spec field, and forks do not + inherit it. Omitted when false. + "sandbox_id": "str", # Optional. Sandbox identity for + support / debugging. Informational; omitted until a sandbox is allocated. + "session_id": "str", # Optional. + "status": "SESSION_STATUS_UNSPECIFIED", # Optional. Default + value is "SESSION_STATUS_UNSPECIFIED". SessionStatus is the lifecycle + state of the sandbox + agent pair. SESSION_STATUS_ALL is a ListSessions + status-filter value only -- no session is ever actually in this status. + SESSION_STATUS_LOCKED is reserved for internal use and never appears in + an end-user-facing response. Known values are: + "SESSION_STATUS_UNSPECIFIED", "SESSION_STATUS_PROVISIONING", + "SESSION_STATUS_READY", "SESSION_STATUS_DETACHED", + "SESSION_STATUS_DESTROYING", "SESSION_STATUS_DESTROYED", + "SESSION_STATUS_FAILED", "SESSION_STATUS_PAUSED", "SESSION_STATUS_ALL", + and "SESSION_STATUS_LOCKED". + "warnings": [ + "str" # Optional. Non-fatal advisories computed from + the manifest at create time: one entry per populated + accepted-but-not-yet-enforced section (flat names: secrets, model, + tools, memory, budget, max_lifetime, mounts; legacy manifests get the + corresponding spec.* paths), a deprecation entry when a + legacy-envelope manifest or an adapter alias (codex-cli, cursor-cli) + is used, and a heuristic entry per credential-looking spec.env value. + Populated on the CreateSession response only. + ] + } + ] + } + # response body for status code(s): 400, 404 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[JSON] = kwargs.pop("cls", None) + + _request = build_harness_list_sessions_request( + page_size=page_size, + page_token=page_token, + status=status, + name=name, + search=search, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [200, 400, 404]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + response_headers = {} + if response.status_code == 200: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 400: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 404: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore + + return cast(JSON, deserialized) # type: ignore + + @overload + async def create_session( + self, + body: JSON, + *, + x_device_uuid: Optional[str] = None, + openai_session_id: Optional[str] = None, + resume_on_topoff: Optional[bool] = None, + content_type: str = "application/json", + **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """CreateSession provisions a sandbox and starts a Harness Runtime session. + + Returns once the session row is allocated. Sandbox boot completes asynchronously. + + Two mutually exclusive request forms are selected by Content-Type (same contract as + harness-api ``harness.swagger.json``\\ ): + + + * YAML (\\ ``application/x-yaml``\\ , ``application/yaml``\\ , ``text/yaml``\\ , + ``text/x-yaml``\\ ): the environment + spec. Two environment-spec formats are accepted during the dual-format transition: the flat + format (primary) and the legacy ``agents.digitalocean.com/v1alpha1`` envelope (deprecated). A + top-level ``apiVersion`` key selects the legacy parser; its absence selects flat. Internally + persists a durable Environment Config, then creates the session. + * JSON (\\ ``application/json``\\ ): ``{name, config_id}`` loads the config, resolves its + credentials from + Secrets Manager at the version pinned when the config was created, and creates the session. + + The OpenAPI ``requestBody`` schemas the JSON form for client generation (Autorest cannot emit a + multi–content-type body). The live API still accepts the YAML forms listed above. + + Successful responses may include ``config_id`` on the session. ``X-Device-UUID`` (if present) + is + persisted on the session row. Query parameters ``openai_session_id`` and ``resume_on_topoff`` + apply to + both forms; when both the JSON body field and ``?resume_on_topoff=`` are present, the query + parameter wins. + + :param body: CreateSessionConfigRequest with ``application/json``. The live API also accepts a + raw environment + spec under YAML Content-Types (see operation description); those forms are omitted from + ``content`` + so generated clients (pydo/autorest) can emit this operation. Required. + :type body: JSON + :keyword x_device_uuid: Optional hardware device id forwarded by doctl. Persisted on the + session row for lifecycle attribution. Values over 128 bytes are ignored. Default value is + None. + :paramtype x_device_uuid: str + :keyword openai_session_id: OpenAI Agents API session id (sess_…) for adapter: codex-agentapi. + doctl creates the OpenAI session before calling DO and passes the resulting id here; + harness-api persists it for correlation. Empty/omitted for every other agent kind. Default + value is None. + :paramtype openai_session_id: str + :keyword resume_on_topoff: Opt the new session in to automatic resumption when the team's + prepayment balance is restored. Defaults to false. Exists as a query parameter so the + environment spec (YAML body) form can carry it too. On the application/json form the body field + is idiomatic, but this parameter wins when both are present. Must be exactly one of + true/false/1/0; anything else is a 400. Default value is None. + :paramtype resume_on_topoff: bool + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # JSON input template you can fill out and use as your body input. + body = { + "config_id": "str", # Required. + "name": "str", # Session name, independent of the config name. Must satisfy + the existing environment-spec metadata.name rules and must not be UUID-shaped. + Required. + "resume_on_topoff": bool # Optional. Opt the new session in to automatic + resumption when the team's prepayment balance is restored. Defaults to false. + Session-scoped, not config-scoped: the referenced config never confers this, so + one shared config cannot enrol every session created from it. Omit to leave the + default (or to let the ?resume_on_topoff= query parameter supply it). + } + + # response body for status code(s): 200 + response == { + "session": { + "agent_kind": "AGENT_KIND_UNSPECIFIED", # Optional. Default value is + "AGENT_KIND_UNSPECIFIED". AgentKind identifies what runs inside the session + sandbox. CreateSession accepts CLAUDE_CODE, OPENCODE, CODEX_CLI, CURSOR_CLI, + OPENAI_CODEX, LANGGRAPH, and CREWAI (the supported set is overridable via + HARNESS_SUPPORTED_AGENT_KINDS); other values are rejected with 400. + AGENT_KIND_OPENAI_CODEX is the OpenAI sandbox-provider kind (DO runs codex + exec-server; the agent loop lives at OpenAI). AGENT_KIND_LANGGRAPH is the + LangGraph framework adapter. AGENT_KIND_CREWAI is the CrewAI Crew framework + adapter: like LANGGRAPH the template bakes no agent code, so spec.env must + supply FRAMEWORK_REPO and FRAMEWORK_REPO_SHA; a Crew kickoff is one-shot and + exposes no HITL surface. CREWAI is additionally gated per team and returns + 400 for teams it is not enabled for. Known values are: + "AGENT_KIND_UNSPECIFIED", "AGENT_KIND_CLAUDE_CODE", "AGENT_KIND_OPENCODE", + "AGENT_KIND_CODEX_CLI", "AGENT_KIND_CURSOR_CLI", "AGENT_KIND_NONE", + "AGENT_KIND_CUSTOM", "AGENT_KIND_HERMES", "AGENT_KIND_OPENAI_CODEX", + "AGENT_KIND_LANGGRAPH", and "AGENT_KIND_CREWAI". + "config_id": "str", # Optional. Durable Environment Config UUID the + session was created from (inline manifests persist a config too, named after + the session). Persisted and returned on create/get/list; absent only for + sessions created before config references were stored. + "created_at": "2020-02-20 00:00:00", # Optional. Session is the + user-facing record of a environment session + sandbox pair. + "fork_from_checkpoint_id": "str", # Optional. Lineage: harness-api + checkpoint id (cp_:code:``) the child booted from at fork time. Drives + OHP inherited-history replay on attach. Omitted/empty for root sessions and + legacy forks created before this field was persisted. + "fork_id": "str", # Optional. Lineage: branch label assigned when + this session was created by ForkSession (or rollback's internal fork). + Omitted/empty for root sessions. + "idle_timeout_seconds": 0, # Optional. This session's own idle-pause + timeout (manifest idle_timeout). Omitted when the session uses the + deployment-wide default. + "keep_warm": bool, # Optional. Session is exempt from idle + auto-pause (manifest keep_warm, or the agent-kind default: hermes sessions + keep warm). Pause requests against such a session are rejected with 409. + Omitted when false. + "last_event_at": "2020-02-20 00:00:00", # Optional. Timestamp of the + most recent event in this session's canonical stream. Drives the inactivity + policy that destroys idle sessions. + "name": "str", # Optional. User-assignable display name, unique per + team among non-terminal sessions. Usable in place of session_id by clients + (doctl / Console resolve it to the id via the ListSessions ?name= filter; the + {session_id} path stays UUID-only). Supplied via the environment-spec + metadata.name, or auto-generated + ("session-:code:``-:code:``") when omitted. + "openai_environment_id": "str", # Optional. OpenAI Agents API + environment id (env_"u2026), captured from the resolved CODEX_ENVIRONMENT_ID + guest env value. Populated only for AGENT_KIND_OPENAI_CODEX sessions. + Non-secret correlation metadata. + "openai_session_id": "str", # Optional. OpenAI Agents API session id + (sess_"u2026) this DO sandbox hosts. Populated only for + AGENT_KIND_OPENAI_CODEX sessions (empty otherwise). Non-secret correlation + metadata; a client (doctl agents attach) reads it to bridge to the OpenAI + session. See docs/design/openai-sandbox-provider.md. + "parent_session_id": "str", # Optional. Lineage: id of the session + this one was forked from. Omitted/empty for root (non-fork) sessions. + "pause_reason": "str", # Optional. Why this session is paused. + Present only when status is SESSION_STATUS_PAUSED; omitted otherwise. Known + values: manual (PauseSession RPC), idle (inactivity auto-pause), low_balance + (prepay gate). Clients should treat an unrecognized value as an opaque reason + string. + "repo_hint": "str", # Optional. Optional repository hint supplied at + session creation (first spec.workspace.repos entry of the manifest). Not + authoritative; the agent may change repos in-session. + "resume_on_topoff": bool, # Optional. Session consents to being + resumed automatically when the team's prepayment balance is restored after a + low-balance pause. Defaults to false at create time; may be toggled later via + PATCH /v2/agents/sessions/{session_id} (resume_on_topoff only). Only a + session paused with reason low_balance whose latest run did not complete is + ever resumed automatically - a session the customer paused themselves, or one + that idled out, stays paused regardless. Not an environment-spec field, and + forks do not inherit it. Omitted when false. + "sandbox_id": "str", # Optional. Sandbox identity for support / + debugging. Informational; omitted until a sandbox is allocated. + "session_id": "str", # Optional. Session is the user-facing record + of a environment session + sandbox pair. + "status": "SESSION_STATUS_UNSPECIFIED", # Optional. Default value is + "SESSION_STATUS_UNSPECIFIED". SessionStatus is the lifecycle state of the + sandbox + agent pair. SESSION_STATUS_ALL is a ListSessions status-filter + value only -- no session is ever actually in this status. + SESSION_STATUS_LOCKED is reserved for internal use and never appears in an + end-user-facing response. Known values are: "SESSION_STATUS_UNSPECIFIED", + "SESSION_STATUS_PROVISIONING", "SESSION_STATUS_READY", + "SESSION_STATUS_DETACHED", "SESSION_STATUS_DESTROYING", + "SESSION_STATUS_DESTROYED", "SESSION_STATUS_FAILED", "SESSION_STATUS_PAUSED", + "SESSION_STATUS_ALL", and "SESSION_STATUS_LOCKED". + "warnings": [ + "str" # Optional. Non-fatal advisories computed from the + manifest at create time: one entry per populated + accepted-but-not-yet-enforced section (flat names: secrets, model, tools, + memory, budget, max_lifetime, mounts; legacy manifests get the + corresponding spec.* paths), a deprecation entry when a legacy-envelope + manifest or an adapter alias (codex-cli, cursor-cli) is used, and a + heuristic entry per credential-looking spec.env value. Populated on the + CreateSession response only. + ] + } + } + # response body for status code(s): 400, 402, 403, 404, 409, 413, 415, 501, 503 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + + @overload + async def create_session( + self, + body: IO[bytes], + *, + x_device_uuid: Optional[str] = None, + openai_session_id: Optional[str] = None, + resume_on_topoff: Optional[bool] = None, + content_type: str = "application/json", + **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """CreateSession provisions a sandbox and starts a Harness Runtime session. + + Returns once the session row is allocated. Sandbox boot completes asynchronously. + + Two mutually exclusive request forms are selected by Content-Type (same contract as + harness-api ``harness.swagger.json``\\ ): + + + * YAML (\\ ``application/x-yaml``\\ , ``application/yaml``\\ , ``text/yaml``\\ , + ``text/x-yaml``\\ ): the environment + spec. Two environment-spec formats are accepted during the dual-format transition: the flat + format (primary) and the legacy ``agents.digitalocean.com/v1alpha1`` envelope (deprecated). A + top-level ``apiVersion`` key selects the legacy parser; its absence selects flat. Internally + persists a durable Environment Config, then creates the session. + * JSON (\\ ``application/json``\\ ): ``{name, config_id}`` loads the config, resolves its + credentials from + Secrets Manager at the version pinned when the config was created, and creates the session. + + The OpenAPI ``requestBody`` schemas the JSON form for client generation (Autorest cannot emit a + multi–content-type body). The live API still accepts the YAML forms listed above. + + Successful responses may include ``config_id`` on the session. ``X-Device-UUID`` (if present) + is + persisted on the session row. Query parameters ``openai_session_id`` and ``resume_on_topoff`` + apply to + both forms; when both the JSON body field and ``?resume_on_topoff=`` are present, the query + parameter wins. + + :param body: CreateSessionConfigRequest with ``application/json``. The live API also accepts a + raw environment + spec under YAML Content-Types (see operation description); those forms are omitted from + ``content`` + so generated clients (pydo/autorest) can emit this operation. Required. + :type body: IO[bytes] + :keyword x_device_uuid: Optional hardware device id forwarded by doctl. Persisted on the + session row for lifecycle attribution. Values over 128 bytes are ignored. Default value is + None. + :paramtype x_device_uuid: str + :keyword openai_session_id: OpenAI Agents API session id (sess_…) for adapter: codex-agentapi. + doctl creates the OpenAI session before calling DO and passes the resulting id here; + harness-api persists it for correlation. Empty/omitted for every other agent kind. Default + value is None. + :paramtype openai_session_id: str + :keyword resume_on_topoff: Opt the new session in to automatic resumption when the team's + prepayment balance is restored. Defaults to false. Exists as a query parameter so the + environment spec (YAML body) form can carry it too. On the application/json form the body field + is idiomatic, but this parameter wins when both are present. Must be exactly one of + true/false/1/0; anything else is a 400. Default value is None. + :paramtype resume_on_topoff: bool + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 200 + response == { + "session": { + "agent_kind": "AGENT_KIND_UNSPECIFIED", # Optional. Default value is + "AGENT_KIND_UNSPECIFIED". AgentKind identifies what runs inside the session + sandbox. CreateSession accepts CLAUDE_CODE, OPENCODE, CODEX_CLI, CURSOR_CLI, + OPENAI_CODEX, LANGGRAPH, and CREWAI (the supported set is overridable via + HARNESS_SUPPORTED_AGENT_KINDS); other values are rejected with 400. + AGENT_KIND_OPENAI_CODEX is the OpenAI sandbox-provider kind (DO runs codex + exec-server; the agent loop lives at OpenAI). AGENT_KIND_LANGGRAPH is the + LangGraph framework adapter. AGENT_KIND_CREWAI is the CrewAI Crew framework + adapter: like LANGGRAPH the template bakes no agent code, so spec.env must + supply FRAMEWORK_REPO and FRAMEWORK_REPO_SHA; a Crew kickoff is one-shot and + exposes no HITL surface. CREWAI is additionally gated per team and returns + 400 for teams it is not enabled for. Known values are: + "AGENT_KIND_UNSPECIFIED", "AGENT_KIND_CLAUDE_CODE", "AGENT_KIND_OPENCODE", + "AGENT_KIND_CODEX_CLI", "AGENT_KIND_CURSOR_CLI", "AGENT_KIND_NONE", + "AGENT_KIND_CUSTOM", "AGENT_KIND_HERMES", "AGENT_KIND_OPENAI_CODEX", + "AGENT_KIND_LANGGRAPH", and "AGENT_KIND_CREWAI". + "config_id": "str", # Optional. Durable Environment Config UUID the + session was created from (inline manifests persist a config too, named after + the session). Persisted and returned on create/get/list; absent only for + sessions created before config references were stored. + "created_at": "2020-02-20 00:00:00", # Optional. Session is the + user-facing record of a environment session + sandbox pair. + "fork_from_checkpoint_id": "str", # Optional. Lineage: harness-api + checkpoint id (cp_:code:``) the child booted from at fork time. Drives + OHP inherited-history replay on attach. Omitted/empty for root sessions and + legacy forks created before this field was persisted. + "fork_id": "str", # Optional. Lineage: branch label assigned when + this session was created by ForkSession (or rollback's internal fork). + Omitted/empty for root sessions. + "idle_timeout_seconds": 0, # Optional. This session's own idle-pause + timeout (manifest idle_timeout). Omitted when the session uses the + deployment-wide default. + "keep_warm": bool, # Optional. Session is exempt from idle + auto-pause (manifest keep_warm, or the agent-kind default: hermes sessions + keep warm). Pause requests against such a session are rejected with 409. + Omitted when false. + "last_event_at": "2020-02-20 00:00:00", # Optional. Timestamp of the + most recent event in this session's canonical stream. Drives the inactivity + policy that destroys idle sessions. + "name": "str", # Optional. User-assignable display name, unique per + team among non-terminal sessions. Usable in place of session_id by clients + (doctl / Console resolve it to the id via the ListSessions ?name= filter; the + {session_id} path stays UUID-only). Supplied via the environment-spec + metadata.name, or auto-generated + ("session-:code:``-:code:``") when omitted. + "openai_environment_id": "str", # Optional. OpenAI Agents API + environment id (env_"u2026), captured from the resolved CODEX_ENVIRONMENT_ID + guest env value. Populated only for AGENT_KIND_OPENAI_CODEX sessions. + Non-secret correlation metadata. + "openai_session_id": "str", # Optional. OpenAI Agents API session id + (sess_"u2026) this DO sandbox hosts. Populated only for + AGENT_KIND_OPENAI_CODEX sessions (empty otherwise). Non-secret correlation + metadata; a client (doctl agents attach) reads it to bridge to the OpenAI + session. See docs/design/openai-sandbox-provider.md. + "parent_session_id": "str", # Optional. Lineage: id of the session + this one was forked from. Omitted/empty for root (non-fork) sessions. + "pause_reason": "str", # Optional. Why this session is paused. + Present only when status is SESSION_STATUS_PAUSED; omitted otherwise. Known + values: manual (PauseSession RPC), idle (inactivity auto-pause), low_balance + (prepay gate). Clients should treat an unrecognized value as an opaque reason + string. + "repo_hint": "str", # Optional. Optional repository hint supplied at + session creation (first spec.workspace.repos entry of the manifest). Not + authoritative; the agent may change repos in-session. + "resume_on_topoff": bool, # Optional. Session consents to being + resumed automatically when the team's prepayment balance is restored after a + low-balance pause. Defaults to false at create time; may be toggled later via + PATCH /v2/agents/sessions/{session_id} (resume_on_topoff only). Only a + session paused with reason low_balance whose latest run did not complete is + ever resumed automatically - a session the customer paused themselves, or one + that idled out, stays paused regardless. Not an environment-spec field, and + forks do not inherit it. Omitted when false. + "sandbox_id": "str", # Optional. Sandbox identity for support / + debugging. Informational; omitted until a sandbox is allocated. + "session_id": "str", # Optional. Session is the user-facing record + of a environment session + sandbox pair. + "status": "SESSION_STATUS_UNSPECIFIED", # Optional. Default value is + "SESSION_STATUS_UNSPECIFIED". SessionStatus is the lifecycle state of the + sandbox + agent pair. SESSION_STATUS_ALL is a ListSessions status-filter + value only -- no session is ever actually in this status. + SESSION_STATUS_LOCKED is reserved for internal use and never appears in an + end-user-facing response. Known values are: "SESSION_STATUS_UNSPECIFIED", + "SESSION_STATUS_PROVISIONING", "SESSION_STATUS_READY", + "SESSION_STATUS_DETACHED", "SESSION_STATUS_DESTROYING", + "SESSION_STATUS_DESTROYED", "SESSION_STATUS_FAILED", "SESSION_STATUS_PAUSED", + "SESSION_STATUS_ALL", and "SESSION_STATUS_LOCKED". + "warnings": [ + "str" # Optional. Non-fatal advisories computed from the + manifest at create time: one entry per populated + accepted-but-not-yet-enforced section (flat names: secrets, model, tools, + memory, budget, max_lifetime, mounts; legacy manifests get the + corresponding spec.* paths), a deprecation entry when a legacy-envelope + manifest or an adapter alias (codex-cli, cursor-cli) is used, and a + heuristic entry per credential-looking spec.env value. Populated on the + CreateSession response only. + ] + } + } + # response body for status code(s): 400, 402, 403, 404, 409, 413, 415, 501, 503 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + + @distributed_trace_async + async def create_session( + self, + body: Union[JSON, IO[bytes]], + *, + x_device_uuid: Optional[str] = None, + openai_session_id: Optional[str] = None, + resume_on_topoff: Optional[bool] = None, + **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """CreateSession provisions a sandbox and starts a Harness Runtime session. + + Returns once the session row is allocated. Sandbox boot completes asynchronously. + + Two mutually exclusive request forms are selected by Content-Type (same contract as + harness-api ``harness.swagger.json``\\ ): + + + * YAML (\\ ``application/x-yaml``\\ , ``application/yaml``\\ , ``text/yaml``\\ , + ``text/x-yaml``\\ ): the environment + spec. Two environment-spec formats are accepted during the dual-format transition: the flat + format (primary) and the legacy ``agents.digitalocean.com/v1alpha1`` envelope (deprecated). A + top-level ``apiVersion`` key selects the legacy parser; its absence selects flat. Internally + persists a durable Environment Config, then creates the session. + * JSON (\\ ``application/json``\\ ): ``{name, config_id}`` loads the config, resolves its + credentials from + Secrets Manager at the version pinned when the config was created, and creates the session. + + The OpenAPI ``requestBody`` schemas the JSON form for client generation (Autorest cannot emit a + multi–content-type body). The live API still accepts the YAML forms listed above. + + Successful responses may include ``config_id`` on the session. ``X-Device-UUID`` (if present) + is + persisted on the session row. Query parameters ``openai_session_id`` and ``resume_on_topoff`` + apply to + both forms; when both the JSON body field and ``?resume_on_topoff=`` are present, the query + parameter wins. + + :param body: CreateSessionConfigRequest with ``application/json``. The live API also accepts a + raw environment + spec under YAML Content-Types (see operation description); those forms are omitted from + ``content`` + so generated clients (pydo/autorest) can emit this operation. Is either a JSON type or a + IO[bytes] type. Required. + :type body: JSON or IO[bytes] + :keyword x_device_uuid: Optional hardware device id forwarded by doctl. Persisted on the + session row for lifecycle attribution. Values over 128 bytes are ignored. Default value is + None. + :paramtype x_device_uuid: str + :keyword openai_session_id: OpenAI Agents API session id (sess_…) for adapter: codex-agentapi. + doctl creates the OpenAI session before calling DO and passes the resulting id here; + harness-api persists it for correlation. Empty/omitted for every other agent kind. Default + value is None. + :paramtype openai_session_id: str + :keyword resume_on_topoff: Opt the new session in to automatic resumption when the team's + prepayment balance is restored. Defaults to false. Exists as a query parameter so the + environment spec (YAML body) form can carry it too. On the application/json form the body field + is idiomatic, but this parameter wins when both are present. Must be exactly one of + true/false/1/0; anything else is a 400. Default value is None. + :paramtype resume_on_topoff: bool + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # JSON input template you can fill out and use as your body input. + body = { + "config_id": "str", # Required. + "name": "str", # Session name, independent of the config name. Must satisfy + the existing environment-spec metadata.name rules and must not be UUID-shaped. + Required. + "resume_on_topoff": bool # Optional. Opt the new session in to automatic + resumption when the team's prepayment balance is restored. Defaults to false. + Session-scoped, not config-scoped: the referenced config never confers this, so + one shared config cannot enrol every session created from it. Omit to leave the + default (or to let the ?resume_on_topoff= query parameter supply it). + } + + # response body for status code(s): 200 + response == { + "session": { + "agent_kind": "AGENT_KIND_UNSPECIFIED", # Optional. Default value is + "AGENT_KIND_UNSPECIFIED". AgentKind identifies what runs inside the session + sandbox. CreateSession accepts CLAUDE_CODE, OPENCODE, CODEX_CLI, CURSOR_CLI, + OPENAI_CODEX, LANGGRAPH, and CREWAI (the supported set is overridable via + HARNESS_SUPPORTED_AGENT_KINDS); other values are rejected with 400. + AGENT_KIND_OPENAI_CODEX is the OpenAI sandbox-provider kind (DO runs codex + exec-server; the agent loop lives at OpenAI). AGENT_KIND_LANGGRAPH is the + LangGraph framework adapter. AGENT_KIND_CREWAI is the CrewAI Crew framework + adapter: like LANGGRAPH the template bakes no agent code, so spec.env must + supply FRAMEWORK_REPO and FRAMEWORK_REPO_SHA; a Crew kickoff is one-shot and + exposes no HITL surface. CREWAI is additionally gated per team and returns + 400 for teams it is not enabled for. Known values are: + "AGENT_KIND_UNSPECIFIED", "AGENT_KIND_CLAUDE_CODE", "AGENT_KIND_OPENCODE", + "AGENT_KIND_CODEX_CLI", "AGENT_KIND_CURSOR_CLI", "AGENT_KIND_NONE", + "AGENT_KIND_CUSTOM", "AGENT_KIND_HERMES", "AGENT_KIND_OPENAI_CODEX", + "AGENT_KIND_LANGGRAPH", and "AGENT_KIND_CREWAI". + "config_id": "str", # Optional. Durable Environment Config UUID the + session was created from (inline manifests persist a config too, named after + the session). Persisted and returned on create/get/list; absent only for + sessions created before config references were stored. + "created_at": "2020-02-20 00:00:00", # Optional. Session is the + user-facing record of a environment session + sandbox pair. + "fork_from_checkpoint_id": "str", # Optional. Lineage: harness-api + checkpoint id (cp_:code:``) the child booted from at fork time. Drives + OHP inherited-history replay on attach. Omitted/empty for root sessions and + legacy forks created before this field was persisted. + "fork_id": "str", # Optional. Lineage: branch label assigned when + this session was created by ForkSession (or rollback's internal fork). + Omitted/empty for root sessions. + "idle_timeout_seconds": 0, # Optional. This session's own idle-pause + timeout (manifest idle_timeout). Omitted when the session uses the + deployment-wide default. + "keep_warm": bool, # Optional. Session is exempt from idle + auto-pause (manifest keep_warm, or the agent-kind default: hermes sessions + keep warm). Pause requests against such a session are rejected with 409. + Omitted when false. + "last_event_at": "2020-02-20 00:00:00", # Optional. Timestamp of the + most recent event in this session's canonical stream. Drives the inactivity + policy that destroys idle sessions. + "name": "str", # Optional. User-assignable display name, unique per + team among non-terminal sessions. Usable in place of session_id by clients + (doctl / Console resolve it to the id via the ListSessions ?name= filter; the + {session_id} path stays UUID-only). Supplied via the environment-spec + metadata.name, or auto-generated + ("session-:code:``-:code:``") when omitted. + "openai_environment_id": "str", # Optional. OpenAI Agents API + environment id (env_"u2026), captured from the resolved CODEX_ENVIRONMENT_ID + guest env value. Populated only for AGENT_KIND_OPENAI_CODEX sessions. + Non-secret correlation metadata. + "openai_session_id": "str", # Optional. OpenAI Agents API session id + (sess_"u2026) this DO sandbox hosts. Populated only for + AGENT_KIND_OPENAI_CODEX sessions (empty otherwise). Non-secret correlation + metadata; a client (doctl agents attach) reads it to bridge to the OpenAI + session. See docs/design/openai-sandbox-provider.md. + "parent_session_id": "str", # Optional. Lineage: id of the session + this one was forked from. Omitted/empty for root (non-fork) sessions. + "pause_reason": "str", # Optional. Why this session is paused. + Present only when status is SESSION_STATUS_PAUSED; omitted otherwise. Known + values: manual (PauseSession RPC), idle (inactivity auto-pause), low_balance + (prepay gate). Clients should treat an unrecognized value as an opaque reason + string. + "repo_hint": "str", # Optional. Optional repository hint supplied at + session creation (first spec.workspace.repos entry of the manifest). Not + authoritative; the agent may change repos in-session. + "resume_on_topoff": bool, # Optional. Session consents to being + resumed automatically when the team's prepayment balance is restored after a + low-balance pause. Defaults to false at create time; may be toggled later via + PATCH /v2/agents/sessions/{session_id} (resume_on_topoff only). Only a + session paused with reason low_balance whose latest run did not complete is + ever resumed automatically - a session the customer paused themselves, or one + that idled out, stays paused regardless. Not an environment-spec field, and + forks do not inherit it. Omitted when false. + "sandbox_id": "str", # Optional. Sandbox identity for support / + debugging. Informational; omitted until a sandbox is allocated. + "session_id": "str", # Optional. Session is the user-facing record + of a environment session + sandbox pair. + "status": "SESSION_STATUS_UNSPECIFIED", # Optional. Default value is + "SESSION_STATUS_UNSPECIFIED". SessionStatus is the lifecycle state of the + sandbox + agent pair. SESSION_STATUS_ALL is a ListSessions status-filter + value only -- no session is ever actually in this status. + SESSION_STATUS_LOCKED is reserved for internal use and never appears in an + end-user-facing response. Known values are: "SESSION_STATUS_UNSPECIFIED", + "SESSION_STATUS_PROVISIONING", "SESSION_STATUS_READY", + "SESSION_STATUS_DETACHED", "SESSION_STATUS_DESTROYING", + "SESSION_STATUS_DESTROYED", "SESSION_STATUS_FAILED", "SESSION_STATUS_PAUSED", + "SESSION_STATUS_ALL", and "SESSION_STATUS_LOCKED". + "warnings": [ + "str" # Optional. Non-fatal advisories computed from the + manifest at create time: one entry per populated + accepted-but-not-yet-enforced section (flat names: secrets, model, tools, + memory, budget, max_lifetime, mounts; legacy manifests get the + corresponding spec.* paths), a deprecation entry when a legacy-envelope + manifest or an adapter alias (codex-cli, cursor-cli) is used, and a + heuristic entry per credential-looking spec.env value. Populated on the + CreateSession response only. + ] + } + } + # response body for status code(s): 400, 402, 403, 404, 409, 413, 415, 501, 503 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop( + "content_type", _headers.pop("Content-Type", None) + ) + cls: ClsType[JSON] = kwargs.pop("cls", None) + + content_type = content_type or "application/json" + _json = None + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _json = body + + _request = build_harness_create_session_request( + x_device_uuid=x_device_uuid, + openai_session_id=openai_session_id, + resume_on_topoff=resume_on_topoff, + content_type=content_type, + json=_json, + content=_content, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [ + 200, + 400, + 402, + 403, + 404, + 409, + 413, + 415, + 501, + 503, + ]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + response_headers = {} + if response.status_code == 200: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 400: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 402: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 403: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 404: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 409: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 413: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 415: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 501: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 503: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore + + return cast(JSON, deserialized) # type: ignore + + @distributed_trace_async + async def validate_session_policy(self, data: IO[bytes], **kwargs: Any) -> JSON: + # pylint: disable=line-too-long + """ValidatePolicy checks a manifest's tool-permission policy against the agent's capabilities + without creating a session. + + Stateless preflight: parses the same environment spec as CreateSession and runs the identical + resolve + validate pipeline against the capability descriptor stamped on the selected + template's image (resolved per-request via the sandbox, not a separate registry). Returns + per-rule verdicts. Nothing is provisioned or persisted. + + Each rule is validated against the descriptor for the (agent, version) the template resolves to + and gets a verdict: ``exact``\\ , ``degraded``\\ , ``unsupported``\\ , or ``unverified``. + ``ok`` is false when any strict rule is unsupported or unverified — i.e. CreateSession would + reject the policy for that agent. An empty ``spec.permissions`` returns ``ok: true`` with no + verdicts. Because it shares the create path's descriptor lookup, the verdicts match what + CreateSession would compute. + + :param data: The same environment spec you would submit to CreateSession (YAML). Only + agent/template selection + and the permissions block are used. The live API also accepts ``application/yaml``\\ , + ``text/yaml``\\ , and + ``text/x-yaml``\\ ; OpenAPI ``content`` lists a single media type so generated clients can + emit this + operation. Required. + :type data: IO[bytes] + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 200 + response == { + "agent": "str", # Optional. + "defaultAction": "str", # Optional. Catch-all disposition AS SHIPPED (after + unverified allow"u2192ask downgrade). When defaultActionVerdict is degraded, this + remains the shipped value; see defaultActionRendered for what the agent will + actually enforce. Known values are: "allow", "ask", and "deny". + "defaultActionReason": "str", # Optional. Human-readable explanation when + defaultActionVerdict is degraded. + "defaultActionRendered": "str", # Optional. Disposition the agent will + actually enforce as the catch-all: equals defaultAction when exact; the + more-restrictive degrade target (usually ask) when degraded. Known values are: + "allow", "ask", and "deny". + "defaultActionVerdict": "str", # Optional. exact "u2014 agent can enforce + the catch-all as written; degraded "u2014 agent cannot enforce the requested + defaultAction (warn-only; shipped value unchanged); omitted when no descriptor + was available to classify against. Known values are: "exact", "degraded", + "unsupported", and "unverified". + "ok": bool, # Optional. False when any strict rule is unsupported or + unverified "u2014 i.e. CreateSession would reject this policy for this (agent, + version). + "verdicts": [ + { + "reason": "str", # Optional. Human-readable explanation of + the verdict. + "rendered": "str", # Optional. The disposition the agent + will actually enforce: the requested action when exact/unverified, the + more-restrictive target when degraded, omitted when unsupported (cannot + render). Known values are: "allow", "ask", and "deny". + "rule": { + "action": "str", # Optional. Known values are: + "allow", "ask", and "deny". + "enforcement": "str", # Optional. Known values are: + "strict" and "best-effort". + "id": "str", # Optional. Stable rule id assigned + during resolution; correlates with the PolicyDenied event's matched + rule. + "match": { + "str": "str" # Optional. Dictionary of + :code:``. + }, + "tool": "str" # Optional. A rule after resolution + ("u2229 tenant "u2229 platform), carrying a stable id for audit + correlation. + }, + "suggestion": "str", # Optional. Optional actionable hint + (e.g. "set enforcement: best-effort, or target a capable agent"). + "verdict": "str" # Optional. exact "u2014 enforced precisely + as written; degraded "u2014 renderable only in a more-restrictive form + (deny"u2192ask), warned; unsupported "u2014 cannot be enforced (strict + "u21d2 session reject); unverified "u2014 no descriptor available, + rendered conservatively but unproven. Known values are: "exact", + "degraded", "unsupported", and "unverified". + } + ], + "version": "str" # Optional. + } + # response body for status code(s): 400, 404 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + content_type: str = kwargs.pop("content_type") + cls: ClsType[JSON] = kwargs.pop("cls", None) + + _content = data + if not content_type: + raise TypeError( + "Missing required keyword-only argument: content_type. Known values are:" + + "'application/x-yaml'" + ) + + _request = build_harness_validate_session_policy_request( + content_type=content_type, + content=_content, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [200, 400, 404]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + response_headers = {} + if response.status_code == 200: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 400: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 404: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore + + return cast(JSON, deserialized) # type: ignore + + @distributed_trace_async + async def list_session_sandbox_sizes(self, **kwargs: Any) -> JSON: + # pylint: disable=line-too-long + """ListSandboxSizes returns the customer-selectable sandbox (microVM) size catalog. + + Read-only catalog the UI (and other clients) render as the size picker, instead of hardcoding + the list. The set is the single source of truth shared with create-time validation and the + default-size fallback, so every ``slug`` returned here is accepted by CreateSession as + spec.sandbox.sizeSlug. Sizes are returned smallest-to-largest. Static and safe to cache; + independent of team/session; no pagination. + + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 200 + response == { + "sizes": [ + { + "memory_mb": 0, # Optional. Memory in MB (for display). + "price_per_hour_usd": 0.0, # Optional. Hourly on-demand + price in USD (for display). + "price_per_month_usd": 0.0, # Optional. Monthly on-demand + price in USD, assuming continuous usage (for display). + "slug": "str", # Optional. DO size slug; submit as + spec.sandbox.sizeSlug on CreateSession. e.g. "mars-2vcpu-4gb". + "vcpus": 0 # Optional. vCPU count (for display). + } + ] + } + # response body for status code(s): 404 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[JSON] = kwargs.pop("cls", None) + + _request = build_harness_list_session_sandbox_sizes_request( + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [200, 404]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + response_headers = {} + if response.status_code == 200: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 404: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore + + return cast(JSON, deserialized) # type: ignore + + @distributed_trace_async + async def delete_session( + self, session_id: str, *, x_device_uuid: Optional[str] = None, **kwargs: Any + ) -> Optional[JSON]: + # pylint: disable=line-too-long + """DestroySession tears down the sandbox and marks the session DESTROYED. + + Persistent workspace contents are retained per tenant policy. Returns 204 No Content on + success. + + :param session_id: The session UUID. Required. + :type session_id: str + :keyword x_device_uuid: Optional hardware device id forwarded by doctl. Recorded on the session + row as the device that performed the destroy. Default value is None. + :paramtype x_device_uuid: str + :return: JSON object or None + :rtype: JSON or None + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 403, 404, 423 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[Optional[JSON]] = kwargs.pop("cls", None) + + _request = build_harness_delete_session_request( + session_id=session_id, + x_device_uuid=x_device_uuid, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [204, 403, 404, 423]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + deserialized = None + response_headers = {} + if response.status_code == 204: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.status_code == 403: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 404: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 423: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, deserialized, response_headers) # type: ignore + + return deserialized # type: ignore + + @distributed_trace_async + async def get_session(self, session_id: str, **kwargs: Any) -> JSON: + # pylint: disable=line-too-long + """GetSession returns the current snapshot of a session. + + GetSession returns the current snapshot of a session. + + :param session_id: The session UUID. Required. + :type session_id: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 200 + response == { + "session": { + "agent_kind": "AGENT_KIND_UNSPECIFIED", # Optional. Default value is + "AGENT_KIND_UNSPECIFIED". AgentKind identifies what runs inside the session + sandbox. CreateSession accepts CLAUDE_CODE, OPENCODE, CODEX_CLI, CURSOR_CLI, + OPENAI_CODEX, LANGGRAPH, and CREWAI (the supported set is overridable via + HARNESS_SUPPORTED_AGENT_KINDS); other values are rejected with 400. + AGENT_KIND_OPENAI_CODEX is the OpenAI sandbox-provider kind (DO runs codex + exec-server; the agent loop lives at OpenAI). AGENT_KIND_LANGGRAPH is the + LangGraph framework adapter. AGENT_KIND_CREWAI is the CrewAI Crew framework + adapter: like LANGGRAPH the template bakes no agent code, so spec.env must + supply FRAMEWORK_REPO and FRAMEWORK_REPO_SHA; a Crew kickoff is one-shot and + exposes no HITL surface. CREWAI is additionally gated per team and returns + 400 for teams it is not enabled for. Known values are: + "AGENT_KIND_UNSPECIFIED", "AGENT_KIND_CLAUDE_CODE", "AGENT_KIND_OPENCODE", + "AGENT_KIND_CODEX_CLI", "AGENT_KIND_CURSOR_CLI", "AGENT_KIND_NONE", + "AGENT_KIND_CUSTOM", "AGENT_KIND_HERMES", "AGENT_KIND_OPENAI_CODEX", + "AGENT_KIND_LANGGRAPH", and "AGENT_KIND_CREWAI". + "config_id": "str", # Optional. Durable Environment Config UUID the + session was created from (inline manifests persist a config too, named after + the session). Persisted and returned on create/get/list; absent only for + sessions created before config references were stored. + "created_at": "2020-02-20 00:00:00", # Optional. Session is the + user-facing record of a environment session + sandbox pair. + "fork_from_checkpoint_id": "str", # Optional. Lineage: harness-api + checkpoint id (cp_:code:``) the child booted from at fork time. Drives + OHP inherited-history replay on attach. Omitted/empty for root sessions and + legacy forks created before this field was persisted. + "fork_id": "str", # Optional. Lineage: branch label assigned when + this session was created by ForkSession (or rollback's internal fork). + Omitted/empty for root sessions. + "idle_timeout_seconds": 0, # Optional. This session's own idle-pause + timeout (manifest idle_timeout). Omitted when the session uses the + deployment-wide default. + "keep_warm": bool, # Optional. Session is exempt from idle + auto-pause (manifest keep_warm, or the agent-kind default: hermes sessions + keep warm). Pause requests against such a session are rejected with 409. + Omitted when false. + "last_event_at": "2020-02-20 00:00:00", # Optional. Timestamp of the + most recent event in this session's canonical stream. Drives the inactivity + policy that destroys idle sessions. + "name": "str", # Optional. User-assignable display name, unique per + team among non-terminal sessions. Usable in place of session_id by clients + (doctl / Console resolve it to the id via the ListSessions ?name= filter; the + {session_id} path stays UUID-only). Supplied via the environment-spec + metadata.name, or auto-generated + ("session-:code:``-:code:``") when omitted. + "openai_environment_id": "str", # Optional. OpenAI Agents API + environment id (env_"u2026), captured from the resolved CODEX_ENVIRONMENT_ID + guest env value. Populated only for AGENT_KIND_OPENAI_CODEX sessions. + Non-secret correlation metadata. + "openai_session_id": "str", # Optional. OpenAI Agents API session id + (sess_"u2026) this DO sandbox hosts. Populated only for + AGENT_KIND_OPENAI_CODEX sessions (empty otherwise). Non-secret correlation + metadata; a client (doctl agents attach) reads it to bridge to the OpenAI + session. See docs/design/openai-sandbox-provider.md. + "parent_session_id": "str", # Optional. Lineage: id of the session + this one was forked from. Omitted/empty for root (non-fork) sessions. + "pause_reason": "str", # Optional. Why this session is paused. + Present only when status is SESSION_STATUS_PAUSED; omitted otherwise. Known + values: manual (PauseSession RPC), idle (inactivity auto-pause), low_balance + (prepay gate). Clients should treat an unrecognized value as an opaque reason + string. + "repo_hint": "str", # Optional. Optional repository hint supplied at + session creation (first spec.workspace.repos entry of the manifest). Not + authoritative; the agent may change repos in-session. + "resume_on_topoff": bool, # Optional. Session consents to being + resumed automatically when the team's prepayment balance is restored after a + low-balance pause. Defaults to false at create time; may be toggled later via + PATCH /v2/agents/sessions/{session_id} (resume_on_topoff only). Only a + session paused with reason low_balance whose latest run did not complete is + ever resumed automatically - a session the customer paused themselves, or one + that idled out, stays paused regardless. Not an environment-spec field, and + forks do not inherit it. Omitted when false. + "sandbox_id": "str", # Optional. Sandbox identity for support / + debugging. Informational; omitted until a sandbox is allocated. + "session_id": "str", # Optional. Session is the user-facing record + of a environment session + sandbox pair. + "status": "SESSION_STATUS_UNSPECIFIED", # Optional. Default value is + "SESSION_STATUS_UNSPECIFIED". SessionStatus is the lifecycle state of the + sandbox + agent pair. SESSION_STATUS_ALL is a ListSessions status-filter + value only -- no session is ever actually in this status. + SESSION_STATUS_LOCKED is reserved for internal use and never appears in an + end-user-facing response. Known values are: "SESSION_STATUS_UNSPECIFIED", + "SESSION_STATUS_PROVISIONING", "SESSION_STATUS_READY", + "SESSION_STATUS_DETACHED", "SESSION_STATUS_DESTROYING", + "SESSION_STATUS_DESTROYED", "SESSION_STATUS_FAILED", "SESSION_STATUS_PAUSED", + "SESSION_STATUS_ALL", and "SESSION_STATUS_LOCKED". + "warnings": [ + "str" # Optional. Non-fatal advisories computed from the + manifest at create time: one entry per populated + accepted-but-not-yet-enforced section (flat names: secrets, model, tools, + memory, budget, max_lifetime, mounts; legacy manifests get the + corresponding spec.* paths), a deprecation entry when a legacy-envelope + manifest or an adapter alias (codex-cli, cursor-cli) is used, and a + heuristic entry per credential-looking spec.env value. Populated on the + CreateSession response only. + ] + } + } + # response body for status code(s): 403, 404, 423 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[JSON] = kwargs.pop("cls", None) + + _request = build_harness_get_session_request( + session_id=session_id, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [200, 403, 404, 423]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + response_headers = {} + if response.status_code == 200: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 403: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 404: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 423: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore + + return cast(JSON, deserialized) # type: ignore + + @overload + async def update_session( + self, + session_id: str, + body: JSON, + *, + content_type: str = "application/json", + **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """PatchSession updates session-scoped settings on an existing session. + + Partial update of session-scoped settings (not the agent manifest / config). The body must + include at least one recognized patchable property; an empty object {} is 400. Only properties + listed on PatchSessionRequest are accepted — any other top-level JSON key is rejected with 400. + Omitted properties are left unchanged. Today the only patchable field is resume_on_topoff; more + session settings may be added over time. Terminal sessions (DESTROYED, FAILED) cannot be + patched. + + :param session_id: The session UUID. Required. + :type session_id: str + :param body: PATCH body: allowlisted session-scoped settings. At least one property required. + Required. + :type body: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # JSON input template you can fill out and use as your body input. + body = { + "resume_on_topoff": bool # Optional. When present, sets whether this session + opts in to automatic resumption when the team's prepayment balance is restored + after a low-balance pause. Session-scoped consent, not config-scoped. Omitted + properties are not modified. + } + + # response body for status code(s): 200 + response == { + "session": { + "agent_kind": "AGENT_KIND_UNSPECIFIED", # Optional. Default value is + "AGENT_KIND_UNSPECIFIED". AgentKind identifies what runs inside the session + sandbox. CreateSession accepts CLAUDE_CODE, OPENCODE, CODEX_CLI, CURSOR_CLI, + OPENAI_CODEX, LANGGRAPH, and CREWAI (the supported set is overridable via + HARNESS_SUPPORTED_AGENT_KINDS); other values are rejected with 400. + AGENT_KIND_OPENAI_CODEX is the OpenAI sandbox-provider kind (DO runs codex + exec-server; the agent loop lives at OpenAI). AGENT_KIND_LANGGRAPH is the + LangGraph framework adapter. AGENT_KIND_CREWAI is the CrewAI Crew framework + adapter: like LANGGRAPH the template bakes no agent code, so spec.env must + supply FRAMEWORK_REPO and FRAMEWORK_REPO_SHA; a Crew kickoff is one-shot and + exposes no HITL surface. CREWAI is additionally gated per team and returns + 400 for teams it is not enabled for. Known values are: + "AGENT_KIND_UNSPECIFIED", "AGENT_KIND_CLAUDE_CODE", "AGENT_KIND_OPENCODE", + "AGENT_KIND_CODEX_CLI", "AGENT_KIND_CURSOR_CLI", "AGENT_KIND_NONE", + "AGENT_KIND_CUSTOM", "AGENT_KIND_HERMES", "AGENT_KIND_OPENAI_CODEX", + "AGENT_KIND_LANGGRAPH", and "AGENT_KIND_CREWAI". + "config_id": "str", # Optional. Durable Environment Config UUID the + session was created from (inline manifests persist a config too, named after + the session). Persisted and returned on create/get/list; absent only for + sessions created before config references were stored. + "created_at": "2020-02-20 00:00:00", # Optional. Session is the + user-facing record of a environment session + sandbox pair. + "fork_from_checkpoint_id": "str", # Optional. Lineage: harness-api + checkpoint id (cp_:code:``) the child booted from at fork time. Drives + OHP inherited-history replay on attach. Omitted/empty for root sessions and + legacy forks created before this field was persisted. + "fork_id": "str", # Optional. Lineage: branch label assigned when + this session was created by ForkSession (or rollback's internal fork). + Omitted/empty for root sessions. + "idle_timeout_seconds": 0, # Optional. This session's own idle-pause + timeout (manifest idle_timeout). Omitted when the session uses the + deployment-wide default. + "keep_warm": bool, # Optional. Session is exempt from idle + auto-pause (manifest keep_warm, or the agent-kind default: hermes sessions + keep warm). Pause requests against such a session are rejected with 409. + Omitted when false. + "last_event_at": "2020-02-20 00:00:00", # Optional. Timestamp of the + most recent event in this session's canonical stream. Drives the inactivity + policy that destroys idle sessions. + "name": "str", # Optional. User-assignable display name, unique per + team among non-terminal sessions. Usable in place of session_id by clients + (doctl / Console resolve it to the id via the ListSessions ?name= filter; the + {session_id} path stays UUID-only). Supplied via the environment-spec + metadata.name, or auto-generated + ("session-:code:``-:code:``") when omitted. + "openai_environment_id": "str", # Optional. OpenAI Agents API + environment id (env_"u2026), captured from the resolved CODEX_ENVIRONMENT_ID + guest env value. Populated only for AGENT_KIND_OPENAI_CODEX sessions. + Non-secret correlation metadata. + "openai_session_id": "str", # Optional. OpenAI Agents API session id + (sess_"u2026) this DO sandbox hosts. Populated only for + AGENT_KIND_OPENAI_CODEX sessions (empty otherwise). Non-secret correlation + metadata; a client (doctl agents attach) reads it to bridge to the OpenAI + session. See docs/design/openai-sandbox-provider.md. + "parent_session_id": "str", # Optional. Lineage: id of the session + this one was forked from. Omitted/empty for root (non-fork) sessions. + "pause_reason": "str", # Optional. Why this session is paused. + Present only when status is SESSION_STATUS_PAUSED; omitted otherwise. Known + values: manual (PauseSession RPC), idle (inactivity auto-pause), low_balance + (prepay gate). Clients should treat an unrecognized value as an opaque reason + string. + "repo_hint": "str", # Optional. Optional repository hint supplied at + session creation (first spec.workspace.repos entry of the manifest). Not + authoritative; the agent may change repos in-session. + "resume_on_topoff": bool, # Optional. Session consents to being + resumed automatically when the team's prepayment balance is restored after a + low-balance pause. Defaults to false at create time; may be toggled later via + PATCH /v2/agents/sessions/{session_id} (resume_on_topoff only). Only a + session paused with reason low_balance whose latest run did not complete is + ever resumed automatically - a session the customer paused themselves, or one + that idled out, stays paused regardless. Not an environment-spec field, and + forks do not inherit it. Omitted when false. + "sandbox_id": "str", # Optional. Sandbox identity for support / + debugging. Informational; omitted until a sandbox is allocated. + "session_id": "str", # Optional. Session is the user-facing record + of a environment session + sandbox pair. + "status": "SESSION_STATUS_UNSPECIFIED", # Optional. Default value is + "SESSION_STATUS_UNSPECIFIED". SessionStatus is the lifecycle state of the + sandbox + agent pair. SESSION_STATUS_ALL is a ListSessions status-filter + value only -- no session is ever actually in this status. + SESSION_STATUS_LOCKED is reserved for internal use and never appears in an + end-user-facing response. Known values are: "SESSION_STATUS_UNSPECIFIED", + "SESSION_STATUS_PROVISIONING", "SESSION_STATUS_READY", + "SESSION_STATUS_DETACHED", "SESSION_STATUS_DESTROYING", + "SESSION_STATUS_DESTROYED", "SESSION_STATUS_FAILED", "SESSION_STATUS_PAUSED", + "SESSION_STATUS_ALL", and "SESSION_STATUS_LOCKED". + "warnings": [ + "str" # Optional. Non-fatal advisories computed from the + manifest at create time: one entry per populated + accepted-but-not-yet-enforced section (flat names: secrets, model, tools, + memory, budget, max_lifetime, mounts; legacy manifests get the + corresponding spec.* paths), a deprecation entry when a legacy-envelope + manifest or an adapter alias (codex-cli, cursor-cli) is used, and a + heuristic entry per credential-looking spec.env value. Populated on the + CreateSession response only. + ] + } + } + # response body for status code(s): 400, 403, 404, 409, 423 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + + @overload + async def update_session( + self, + session_id: str, + body: IO[bytes], + *, + content_type: str = "application/json", + **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """PatchSession updates session-scoped settings on an existing session. + + Partial update of session-scoped settings (not the agent manifest / config). The body must + include at least one recognized patchable property; an empty object {} is 400. Only properties + listed on PatchSessionRequest are accepted — any other top-level JSON key is rejected with 400. + Omitted properties are left unchanged. Today the only patchable field is resume_on_topoff; more + session settings may be added over time. Terminal sessions (DESTROYED, FAILED) cannot be + patched. + + :param session_id: The session UUID. Required. + :type session_id: str + :param body: PATCH body: allowlisted session-scoped settings. At least one property required. + Required. + :type body: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 200 + response == { + "session": { + "agent_kind": "AGENT_KIND_UNSPECIFIED", # Optional. Default value is + "AGENT_KIND_UNSPECIFIED". AgentKind identifies what runs inside the session + sandbox. CreateSession accepts CLAUDE_CODE, OPENCODE, CODEX_CLI, CURSOR_CLI, + OPENAI_CODEX, LANGGRAPH, and CREWAI (the supported set is overridable via + HARNESS_SUPPORTED_AGENT_KINDS); other values are rejected with 400. + AGENT_KIND_OPENAI_CODEX is the OpenAI sandbox-provider kind (DO runs codex + exec-server; the agent loop lives at OpenAI). AGENT_KIND_LANGGRAPH is the + LangGraph framework adapter. AGENT_KIND_CREWAI is the CrewAI Crew framework + adapter: like LANGGRAPH the template bakes no agent code, so spec.env must + supply FRAMEWORK_REPO and FRAMEWORK_REPO_SHA; a Crew kickoff is one-shot and + exposes no HITL surface. CREWAI is additionally gated per team and returns + 400 for teams it is not enabled for. Known values are: + "AGENT_KIND_UNSPECIFIED", "AGENT_KIND_CLAUDE_CODE", "AGENT_KIND_OPENCODE", + "AGENT_KIND_CODEX_CLI", "AGENT_KIND_CURSOR_CLI", "AGENT_KIND_NONE", + "AGENT_KIND_CUSTOM", "AGENT_KIND_HERMES", "AGENT_KIND_OPENAI_CODEX", + "AGENT_KIND_LANGGRAPH", and "AGENT_KIND_CREWAI". + "config_id": "str", # Optional. Durable Environment Config UUID the + session was created from (inline manifests persist a config too, named after + the session). Persisted and returned on create/get/list; absent only for + sessions created before config references were stored. + "created_at": "2020-02-20 00:00:00", # Optional. Session is the + user-facing record of a environment session + sandbox pair. + "fork_from_checkpoint_id": "str", # Optional. Lineage: harness-api + checkpoint id (cp_:code:``) the child booted from at fork time. Drives + OHP inherited-history replay on attach. Omitted/empty for root sessions and + legacy forks created before this field was persisted. + "fork_id": "str", # Optional. Lineage: branch label assigned when + this session was created by ForkSession (or rollback's internal fork). + Omitted/empty for root sessions. + "idle_timeout_seconds": 0, # Optional. This session's own idle-pause + timeout (manifest idle_timeout). Omitted when the session uses the + deployment-wide default. + "keep_warm": bool, # Optional. Session is exempt from idle + auto-pause (manifest keep_warm, or the agent-kind default: hermes sessions + keep warm). Pause requests against such a session are rejected with 409. + Omitted when false. + "last_event_at": "2020-02-20 00:00:00", # Optional. Timestamp of the + most recent event in this session's canonical stream. Drives the inactivity + policy that destroys idle sessions. + "name": "str", # Optional. User-assignable display name, unique per + team among non-terminal sessions. Usable in place of session_id by clients + (doctl / Console resolve it to the id via the ListSessions ?name= filter; the + {session_id} path stays UUID-only). Supplied via the environment-spec + metadata.name, or auto-generated + ("session-:code:``-:code:``") when omitted. + "openai_environment_id": "str", # Optional. OpenAI Agents API + environment id (env_"u2026), captured from the resolved CODEX_ENVIRONMENT_ID + guest env value. Populated only for AGENT_KIND_OPENAI_CODEX sessions. + Non-secret correlation metadata. + "openai_session_id": "str", # Optional. OpenAI Agents API session id + (sess_"u2026) this DO sandbox hosts. Populated only for + AGENT_KIND_OPENAI_CODEX sessions (empty otherwise). Non-secret correlation + metadata; a client (doctl agents attach) reads it to bridge to the OpenAI + session. See docs/design/openai-sandbox-provider.md. + "parent_session_id": "str", # Optional. Lineage: id of the session + this one was forked from. Omitted/empty for root (non-fork) sessions. + "pause_reason": "str", # Optional. Why this session is paused. + Present only when status is SESSION_STATUS_PAUSED; omitted otherwise. Known + values: manual (PauseSession RPC), idle (inactivity auto-pause), low_balance + (prepay gate). Clients should treat an unrecognized value as an opaque reason + string. + "repo_hint": "str", # Optional. Optional repository hint supplied at + session creation (first spec.workspace.repos entry of the manifest). Not + authoritative; the agent may change repos in-session. + "resume_on_topoff": bool, # Optional. Session consents to being + resumed automatically when the team's prepayment balance is restored after a + low-balance pause. Defaults to false at create time; may be toggled later via + PATCH /v2/agents/sessions/{session_id} (resume_on_topoff only). Only a + session paused with reason low_balance whose latest run did not complete is + ever resumed automatically - a session the customer paused themselves, or one + that idled out, stays paused regardless. Not an environment-spec field, and + forks do not inherit it. Omitted when false. + "sandbox_id": "str", # Optional. Sandbox identity for support / + debugging. Informational; omitted until a sandbox is allocated. + "session_id": "str", # Optional. Session is the user-facing record + of a environment session + sandbox pair. + "status": "SESSION_STATUS_UNSPECIFIED", # Optional. Default value is + "SESSION_STATUS_UNSPECIFIED". SessionStatus is the lifecycle state of the + sandbox + agent pair. SESSION_STATUS_ALL is a ListSessions status-filter + value only -- no session is ever actually in this status. + SESSION_STATUS_LOCKED is reserved for internal use and never appears in an + end-user-facing response. Known values are: "SESSION_STATUS_UNSPECIFIED", + "SESSION_STATUS_PROVISIONING", "SESSION_STATUS_READY", + "SESSION_STATUS_DETACHED", "SESSION_STATUS_DESTROYING", + "SESSION_STATUS_DESTROYED", "SESSION_STATUS_FAILED", "SESSION_STATUS_PAUSED", + "SESSION_STATUS_ALL", and "SESSION_STATUS_LOCKED". + "warnings": [ + "str" # Optional. Non-fatal advisories computed from the + manifest at create time: one entry per populated + accepted-but-not-yet-enforced section (flat names: secrets, model, tools, + memory, budget, max_lifetime, mounts; legacy manifests get the + corresponding spec.* paths), a deprecation entry when a legacy-envelope + manifest or an adapter alias (codex-cli, cursor-cli) is used, and a + heuristic entry per credential-looking spec.env value. Populated on the + CreateSession response only. + ] + } + } + # response body for status code(s): 400, 403, 404, 409, 423 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + + @distributed_trace_async + async def update_session( + self, session_id: str, body: Union[JSON, IO[bytes]], **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """PatchSession updates session-scoped settings on an existing session. + + Partial update of session-scoped settings (not the agent manifest / config). The body must + include at least one recognized patchable property; an empty object {} is 400. Only properties + listed on PatchSessionRequest are accepted — any other top-level JSON key is rejected with 400. + Omitted properties are left unchanged. Today the only patchable field is resume_on_topoff; more + session settings may be added over time. Terminal sessions (DESTROYED, FAILED) cannot be + patched. + + :param session_id: The session UUID. Required. + :type session_id: str + :param body: PATCH body: allowlisted session-scoped settings. At least one property required. + Is either a JSON type or a IO[bytes] type. Required. + :type body: JSON or IO[bytes] + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # JSON input template you can fill out and use as your body input. + body = { + "resume_on_topoff": bool # Optional. When present, sets whether this session + opts in to automatic resumption when the team's prepayment balance is restored + after a low-balance pause. Session-scoped consent, not config-scoped. Omitted + properties are not modified. + } + + # response body for status code(s): 200 + response == { + "session": { + "agent_kind": "AGENT_KIND_UNSPECIFIED", # Optional. Default value is + "AGENT_KIND_UNSPECIFIED". AgentKind identifies what runs inside the session + sandbox. CreateSession accepts CLAUDE_CODE, OPENCODE, CODEX_CLI, CURSOR_CLI, + OPENAI_CODEX, LANGGRAPH, and CREWAI (the supported set is overridable via + HARNESS_SUPPORTED_AGENT_KINDS); other values are rejected with 400. + AGENT_KIND_OPENAI_CODEX is the OpenAI sandbox-provider kind (DO runs codex + exec-server; the agent loop lives at OpenAI). AGENT_KIND_LANGGRAPH is the + LangGraph framework adapter. AGENT_KIND_CREWAI is the CrewAI Crew framework + adapter: like LANGGRAPH the template bakes no agent code, so spec.env must + supply FRAMEWORK_REPO and FRAMEWORK_REPO_SHA; a Crew kickoff is one-shot and + exposes no HITL surface. CREWAI is additionally gated per team and returns + 400 for teams it is not enabled for. Known values are: + "AGENT_KIND_UNSPECIFIED", "AGENT_KIND_CLAUDE_CODE", "AGENT_KIND_OPENCODE", + "AGENT_KIND_CODEX_CLI", "AGENT_KIND_CURSOR_CLI", "AGENT_KIND_NONE", + "AGENT_KIND_CUSTOM", "AGENT_KIND_HERMES", "AGENT_KIND_OPENAI_CODEX", + "AGENT_KIND_LANGGRAPH", and "AGENT_KIND_CREWAI". + "config_id": "str", # Optional. Durable Environment Config UUID the + session was created from (inline manifests persist a config too, named after + the session). Persisted and returned on create/get/list; absent only for + sessions created before config references were stored. + "created_at": "2020-02-20 00:00:00", # Optional. Session is the + user-facing record of a environment session + sandbox pair. + "fork_from_checkpoint_id": "str", # Optional. Lineage: harness-api + checkpoint id (cp_:code:``) the child booted from at fork time. Drives + OHP inherited-history replay on attach. Omitted/empty for root sessions and + legacy forks created before this field was persisted. + "fork_id": "str", # Optional. Lineage: branch label assigned when + this session was created by ForkSession (or rollback's internal fork). + Omitted/empty for root sessions. + "idle_timeout_seconds": 0, # Optional. This session's own idle-pause + timeout (manifest idle_timeout). Omitted when the session uses the + deployment-wide default. + "keep_warm": bool, # Optional. Session is exempt from idle + auto-pause (manifest keep_warm, or the agent-kind default: hermes sessions + keep warm). Pause requests against such a session are rejected with 409. + Omitted when false. + "last_event_at": "2020-02-20 00:00:00", # Optional. Timestamp of the + most recent event in this session's canonical stream. Drives the inactivity + policy that destroys idle sessions. + "name": "str", # Optional. User-assignable display name, unique per + team among non-terminal sessions. Usable in place of session_id by clients + (doctl / Console resolve it to the id via the ListSessions ?name= filter; the + {session_id} path stays UUID-only). Supplied via the environment-spec + metadata.name, or auto-generated + ("session-:code:``-:code:``") when omitted. + "openai_environment_id": "str", # Optional. OpenAI Agents API + environment id (env_"u2026), captured from the resolved CODEX_ENVIRONMENT_ID + guest env value. Populated only for AGENT_KIND_OPENAI_CODEX sessions. + Non-secret correlation metadata. + "openai_session_id": "str", # Optional. OpenAI Agents API session id + (sess_"u2026) this DO sandbox hosts. Populated only for + AGENT_KIND_OPENAI_CODEX sessions (empty otherwise). Non-secret correlation + metadata; a client (doctl agents attach) reads it to bridge to the OpenAI + session. See docs/design/openai-sandbox-provider.md. + "parent_session_id": "str", # Optional. Lineage: id of the session + this one was forked from. Omitted/empty for root (non-fork) sessions. + "pause_reason": "str", # Optional. Why this session is paused. + Present only when status is SESSION_STATUS_PAUSED; omitted otherwise. Known + values: manual (PauseSession RPC), idle (inactivity auto-pause), low_balance + (prepay gate). Clients should treat an unrecognized value as an opaque reason + string. + "repo_hint": "str", # Optional. Optional repository hint supplied at + session creation (first spec.workspace.repos entry of the manifest). Not + authoritative; the agent may change repos in-session. + "resume_on_topoff": bool, # Optional. Session consents to being + resumed automatically when the team's prepayment balance is restored after a + low-balance pause. Defaults to false at create time; may be toggled later via + PATCH /v2/agents/sessions/{session_id} (resume_on_topoff only). Only a + session paused with reason low_balance whose latest run did not complete is + ever resumed automatically - a session the customer paused themselves, or one + that idled out, stays paused regardless. Not an environment-spec field, and + forks do not inherit it. Omitted when false. + "sandbox_id": "str", # Optional. Sandbox identity for support / + debugging. Informational; omitted until a sandbox is allocated. + "session_id": "str", # Optional. Session is the user-facing record + of a environment session + sandbox pair. + "status": "SESSION_STATUS_UNSPECIFIED", # Optional. Default value is + "SESSION_STATUS_UNSPECIFIED". SessionStatus is the lifecycle state of the + sandbox + agent pair. SESSION_STATUS_ALL is a ListSessions status-filter + value only -- no session is ever actually in this status. + SESSION_STATUS_LOCKED is reserved for internal use and never appears in an + end-user-facing response. Known values are: "SESSION_STATUS_UNSPECIFIED", + "SESSION_STATUS_PROVISIONING", "SESSION_STATUS_READY", + "SESSION_STATUS_DETACHED", "SESSION_STATUS_DESTROYING", + "SESSION_STATUS_DESTROYED", "SESSION_STATUS_FAILED", "SESSION_STATUS_PAUSED", + "SESSION_STATUS_ALL", and "SESSION_STATUS_LOCKED". + "warnings": [ + "str" # Optional. Non-fatal advisories computed from the + manifest at create time: one entry per populated + accepted-but-not-yet-enforced section (flat names: secrets, model, tools, + memory, budget, max_lifetime, mounts; legacy manifests get the + corresponding spec.* paths), a deprecation entry when a legacy-envelope + manifest or an adapter alias (codex-cli, cursor-cli) is used, and a + heuristic entry per credential-looking spec.env value. Populated on the + CreateSession response only. + ] + } + } + # response body for status code(s): 400, 403, 404, 409, 423 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop( + "content_type", _headers.pop("Content-Type", None) + ) + cls: ClsType[JSON] = kwargs.pop("cls", None) + + content_type = content_type or "application/json" + _json = None + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _json = body + + _request = build_harness_update_session_request( + session_id=session_id, + content_type=content_type, + json=_json, + content=_content, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [200, 400, 403, 404, 409, 423]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + response_headers = {} + if response.status_code == 200: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 400: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 403: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 404: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 409: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 423: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore + + return cast(JSON, deserialized) # type: ignore + + @distributed_trace_async + async def list_session_checkpoints( + self, + session_id: str, + *, + page_size: Optional[int] = None, + page_token: Optional[str] = None, + **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """ListCheckpoints enumerates a session's checkpoints, newest first. + + Keyset-paginated on an opaque cursor, like ListSessions. + + :param session_id: The session UUID. Required. + :type session_id: str + :keyword page_size: Page size. Defaults to 50 when omitted or non-positive. Default value is + None. + :paramtype page_size: int + :keyword page_token: Opaque keyset cursor returned by the previous page. Empty starts from the + beginning. Default value is None. + :paramtype page_token: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 200 + response == { + "checkpoints": [ + { + "checkpoint_id": "str", # Optional. Public checkpoint id, + stable for the checkpoint's lifetime (e.g. cp_9f2c1a4b). + "created_at": "2020-02-20 00:00:00", # Optional. Newest + first. + "error_message": "str", # Optional. Client-safe failure + detail; set only when status is FAILED. + "event_id": "str", # Optional. Event-log cursor (ULID) of + the last event included in the cut; forks resume event replay from here. + "kind": "str", # Optional. How the checkpoint came to exist: + explicit - requested via CreateCheckpoint; implicit - minted + automatically by the platform (on run completion, or when ForkSession is + called without from_checkpoint_id). Known values are: "explicit" and + "implicit". + "label": "str", # Optional. Optional user-supplied label + from CreateCheckpoint; empty when omitted. + "session_id": "str", # Optional. Owning session. + "size_bytes": 0, # Optional. Size of the captured machine + state. 0 until the checkpoint reaches READY. + "status": "str" # Optional. Capture lifecycle: PENDING -> + READY | FAILED. Only READY checkpoints are forkable/restorable. + CreateCheckpoint blocks until terminal, so PENDING is only ever + observable via list/get while a capture is in flight. Known values are: + "PENDING", "READY", and "FAILED". + } + ], + "next_page_token": "str" # Optional. Opaque cursor for the next page. Empty + when there are no further pages. + } + # response body for status code(s): 400, 403, 404, 423 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[JSON] = kwargs.pop("cls", None) + + _request = build_harness_list_session_checkpoints_request( + session_id=session_id, + page_size=page_size, + page_token=page_token, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [200, 400, 403, 404, 423]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + response_headers = {} + if response.status_code == 200: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 400: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 403: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 404: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 423: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore + + return cast(JSON, deserialized) # type: ignore + + @overload + async def create_session_checkpoint( + self, + session_id: str, + body: Optional[JSON] = None, + *, + content_type: str = "application/json", + **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """CreateCheckpoint captures the session's live sandbox state as a durable checkpoint. + + Synchronous: blocks until the capture reaches READY and returns the terminal Checkpoint — + clients never see PENDING on this endpoint and need no poll loop. The checkpoint binds the + machine-state capture to the event-log cursor at the cut, so it can later seed forks (POST + .../fork) and rollback. Checkpoints are taken between turns; a session with an active turn in + flight returns 409. + + :param session_id: The session UUID. Required. + :type session_id: str + :param body: Optional; an empty or omitted body creates an unlabeled checkpoint. Default value + is None. + :type body: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # JSON input template you can fill out and use as your body input. + body = { + "label": "str" # Optional. Optional human-readable label stored on the + checkpoint; "" if omitted. + } + + # response body for status code(s): 200 + response == { + "checkpoint": { + "checkpoint_id": "str", # Optional. Public checkpoint id, stable for + the checkpoint's lifetime (e.g. cp_9f2c1a4b). + "created_at": "2020-02-20 00:00:00", # Optional. Checkpoint is a + durable capture of a session's live sandbox machine state bound to the + event-log cursor at the cut. READY checkpoints seed forks (ForkSession) and + rollback. Internal fields (team_id, the substrate checkpoint id, event_seq) + are never serialized. + "error_message": "str", # Optional. Client-safe failure detail; set + only when status is FAILED. + "event_id": "str", # Optional. Event-log cursor (ULID) of the last + event included in the cut; forks resume event replay from here. + "kind": "str", # Optional. How the checkpoint came to exist: + explicit - requested via CreateCheckpoint; implicit - minted automatically by + the platform (on run completion, or when ForkSession is called without + from_checkpoint_id). Known values are: "explicit" and "implicit". + "label": "str", # Optional. Optional user-supplied label from + CreateCheckpoint; empty when omitted. + "session_id": "str", # Optional. Owning session. + "size_bytes": 0, # Optional. Size of the captured machine state. 0 + until the checkpoint reaches READY. + "status": "str" # Optional. Capture lifecycle: PENDING -> READY | + FAILED. Only READY checkpoints are forkable/restorable. CreateCheckpoint + blocks until terminal, so PENDING is only ever observable via list/get while + a capture is in flight. Known values are: "PENDING", "READY", and "FAILED". + } + } + # response body for status code(s): 402, 403, 404, 409, 423, 501 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + + @overload + async def create_session_checkpoint( + self, + session_id: str, + body: Optional[IO[bytes]] = None, + *, + content_type: str = "application/json", + **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """CreateCheckpoint captures the session's live sandbox state as a durable checkpoint. + + Synchronous: blocks until the capture reaches READY and returns the terminal Checkpoint — + clients never see PENDING on this endpoint and need no poll loop. The checkpoint binds the + machine-state capture to the event-log cursor at the cut, so it can later seed forks (POST + .../fork) and rollback. Checkpoints are taken between turns; a session with an active turn in + flight returns 409. + + :param session_id: The session UUID. Required. + :type session_id: str + :param body: Optional; an empty or omitted body creates an unlabeled checkpoint. Default value + is None. + :type body: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 200 + response == { + "checkpoint": { + "checkpoint_id": "str", # Optional. Public checkpoint id, stable for + the checkpoint's lifetime (e.g. cp_9f2c1a4b). + "created_at": "2020-02-20 00:00:00", # Optional. Checkpoint is a + durable capture of a session's live sandbox machine state bound to the + event-log cursor at the cut. READY checkpoints seed forks (ForkSession) and + rollback. Internal fields (team_id, the substrate checkpoint id, event_seq) + are never serialized. + "error_message": "str", # Optional. Client-safe failure detail; set + only when status is FAILED. + "event_id": "str", # Optional. Event-log cursor (ULID) of the last + event included in the cut; forks resume event replay from here. + "kind": "str", # Optional. How the checkpoint came to exist: + explicit - requested via CreateCheckpoint; implicit - minted automatically by + the platform (on run completion, or when ForkSession is called without + from_checkpoint_id). Known values are: "explicit" and "implicit". + "label": "str", # Optional. Optional user-supplied label from + CreateCheckpoint; empty when omitted. + "session_id": "str", # Optional. Owning session. + "size_bytes": 0, # Optional. Size of the captured machine state. 0 + until the checkpoint reaches READY. + "status": "str" # Optional. Capture lifecycle: PENDING -> READY | + FAILED. Only READY checkpoints are forkable/restorable. CreateCheckpoint + blocks until terminal, so PENDING is only ever observable via list/get while + a capture is in flight. Known values are: "PENDING", "READY", and "FAILED". + } + } + # response body for status code(s): 402, 403, 404, 409, 423, 501 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + + @distributed_trace_async + async def create_session_checkpoint( + self, + session_id: str, + body: Optional[Union[JSON, IO[bytes]]] = None, + **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """CreateCheckpoint captures the session's live sandbox state as a durable checkpoint. + + Synchronous: blocks until the capture reaches READY and returns the terminal Checkpoint — + clients never see PENDING on this endpoint and need no poll loop. The checkpoint binds the + machine-state capture to the event-log cursor at the cut, so it can later seed forks (POST + .../fork) and rollback. Checkpoints are taken between turns; a session with an active turn in + flight returns 409. + + :param session_id: The session UUID. Required. + :type session_id: str + :param body: Optional; an empty or omitted body creates an unlabeled checkpoint. Is either a + JSON type or a IO[bytes] type. Default value is None. + :type body: JSON or IO[bytes] + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # JSON input template you can fill out and use as your body input. + body = { + "label": "str" # Optional. Optional human-readable label stored on the + checkpoint; "" if omitted. + } + + # response body for status code(s): 200 + response == { + "checkpoint": { + "checkpoint_id": "str", # Optional. Public checkpoint id, stable for + the checkpoint's lifetime (e.g. cp_9f2c1a4b). + "created_at": "2020-02-20 00:00:00", # Optional. Checkpoint is a + durable capture of a session's live sandbox machine state bound to the + event-log cursor at the cut. READY checkpoints seed forks (ForkSession) and + rollback. Internal fields (team_id, the substrate checkpoint id, event_seq) + are never serialized. + "error_message": "str", # Optional. Client-safe failure detail; set + only when status is FAILED. + "event_id": "str", # Optional. Event-log cursor (ULID) of the last + event included in the cut; forks resume event replay from here. + "kind": "str", # Optional. How the checkpoint came to exist: + explicit - requested via CreateCheckpoint; implicit - minted automatically by + the platform (on run completion, or when ForkSession is called without + from_checkpoint_id). Known values are: "explicit" and "implicit". + "label": "str", # Optional. Optional user-supplied label from + CreateCheckpoint; empty when omitted. + "session_id": "str", # Optional. Owning session. + "size_bytes": 0, # Optional. Size of the captured machine state. 0 + until the checkpoint reaches READY. + "status": "str" # Optional. Capture lifecycle: PENDING -> READY | + FAILED. Only READY checkpoints are forkable/restorable. CreateCheckpoint + blocks until terminal, so PENDING is only ever observable via list/get while + a capture is in flight. Known values are: "PENDING", "READY", and "FAILED". + } + } + # response body for status code(s): 402, 403, 404, 409, 423, 501 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop( + "content_type", _headers.pop("Content-Type", None) + ) + cls: ClsType[JSON] = kwargs.pop("cls", None) + + content_type = content_type or "application/json" + _json = None + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + if body is not None: + _json = body + else: + _json = None + + _request = build_harness_create_session_checkpoint_request( + session_id=session_id, + content_type=content_type, + json=_json, + content=_content, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [200, 402, 403, 404, 409, 423, 501]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + response_headers = {} + if response.status_code == 200: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 402: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 403: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 404: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 409: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 423: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 501: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore + + return cast(JSON, deserialized) # type: ignore + + @distributed_trace_async + async def get_session_checkpoint( + self, session_id: str, checkpoint_id: str, **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """GetCheckpoint returns the current snapshot of a checkpoint. + + GetCheckpoint returns the current snapshot of a checkpoint. + + :param session_id: The session UUID. Required. + :type session_id: str + :param checkpoint_id: The checkpoint id. Required. + :type checkpoint_id: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 200 + response == { + "checkpoint": { + "checkpoint_id": "str", # Optional. Public checkpoint id, stable for + the checkpoint's lifetime (e.g. cp_9f2c1a4b). + "created_at": "2020-02-20 00:00:00", # Optional. Checkpoint is a + durable capture of a session's live sandbox machine state bound to the + event-log cursor at the cut. READY checkpoints seed forks (ForkSession) and + rollback. Internal fields (team_id, the substrate checkpoint id, event_seq) + are never serialized. + "error_message": "str", # Optional. Client-safe failure detail; set + only when status is FAILED. + "event_id": "str", # Optional. Event-log cursor (ULID) of the last + event included in the cut; forks resume event replay from here. + "kind": "str", # Optional. How the checkpoint came to exist: + explicit - requested via CreateCheckpoint; implicit - minted automatically by + the platform (on run completion, or when ForkSession is called without + from_checkpoint_id). Known values are: "explicit" and "implicit". + "label": "str", # Optional. Optional user-supplied label from + CreateCheckpoint; empty when omitted. + "session_id": "str", # Optional. Owning session. + "size_bytes": 0, # Optional. Size of the captured machine state. 0 + until the checkpoint reaches READY. + "status": "str" # Optional. Capture lifecycle: PENDING -> READY | + FAILED. Only READY checkpoints are forkable/restorable. CreateCheckpoint + blocks until terminal, so PENDING is only ever observable via list/get while + a capture is in flight. Known values are: "PENDING", "READY", and "FAILED". + } + } + # response body for status code(s): 403, 404, 423 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[JSON] = kwargs.pop("cls", None) + + _request = build_harness_get_session_checkpoint_request( + session_id=session_id, + checkpoint_id=checkpoint_id, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [200, 403, 404, 423]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + response_headers = {} + if response.status_code == 200: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 403: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 404: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 423: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore + + return cast(JSON, deserialized) # type: ignore + + @distributed_trace_async + async def delete_session_checkpoint( + self, session_id: str, checkpoint_id: str, **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """DeleteCheckpoint removes a checkpoint and its underlying machine-state capture. + + Idempotent: deleting an already-gone checkpoint still returns 200. Removes the DB row and the + substrate checkpoint. + + :param session_id: The session UUID. Required. + :type session_id: str + :param checkpoint_id: The checkpoint id. Required. + :type checkpoint_id: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 200 + response == { + "checkpoint_id": "str", # Optional. Echo of the requested checkpoint id. + "deleted": bool # Optional. Always true on 200 (delete is idempotent). + } + # response body for status code(s): 403, 404, 423 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[JSON] = kwargs.pop("cls", None) + + _request = build_harness_delete_session_checkpoint_request( + session_id=session_id, + checkpoint_id=checkpoint_id, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [200, 403, 404, 423]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + response_headers = {} + if response.status_code == 200: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 403: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 404: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 423: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore + + return cast(JSON, deserialized) # type: ignore + + @distributed_trace_async + async def revert_session_checkpoint( + self, session_id: str, checkpoint_id: str, **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """RollbackToCheckpoint rewinds a session to a checkpoint. + + In-place restore: boots a fresh sandbox from the checkpoint and atomically swaps the session's + sandbox pointer to it, so the session keeps its same session_id; the old sandbox is then torn + down. + + :param session_id: The session UUID. Required. + :type session_id: str + :param checkpoint_id: The checkpoint id. Required. + :type checkpoint_id: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 200 + response == { + "session": { + "agent_kind": "AGENT_KIND_UNSPECIFIED", # Optional. Default value is + "AGENT_KIND_UNSPECIFIED". AgentKind identifies what runs inside the session + sandbox. CreateSession accepts CLAUDE_CODE, OPENCODE, CODEX_CLI, CURSOR_CLI, + OPENAI_CODEX, LANGGRAPH, and CREWAI (the supported set is overridable via + HARNESS_SUPPORTED_AGENT_KINDS); other values are rejected with 400. + AGENT_KIND_OPENAI_CODEX is the OpenAI sandbox-provider kind (DO runs codex + exec-server; the agent loop lives at OpenAI). AGENT_KIND_LANGGRAPH is the + LangGraph framework adapter. AGENT_KIND_CREWAI is the CrewAI Crew framework + adapter: like LANGGRAPH the template bakes no agent code, so spec.env must + supply FRAMEWORK_REPO and FRAMEWORK_REPO_SHA; a Crew kickoff is one-shot and + exposes no HITL surface. CREWAI is additionally gated per team and returns + 400 for teams it is not enabled for. Known values are: + "AGENT_KIND_UNSPECIFIED", "AGENT_KIND_CLAUDE_CODE", "AGENT_KIND_OPENCODE", + "AGENT_KIND_CODEX_CLI", "AGENT_KIND_CURSOR_CLI", "AGENT_KIND_NONE", + "AGENT_KIND_CUSTOM", "AGENT_KIND_HERMES", "AGENT_KIND_OPENAI_CODEX", + "AGENT_KIND_LANGGRAPH", and "AGENT_KIND_CREWAI". + "config_id": "str", # Optional. Durable Environment Config UUID the + session was created from (inline manifests persist a config too, named after + the session). Persisted and returned on create/get/list; absent only for + sessions created before config references were stored. + "created_at": "2020-02-20 00:00:00", # Optional. Session is the + user-facing record of a environment session + sandbox pair. + "fork_from_checkpoint_id": "str", # Optional. Lineage: harness-api + checkpoint id (cp_:code:``) the child booted from at fork time. Drives + OHP inherited-history replay on attach. Omitted/empty for root sessions and + legacy forks created before this field was persisted. + "fork_id": "str", # Optional. Lineage: branch label assigned when + this session was created by ForkSession (or rollback's internal fork). + Omitted/empty for root sessions. + "idle_timeout_seconds": 0, # Optional. This session's own idle-pause + timeout (manifest idle_timeout). Omitted when the session uses the + deployment-wide default. + "keep_warm": bool, # Optional. Session is exempt from idle + auto-pause (manifest keep_warm, or the agent-kind default: hermes sessions + keep warm). Pause requests against such a session are rejected with 409. + Omitted when false. + "last_event_at": "2020-02-20 00:00:00", # Optional. Timestamp of the + most recent event in this session's canonical stream. Drives the inactivity + policy that destroys idle sessions. + "name": "str", # Optional. User-assignable display name, unique per + team among non-terminal sessions. Usable in place of session_id by clients + (doctl / Console resolve it to the id via the ListSessions ?name= filter; the + {session_id} path stays UUID-only). Supplied via the environment-spec + metadata.name, or auto-generated + ("session-:code:``-:code:``") when omitted. + "openai_environment_id": "str", # Optional. OpenAI Agents API + environment id (env_"u2026), captured from the resolved CODEX_ENVIRONMENT_ID + guest env value. Populated only for AGENT_KIND_OPENAI_CODEX sessions. + Non-secret correlation metadata. + "openai_session_id": "str", # Optional. OpenAI Agents API session id + (sess_"u2026) this DO sandbox hosts. Populated only for + AGENT_KIND_OPENAI_CODEX sessions (empty otherwise). Non-secret correlation + metadata; a client (doctl agents attach) reads it to bridge to the OpenAI + session. See docs/design/openai-sandbox-provider.md. + "parent_session_id": "str", # Optional. Lineage: id of the session + this one was forked from. Omitted/empty for root (non-fork) sessions. + "pause_reason": "str", # Optional. Why this session is paused. + Present only when status is SESSION_STATUS_PAUSED; omitted otherwise. Known + values: manual (PauseSession RPC), idle (inactivity auto-pause), low_balance + (prepay gate). Clients should treat an unrecognized value as an opaque reason + string. + "repo_hint": "str", # Optional. Optional repository hint supplied at + session creation (first spec.workspace.repos entry of the manifest). Not + authoritative; the agent may change repos in-session. + "resume_on_topoff": bool, # Optional. Session consents to being + resumed automatically when the team's prepayment balance is restored after a + low-balance pause. Defaults to false at create time; may be toggled later via + PATCH /v2/agents/sessions/{session_id} (resume_on_topoff only). Only a + session paused with reason low_balance whose latest run did not complete is + ever resumed automatically - a session the customer paused themselves, or one + that idled out, stays paused regardless. Not an environment-spec field, and + forks do not inherit it. Omitted when false. + "sandbox_id": "str", # Optional. Sandbox identity for support / + debugging. Informational; omitted until a sandbox is allocated. + "session_id": "str", # Optional. Session is the user-facing record + of a environment session + sandbox pair. + "status": "SESSION_STATUS_UNSPECIFIED", # Optional. Default value is + "SESSION_STATUS_UNSPECIFIED". SessionStatus is the lifecycle state of the + sandbox + agent pair. SESSION_STATUS_ALL is a ListSessions status-filter + value only -- no session is ever actually in this status. + SESSION_STATUS_LOCKED is reserved for internal use and never appears in an + end-user-facing response. Known values are: "SESSION_STATUS_UNSPECIFIED", + "SESSION_STATUS_PROVISIONING", "SESSION_STATUS_READY", + "SESSION_STATUS_DETACHED", "SESSION_STATUS_DESTROYING", + "SESSION_STATUS_DESTROYED", "SESSION_STATUS_FAILED", "SESSION_STATUS_PAUSED", + "SESSION_STATUS_ALL", and "SESSION_STATUS_LOCKED". + "warnings": [ + "str" # Optional. Non-fatal advisories computed from the + manifest at create time: one entry per populated + accepted-but-not-yet-enforced section (flat names: secrets, model, tools, + memory, budget, max_lifetime, mounts; legacy manifests get the + corresponding spec.* paths), a deprecation entry when a legacy-envelope + manifest or an adapter alias (codex-cli, cursor-cli) is used, and a + heuristic entry per credential-looking spec.env value. Populated on the + CreateSession response only. + ] + } + } + # response body for status code(s): 402, 403, 404, 409, 423, 501 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[JSON] = kwargs.pop("cls", None) + + _request = build_harness_revert_session_checkpoint_request( + session_id=session_id, + checkpoint_id=checkpoint_id, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [200, 402, 403, 404, 409, 423, 501]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + response_headers = {} + if response.status_code == 200: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 402: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 403: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 404: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 409: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 423: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 501: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore + + return cast(JSON, deserialized) # type: ignore + + @overload + async def create_session_fork( + self, + session_id: str, + body: Optional[JSON] = None, + *, + content_type: str = "application/json", + **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """ForkSession creates child sessions from a checkpoint of this session. + + Each child boots a fresh sandbox from the checkpoint's machine-state capture and is drivable + exactly like any other session (input, HITL), with parent_session_id and fork_id set for + lineage. When from_checkpoint_id is omitted, an implicit checkpoint of the session "now" is + captured first and the children fork from that. All-or-nothing: on failure nothing is left + running. + + :param session_id: The session UUID. Required. + :type session_id: str + :param body: Optional; an empty or omitted body forks a single child from an implicit + checkpoint of "now". Default value is None. + :type body: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # JSON input template you can fill out and use as your body input. + body = { + "count": 1, # Optional. Default value is 1. Number of child sessions to + create, 1-4. Defaults to 1 when omitted; a value outside 1-4 is rejected with + 400. + "from_checkpoint_id": "str" # Optional. Checkpoint to fork from; must be + READY. Omitted/empty means capture an implicit checkpoint of the session "now" + first and fork from that. + } + + # response body for status code(s): 200 + response == { + "sessions": [ + { + "agent_kind": "AGENT_KIND_UNSPECIFIED", # Optional. Default + value is "AGENT_KIND_UNSPECIFIED". AgentKind identifies what runs inside + the session sandbox. CreateSession accepts CLAUDE_CODE, OPENCODE, + CODEX_CLI, CURSOR_CLI, OPENAI_CODEX, LANGGRAPH, and CREWAI (the supported + set is overridable via HARNESS_SUPPORTED_AGENT_KINDS); other values are + rejected with 400. AGENT_KIND_OPENAI_CODEX is the OpenAI sandbox-provider + kind (DO runs codex exec-server; the agent loop lives at OpenAI). + AGENT_KIND_LANGGRAPH is the LangGraph framework adapter. + AGENT_KIND_CREWAI is the CrewAI Crew framework adapter: like LANGGRAPH + the template bakes no agent code, so spec.env must supply FRAMEWORK_REPO + and FRAMEWORK_REPO_SHA; a Crew kickoff is one-shot and exposes no HITL + surface. CREWAI is additionally gated per team and returns 400 for teams + it is not enabled for. Known values are: "AGENT_KIND_UNSPECIFIED", + "AGENT_KIND_CLAUDE_CODE", "AGENT_KIND_OPENCODE", "AGENT_KIND_CODEX_CLI", + "AGENT_KIND_CURSOR_CLI", "AGENT_KIND_NONE", "AGENT_KIND_CUSTOM", + "AGENT_KIND_HERMES", "AGENT_KIND_OPENAI_CODEX", "AGENT_KIND_LANGGRAPH", + and "AGENT_KIND_CREWAI". + "config_id": "str", # Optional. Durable Environment Config + UUID the session was created from (inline manifests persist a config too, + named after the session). Persisted and returned on create/get/list; + absent only for sessions created before config references were stored. + "created_at": "2020-02-20 00:00:00", # Optional. The created + child sessions, each a full Session with parent_session_id and fork_id + set (PROVISIONING -> READY) and drivable exactly like any other session. + "fork_from_checkpoint_id": "str", # Optional. Lineage: + harness-api checkpoint id (cp_:code:``) the child booted from at + fork time. Drives OHP inherited-history replay on attach. Omitted/empty + for root sessions and legacy forks created before this field was + persisted. + "fork_id": "str", # Optional. Lineage: branch label assigned + when this session was created by ForkSession (or rollback's internal + fork). Omitted/empty for root sessions. + "idle_timeout_seconds": 0, # Optional. This session's own + idle-pause timeout (manifest idle_timeout). Omitted when the session uses + the deployment-wide default. + "keep_warm": bool, # Optional. Session is exempt from idle + auto-pause (manifest keep_warm, or the agent-kind default: hermes + sessions keep warm). Pause requests against such a session are rejected + with 409. Omitted when false. + "last_event_at": "2020-02-20 00:00:00", # Optional. + Timestamp of the most recent event in this session's canonical stream. + Drives the inactivity policy that destroys idle sessions. + "name": "str", # Optional. User-assignable display name, + unique per team among non-terminal sessions. Usable in place of + session_id by clients (doctl / Console resolve it to the id via the + ListSessions ?name= filter; the {session_id} path stays UUID-only). + Supplied via the environment-spec metadata.name, or auto-generated + ("session-:code:``-:code:``") when omitted. + "openai_environment_id": "str", # Optional. OpenAI Agents + API environment id (env_"u2026), captured from the resolved + CODEX_ENVIRONMENT_ID guest env value. Populated only for + AGENT_KIND_OPENAI_CODEX sessions. Non-secret correlation metadata. + "openai_session_id": "str", # Optional. OpenAI Agents API + session id (sess_"u2026) this DO sandbox hosts. Populated only for + AGENT_KIND_OPENAI_CODEX sessions (empty otherwise). Non-secret + correlation metadata; a client (doctl agents attach) reads it to bridge + to the OpenAI session. See docs/design/openai-sandbox-provider.md. + "parent_session_id": "str", # Optional. Lineage: id of the + session this one was forked from. Omitted/empty for root (non-fork) + sessions. + "pause_reason": "str", # Optional. Why this session is + paused. Present only when status is SESSION_STATUS_PAUSED; omitted + otherwise. Known values: manual (PauseSession RPC), idle (inactivity + auto-pause), low_balance (prepay gate). Clients should treat an + unrecognized value as an opaque reason string. + "repo_hint": "str", # Optional. Optional repository hint + supplied at session creation (first spec.workspace.repos entry of the + manifest). Not authoritative; the agent may change repos in-session. + "resume_on_topoff": bool, # Optional. Session consents to + being resumed automatically when the team's prepayment balance is + restored after a low-balance pause. Defaults to false at create time; may + be toggled later via PATCH /v2/agents/sessions/{session_id} + (resume_on_topoff only). Only a session paused with reason low_balance + whose latest run did not complete is ever resumed automatically - a + session the customer paused themselves, or one that idled out, stays + paused regardless. Not an environment-spec field, and forks do not + inherit it. Omitted when false. + "sandbox_id": "str", # Optional. Sandbox identity for + support / debugging. Informational; omitted until a sandbox is allocated. + "session_id": "str", # Optional. The created child sessions, + each a full Session with parent_session_id and fork_id set (PROVISIONING + -> READY) and drivable exactly like any other session. + "status": "SESSION_STATUS_UNSPECIFIED", # Optional. Default + value is "SESSION_STATUS_UNSPECIFIED". SessionStatus is the lifecycle + state of the sandbox + agent pair. SESSION_STATUS_ALL is a ListSessions + status-filter value only -- no session is ever actually in this status. + SESSION_STATUS_LOCKED is reserved for internal use and never appears in + an end-user-facing response. Known values are: + "SESSION_STATUS_UNSPECIFIED", "SESSION_STATUS_PROVISIONING", + "SESSION_STATUS_READY", "SESSION_STATUS_DETACHED", + "SESSION_STATUS_DESTROYING", "SESSION_STATUS_DESTROYED", + "SESSION_STATUS_FAILED", "SESSION_STATUS_PAUSED", "SESSION_STATUS_ALL", + and "SESSION_STATUS_LOCKED". + "warnings": [ + "str" # Optional. Non-fatal advisories computed from + the manifest at create time: one entry per populated + accepted-but-not-yet-enforced section (flat names: secrets, model, + tools, memory, budget, max_lifetime, mounts; legacy manifests get the + corresponding spec.* paths), a deprecation entry when a + legacy-envelope manifest or an adapter alias (codex-cli, cursor-cli) + is used, and a heuristic entry per credential-looking spec.env value. + Populated on the CreateSession response only. + ] + } + ] + } + # response body for status code(s): 402, 403, 404, 409, 423, 501 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + + @overload + async def create_session_fork( + self, + session_id: str, + body: Optional[IO[bytes]] = None, + *, + content_type: str = "application/json", + **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """ForkSession creates child sessions from a checkpoint of this session. + + Each child boots a fresh sandbox from the checkpoint's machine-state capture and is drivable + exactly like any other session (input, HITL), with parent_session_id and fork_id set for + lineage. When from_checkpoint_id is omitted, an implicit checkpoint of the session "now" is + captured first and the children fork from that. All-or-nothing: on failure nothing is left + running. + + :param session_id: The session UUID. Required. + :type session_id: str + :param body: Optional; an empty or omitted body forks a single child from an implicit + checkpoint of "now". Default value is None. + :type body: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 200 + response == { + "sessions": [ + { + "agent_kind": "AGENT_KIND_UNSPECIFIED", # Optional. Default + value is "AGENT_KIND_UNSPECIFIED". AgentKind identifies what runs inside + the session sandbox. CreateSession accepts CLAUDE_CODE, OPENCODE, + CODEX_CLI, CURSOR_CLI, OPENAI_CODEX, LANGGRAPH, and CREWAI (the supported + set is overridable via HARNESS_SUPPORTED_AGENT_KINDS); other values are + rejected with 400. AGENT_KIND_OPENAI_CODEX is the OpenAI sandbox-provider + kind (DO runs codex exec-server; the agent loop lives at OpenAI). + AGENT_KIND_LANGGRAPH is the LangGraph framework adapter. + AGENT_KIND_CREWAI is the CrewAI Crew framework adapter: like LANGGRAPH + the template bakes no agent code, so spec.env must supply FRAMEWORK_REPO + and FRAMEWORK_REPO_SHA; a Crew kickoff is one-shot and exposes no HITL + surface. CREWAI is additionally gated per team and returns 400 for teams + it is not enabled for. Known values are: "AGENT_KIND_UNSPECIFIED", + "AGENT_KIND_CLAUDE_CODE", "AGENT_KIND_OPENCODE", "AGENT_KIND_CODEX_CLI", + "AGENT_KIND_CURSOR_CLI", "AGENT_KIND_NONE", "AGENT_KIND_CUSTOM", + "AGENT_KIND_HERMES", "AGENT_KIND_OPENAI_CODEX", "AGENT_KIND_LANGGRAPH", + and "AGENT_KIND_CREWAI". + "config_id": "str", # Optional. Durable Environment Config + UUID the session was created from (inline manifests persist a config too, + named after the session). Persisted and returned on create/get/list; + absent only for sessions created before config references were stored. + "created_at": "2020-02-20 00:00:00", # Optional. The created + child sessions, each a full Session with parent_session_id and fork_id + set (PROVISIONING -> READY) and drivable exactly like any other session. + "fork_from_checkpoint_id": "str", # Optional. Lineage: + harness-api checkpoint id (cp_:code:``) the child booted from at + fork time. Drives OHP inherited-history replay on attach. Omitted/empty + for root sessions and legacy forks created before this field was + persisted. + "fork_id": "str", # Optional. Lineage: branch label assigned + when this session was created by ForkSession (or rollback's internal + fork). Omitted/empty for root sessions. + "idle_timeout_seconds": 0, # Optional. This session's own + idle-pause timeout (manifest idle_timeout). Omitted when the session uses + the deployment-wide default. + "keep_warm": bool, # Optional. Session is exempt from idle + auto-pause (manifest keep_warm, or the agent-kind default: hermes + sessions keep warm). Pause requests against such a session are rejected + with 409. Omitted when false. + "last_event_at": "2020-02-20 00:00:00", # Optional. + Timestamp of the most recent event in this session's canonical stream. + Drives the inactivity policy that destroys idle sessions. + "name": "str", # Optional. User-assignable display name, + unique per team among non-terminal sessions. Usable in place of + session_id by clients (doctl / Console resolve it to the id via the + ListSessions ?name= filter; the {session_id} path stays UUID-only). + Supplied via the environment-spec metadata.name, or auto-generated + ("session-:code:``-:code:``") when omitted. + "openai_environment_id": "str", # Optional. OpenAI Agents + API environment id (env_"u2026), captured from the resolved + CODEX_ENVIRONMENT_ID guest env value. Populated only for + AGENT_KIND_OPENAI_CODEX sessions. Non-secret correlation metadata. + "openai_session_id": "str", # Optional. OpenAI Agents API + session id (sess_"u2026) this DO sandbox hosts. Populated only for + AGENT_KIND_OPENAI_CODEX sessions (empty otherwise). Non-secret + correlation metadata; a client (doctl agents attach) reads it to bridge + to the OpenAI session. See docs/design/openai-sandbox-provider.md. + "parent_session_id": "str", # Optional. Lineage: id of the + session this one was forked from. Omitted/empty for root (non-fork) + sessions. + "pause_reason": "str", # Optional. Why this session is + paused. Present only when status is SESSION_STATUS_PAUSED; omitted + otherwise. Known values: manual (PauseSession RPC), idle (inactivity + auto-pause), low_balance (prepay gate). Clients should treat an + unrecognized value as an opaque reason string. + "repo_hint": "str", # Optional. Optional repository hint + supplied at session creation (first spec.workspace.repos entry of the + manifest). Not authoritative; the agent may change repos in-session. + "resume_on_topoff": bool, # Optional. Session consents to + being resumed automatically when the team's prepayment balance is + restored after a low-balance pause. Defaults to false at create time; may + be toggled later via PATCH /v2/agents/sessions/{session_id} + (resume_on_topoff only). Only a session paused with reason low_balance + whose latest run did not complete is ever resumed automatically - a + session the customer paused themselves, or one that idled out, stays + paused regardless. Not an environment-spec field, and forks do not + inherit it. Omitted when false. + "sandbox_id": "str", # Optional. Sandbox identity for + support / debugging. Informational; omitted until a sandbox is allocated. + "session_id": "str", # Optional. The created child sessions, + each a full Session with parent_session_id and fork_id set (PROVISIONING + -> READY) and drivable exactly like any other session. + "status": "SESSION_STATUS_UNSPECIFIED", # Optional. Default + value is "SESSION_STATUS_UNSPECIFIED". SessionStatus is the lifecycle + state of the sandbox + agent pair. SESSION_STATUS_ALL is a ListSessions + status-filter value only -- no session is ever actually in this status. + SESSION_STATUS_LOCKED is reserved for internal use and never appears in + an end-user-facing response. Known values are: + "SESSION_STATUS_UNSPECIFIED", "SESSION_STATUS_PROVISIONING", + "SESSION_STATUS_READY", "SESSION_STATUS_DETACHED", + "SESSION_STATUS_DESTROYING", "SESSION_STATUS_DESTROYED", + "SESSION_STATUS_FAILED", "SESSION_STATUS_PAUSED", "SESSION_STATUS_ALL", + and "SESSION_STATUS_LOCKED". + "warnings": [ + "str" # Optional. Non-fatal advisories computed from + the manifest at create time: one entry per populated + accepted-but-not-yet-enforced section (flat names: secrets, model, + tools, memory, budget, max_lifetime, mounts; legacy manifests get the + corresponding spec.* paths), a deprecation entry when a + legacy-envelope manifest or an adapter alias (codex-cli, cursor-cli) + is used, and a heuristic entry per credential-looking spec.env value. + Populated on the CreateSession response only. + ] + } + ] + } + # response body for status code(s): 402, 403, 404, 409, 423, 501 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + + @distributed_trace_async + async def create_session_fork( + self, + session_id: str, + body: Optional[Union[JSON, IO[bytes]]] = None, + **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """ForkSession creates child sessions from a checkpoint of this session. + + Each child boots a fresh sandbox from the checkpoint's machine-state capture and is drivable + exactly like any other session (input, HITL), with parent_session_id and fork_id set for + lineage. When from_checkpoint_id is omitted, an implicit checkpoint of the session "now" is + captured first and the children fork from that. All-or-nothing: on failure nothing is left + running. + + :param session_id: The session UUID. Required. + :type session_id: str + :param body: Optional; an empty or omitted body forks a single child from an implicit + checkpoint of "now". Is either a JSON type or a IO[bytes] type. Default value is None. + :type body: JSON or IO[bytes] + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # JSON input template you can fill out and use as your body input. + body = { + "count": 1, # Optional. Default value is 1. Number of child sessions to + create, 1-4. Defaults to 1 when omitted; a value outside 1-4 is rejected with + 400. + "from_checkpoint_id": "str" # Optional. Checkpoint to fork from; must be + READY. Omitted/empty means capture an implicit checkpoint of the session "now" + first and fork from that. + } + + # response body for status code(s): 200 + response == { + "sessions": [ + { + "agent_kind": "AGENT_KIND_UNSPECIFIED", # Optional. Default + value is "AGENT_KIND_UNSPECIFIED". AgentKind identifies what runs inside + the session sandbox. CreateSession accepts CLAUDE_CODE, OPENCODE, + CODEX_CLI, CURSOR_CLI, OPENAI_CODEX, LANGGRAPH, and CREWAI (the supported + set is overridable via HARNESS_SUPPORTED_AGENT_KINDS); other values are + rejected with 400. AGENT_KIND_OPENAI_CODEX is the OpenAI sandbox-provider + kind (DO runs codex exec-server; the agent loop lives at OpenAI). + AGENT_KIND_LANGGRAPH is the LangGraph framework adapter. + AGENT_KIND_CREWAI is the CrewAI Crew framework adapter: like LANGGRAPH + the template bakes no agent code, so spec.env must supply FRAMEWORK_REPO + and FRAMEWORK_REPO_SHA; a Crew kickoff is one-shot and exposes no HITL + surface. CREWAI is additionally gated per team and returns 400 for teams + it is not enabled for. Known values are: "AGENT_KIND_UNSPECIFIED", + "AGENT_KIND_CLAUDE_CODE", "AGENT_KIND_OPENCODE", "AGENT_KIND_CODEX_CLI", + "AGENT_KIND_CURSOR_CLI", "AGENT_KIND_NONE", "AGENT_KIND_CUSTOM", + "AGENT_KIND_HERMES", "AGENT_KIND_OPENAI_CODEX", "AGENT_KIND_LANGGRAPH", + and "AGENT_KIND_CREWAI". + "config_id": "str", # Optional. Durable Environment Config + UUID the session was created from (inline manifests persist a config too, + named after the session). Persisted and returned on create/get/list; + absent only for sessions created before config references were stored. + "created_at": "2020-02-20 00:00:00", # Optional. The created + child sessions, each a full Session with parent_session_id and fork_id + set (PROVISIONING -> READY) and drivable exactly like any other session. + "fork_from_checkpoint_id": "str", # Optional. Lineage: + harness-api checkpoint id (cp_:code:``) the child booted from at + fork time. Drives OHP inherited-history replay on attach. Omitted/empty + for root sessions and legacy forks created before this field was + persisted. + "fork_id": "str", # Optional. Lineage: branch label assigned + when this session was created by ForkSession (or rollback's internal + fork). Omitted/empty for root sessions. + "idle_timeout_seconds": 0, # Optional. This session's own + idle-pause timeout (manifest idle_timeout). Omitted when the session uses + the deployment-wide default. + "keep_warm": bool, # Optional. Session is exempt from idle + auto-pause (manifest keep_warm, or the agent-kind default: hermes + sessions keep warm). Pause requests against such a session are rejected + with 409. Omitted when false. + "last_event_at": "2020-02-20 00:00:00", # Optional. + Timestamp of the most recent event in this session's canonical stream. + Drives the inactivity policy that destroys idle sessions. + "name": "str", # Optional. User-assignable display name, + unique per team among non-terminal sessions. Usable in place of + session_id by clients (doctl / Console resolve it to the id via the + ListSessions ?name= filter; the {session_id} path stays UUID-only). + Supplied via the environment-spec metadata.name, or auto-generated + ("session-:code:``-:code:``") when omitted. + "openai_environment_id": "str", # Optional. OpenAI Agents + API environment id (env_"u2026), captured from the resolved + CODEX_ENVIRONMENT_ID guest env value. Populated only for + AGENT_KIND_OPENAI_CODEX sessions. Non-secret correlation metadata. + "openai_session_id": "str", # Optional. OpenAI Agents API + session id (sess_"u2026) this DO sandbox hosts. Populated only for + AGENT_KIND_OPENAI_CODEX sessions (empty otherwise). Non-secret + correlation metadata; a client (doctl agents attach) reads it to bridge + to the OpenAI session. See docs/design/openai-sandbox-provider.md. + "parent_session_id": "str", # Optional. Lineage: id of the + session this one was forked from. Omitted/empty for root (non-fork) + sessions. + "pause_reason": "str", # Optional. Why this session is + paused. Present only when status is SESSION_STATUS_PAUSED; omitted + otherwise. Known values: manual (PauseSession RPC), idle (inactivity + auto-pause), low_balance (prepay gate). Clients should treat an + unrecognized value as an opaque reason string. + "repo_hint": "str", # Optional. Optional repository hint + supplied at session creation (first spec.workspace.repos entry of the + manifest). Not authoritative; the agent may change repos in-session. + "resume_on_topoff": bool, # Optional. Session consents to + being resumed automatically when the team's prepayment balance is + restored after a low-balance pause. Defaults to false at create time; may + be toggled later via PATCH /v2/agents/sessions/{session_id} + (resume_on_topoff only). Only a session paused with reason low_balance + whose latest run did not complete is ever resumed automatically - a + session the customer paused themselves, or one that idled out, stays + paused regardless. Not an environment-spec field, and forks do not + inherit it. Omitted when false. + "sandbox_id": "str", # Optional. Sandbox identity for + support / debugging. Informational; omitted until a sandbox is allocated. + "session_id": "str", # Optional. The created child sessions, + each a full Session with parent_session_id and fork_id set (PROVISIONING + -> READY) and drivable exactly like any other session. + "status": "SESSION_STATUS_UNSPECIFIED", # Optional. Default + value is "SESSION_STATUS_UNSPECIFIED". SessionStatus is the lifecycle + state of the sandbox + agent pair. SESSION_STATUS_ALL is a ListSessions + status-filter value only -- no session is ever actually in this status. + SESSION_STATUS_LOCKED is reserved for internal use and never appears in + an end-user-facing response. Known values are: + "SESSION_STATUS_UNSPECIFIED", "SESSION_STATUS_PROVISIONING", + "SESSION_STATUS_READY", "SESSION_STATUS_DETACHED", + "SESSION_STATUS_DESTROYING", "SESSION_STATUS_DESTROYED", + "SESSION_STATUS_FAILED", "SESSION_STATUS_PAUSED", "SESSION_STATUS_ALL", + and "SESSION_STATUS_LOCKED". + "warnings": [ + "str" # Optional. Non-fatal advisories computed from + the manifest at create time: one entry per populated + accepted-but-not-yet-enforced section (flat names: secrets, model, + tools, memory, budget, max_lifetime, mounts; legacy manifests get the + corresponding spec.* paths), a deprecation entry when a + legacy-envelope manifest or an adapter alias (codex-cli, cursor-cli) + is used, and a heuristic entry per credential-looking spec.env value. + Populated on the CreateSession response only. + ] + } + ] + } + # response body for status code(s): 402, 403, 404, 409, 423, 501 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop( + "content_type", _headers.pop("Content-Type", None) + ) + cls: ClsType[JSON] = kwargs.pop("cls", None) + + content_type = content_type or "application/json" + _json = None + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + if body is not None: + _json = body + else: + _json = None + + _request = build_harness_create_session_fork_request( + session_id=session_id, + content_type=content_type, + json=_json, + content=_content, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [200, 402, 403, 404, 409, 423, 501]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + response_headers = {} + if response.status_code == 200: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 402: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 403: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 404: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 409: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 423: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 501: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore + + return cast(JSON, deserialized) # type: ignore + + @overload + async def post_session_hitl( + self, + session_id: str, + request_id_parameter: str, + body: JSON, + *, + x_device_uuid: Optional[str] = None, + content_type: str = "application/json", + **kwargs: Any + ) -> Optional[JSON]: + # pylint: disable=line-too-long + """ResolveHITL records the user's decision on a pending HITL request. + + harness-api forwards the decision into the owning microVM via the inbound + runtime.v1.OHR.ResolveHITL RPC (dialed through the guest-fronting proxy), which unblocks the + gated agent action. The resolution is also echoed to all attached clients as a HITLResolved + event on StreamSession. Returns 204 No Content on success. + + :param session_id: The session UUID. Required. + :type session_id: str + :param request_id_parameter: The HITL request identifier. Required. + :type request_id_parameter: str + :param body: Required. + :type body: JSON + :keyword x_device_uuid: Optional hardware device id forwarded by doctl. Recorded on the + resulting HITL decision. Default value is None. + :paramtype x_device_uuid: str + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: JSON object or None + :rtype: JSON or None + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # JSON input template you can fill out and use as your body input. + body = { + "outcome": "HITL_OUTCOME_UNSPECIFIED", # Default value is + "HITL_OUTCOME_UNSPECIFIED". HITLOutcome is the user's decision on a HITL request. + DEFER backs off this attempt without treating it as a rejection. Known values + are: "HITL_OUTCOME_UNSPECIFIED", "HITL_OUTCOME_APPROVE", "HITL_OUTCOME_REJECT", + and "HITL_OUTCOME_DEFER". + "reason": "str", # Optional. Optional free-text reason. Captured in the + audit trail; not interpreted. + "source": "RESOLUTION_SOURCE_UNSPECIFIED" # Optional. Default value is + "RESOLUTION_SOURCE_UNSPECIFIED". How a HITL resolution was triggered. Defaults to + RESOLUTION_SOURCE_OUT_OF_BAND when omitted. Known values are: + "RESOLUTION_SOURCE_UNSPECIFIED", "RESOLUTION_SOURCE_INLINE_KEYSTROKE", and + "RESOLUTION_SOURCE_OUT_OF_BAND". + } + + # response body for status code(s): 400, 402, 403, 404, 423 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + + @overload + async def post_session_hitl( + self, + session_id: str, + request_id_parameter: str, + body: IO[bytes], + *, + x_device_uuid: Optional[str] = None, + content_type: str = "application/json", + **kwargs: Any + ) -> Optional[JSON]: + # pylint: disable=line-too-long + """ResolveHITL records the user's decision on a pending HITL request. + + harness-api forwards the decision into the owning microVM via the inbound + runtime.v1.OHR.ResolveHITL RPC (dialed through the guest-fronting proxy), which unblocks the + gated agent action. The resolution is also echoed to all attached clients as a HITLResolved + event on StreamSession. Returns 204 No Content on success. + + :param session_id: The session UUID. Required. + :type session_id: str + :param request_id_parameter: The HITL request identifier. Required. + :type request_id_parameter: str + :param body: Required. + :type body: IO[bytes] + :keyword x_device_uuid: Optional hardware device id forwarded by doctl. Recorded on the + resulting HITL decision. Default value is None. + :paramtype x_device_uuid: str + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: JSON object or None + :rtype: JSON or None + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 400, 402, 403, 404, 423 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + + @distributed_trace_async + async def post_session_hitl( + self, + session_id: str, + request_id_parameter: str, + body: Union[JSON, IO[bytes]], + *, + x_device_uuid: Optional[str] = None, + **kwargs: Any + ) -> Optional[JSON]: + # pylint: disable=line-too-long + """ResolveHITL records the user's decision on a pending HITL request. + + harness-api forwards the decision into the owning microVM via the inbound + runtime.v1.OHR.ResolveHITL RPC (dialed through the guest-fronting proxy), which unblocks the + gated agent action. The resolution is also echoed to all attached clients as a HITLResolved + event on StreamSession. Returns 204 No Content on success. + + :param session_id: The session UUID. Required. + :type session_id: str + :param request_id_parameter: The HITL request identifier. Required. + :type request_id_parameter: str + :param body: Is either a JSON type or a IO[bytes] type. Required. + :type body: JSON or IO[bytes] + :keyword x_device_uuid: Optional hardware device id forwarded by doctl. Recorded on the + resulting HITL decision. Default value is None. + :paramtype x_device_uuid: str + :return: JSON object or None + :rtype: JSON or None + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # JSON input template you can fill out and use as your body input. + body = { + "outcome": "HITL_OUTCOME_UNSPECIFIED", # Default value is + "HITL_OUTCOME_UNSPECIFIED". HITLOutcome is the user's decision on a HITL request. + DEFER backs off this attempt without treating it as a rejection. Known values + are: "HITL_OUTCOME_UNSPECIFIED", "HITL_OUTCOME_APPROVE", "HITL_OUTCOME_REJECT", + and "HITL_OUTCOME_DEFER". + "reason": "str", # Optional. Optional free-text reason. Captured in the + audit trail; not interpreted. + "source": "RESOLUTION_SOURCE_UNSPECIFIED" # Optional. Default value is + "RESOLUTION_SOURCE_UNSPECIFIED". How a HITL resolution was triggered. Defaults to + RESOLUTION_SOURCE_OUT_OF_BAND when omitted. Known values are: + "RESOLUTION_SOURCE_UNSPECIFIED", "RESOLUTION_SOURCE_INLINE_KEYSTROKE", and + "RESOLUTION_SOURCE_OUT_OF_BAND". + } + + # response body for status code(s): 400, 402, 403, 404, 423 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop( + "content_type", _headers.pop("Content-Type", None) + ) + cls: ClsType[Optional[JSON]] = kwargs.pop("cls", None) + + content_type = content_type or "application/json" + _json = None + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _json = body + + _request = build_harness_post_session_hitl_request( + session_id=session_id, + request_id_parameter=request_id_parameter, + x_device_uuid=x_device_uuid, + content_type=content_type, + json=_json, + content=_content, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [204, 400, 402, 403, 404, 423]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + deserialized = None + response_headers = {} + if response.status_code == 204: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.status_code == 400: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 402: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 403: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 404: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 423: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, deserialized, response_headers) # type: ignore + + return deserialized # type: ignore + + @overload + async def create_session_input( + self, + session_id: str, + body: JSON, + *, + content_type: str = "application/json", + **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """SendInput delivers a user chat message into the session. + + The agent observes it on its next turn and emits subsequent events via StreamSession. The input + is forwarded to the owning microVM's OHR inbound surface, which attributes it to a run and + returns the run id. + + :param session_id: The session UUID. Required. + :type session_id: str + :param body: Required. + :type body: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # JSON input template you can fill out and use as your body input. + body = { + "text": "str" # The user message to deliver into the session. Required and + non-empty. Required. + } + + # response body for status code(s): 200 + response == { + "run_id": "str" # Optional. The run id the input was attributed to. Clients + can correlate against the run.started event on the stream. May be empty if OHR + returned no run id. + } + # response body for status code(s): 400, 402, 403, 404, 409, 423 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + + @overload + async def create_session_input( + self, + session_id: str, + body: IO[bytes], + *, + content_type: str = "application/json", + **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """SendInput delivers a user chat message into the session. + + The agent observes it on its next turn and emits subsequent events via StreamSession. The input + is forwarded to the owning microVM's OHR inbound surface, which attributes it to a run and + returns the run id. + + :param session_id: The session UUID. Required. + :type session_id: str + :param body: Required. + :type body: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 200 + response == { + "run_id": "str" # Optional. The run id the input was attributed to. Clients + can correlate against the run.started event on the stream. May be empty if OHR + returned no run id. + } + # response body for status code(s): 400, 402, 403, 404, 409, 423 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + + @distributed_trace_async + async def create_session_input( + self, session_id: str, body: Union[JSON, IO[bytes]], **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """SendInput delivers a user chat message into the session. + + The agent observes it on its next turn and emits subsequent events via StreamSession. The input + is forwarded to the owning microVM's OHR inbound surface, which attributes it to a run and + returns the run id. + + :param session_id: The session UUID. Required. + :type session_id: str + :param body: Is either a JSON type or a IO[bytes] type. Required. + :type body: JSON or IO[bytes] + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # JSON input template you can fill out and use as your body input. + body = { + "text": "str" # The user message to deliver into the session. Required and + non-empty. Required. + } + + # response body for status code(s): 200 + response == { + "run_id": "str" # Optional. The run id the input was attributed to. Clients + can correlate against the run.started event on the stream. May be empty if OHR + returned no run id. + } + # response body for status code(s): 400, 402, 403, 404, 409, 423 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop( + "content_type", _headers.pop("Content-Type", None) + ) + cls: ClsType[JSON] = kwargs.pop("cls", None) + + content_type = content_type or "application/json" + _json = None + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _json = body + + _request = build_harness_create_session_input_request( + session_id=session_id, + content_type=content_type, + json=_json, + content=_content, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [200, 400, 402, 403, 404, 409, 423]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + response_headers = {} + if response.status_code == 200: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 400: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 402: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 403: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 404: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 409: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 423: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore + + return cast(JSON, deserialized) # type: ignore + + @distributed_trace_async + async def post_session_pause( + self, session_id: str, *, x_device_uuid: Optional[str] = None, **kwargs: Any + ) -> Optional[JSON]: + # pylint: disable=line-too-long + """PauseSession freezes the session's microVM sandbox. + + Supported on microvm-substrate sessions only; dewdrop/docker sessions return 501 Not + Implemented. Paused sessions count against quota. Clients learn state via + GetSession/ListSessions; no live session.updated event is emitted. + + :param session_id: The session UUID. Required. + :type session_id: str + :keyword x_device_uuid: Optional hardware device id forwarded by doctl. Recorded for audit. + Default value is None. + :paramtype x_device_uuid: str + :return: JSON object or None + :rtype: JSON or None + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 402, 403, 404, 409, 423, 501 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[Optional[JSON]] = kwargs.pop("cls", None) + + _request = build_harness_post_session_pause_request( + session_id=session_id, + x_device_uuid=x_device_uuid, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [204, 402, 403, 404, 409, 423, 501]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + deserialized = None + response_headers = {} + if response.status_code == 204: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.status_code == 402: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 403: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 404: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 409: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 423: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 501: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, deserialized, response_headers) # type: ignore + + return deserialized # type: ignore + + @distributed_trace_async + async def post_session_resume( + self, session_id: str, *, x_device_uuid: Optional[str] = None, **kwargs: Any + ) -> Optional[JSON]: + # pylint: disable=line-too-long + """ResumeSession explicitly thaws a PAUSED session's microVM sandbox. + + Only valid for microvm-substrate sessions; dewdrop/docker sessions return 501 Not Implemented. + Implicit resume also occurs when SendInput or ResolveHITL is called on a paused session. + Returns synchronously once the sandbox is ready. + + :param session_id: The session UUID. Required. + :type session_id: str + :keyword x_device_uuid: Optional hardware device id forwarded by doctl. Recorded for audit. + Default value is None. + :paramtype x_device_uuid: str + :return: JSON object or None + :rtype: JSON or None + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 402, 403, 404, 409, 423, 501 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[Optional[JSON]] = kwargs.pop("cls", None) + + _request = build_harness_post_session_resume_request( + session_id=session_id, + x_device_uuid=x_device_uuid, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [204, 402, 403, 404, 409, 423, 501]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + deserialized = None + response_headers = {} + if response.status_code == 204: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.status_code == 402: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 403: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 404: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 409: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 423: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 501: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, deserialized, response_headers) # type: ignore + + return deserialized # type: ignore + + @overload + async def post_session_sandbox_exec( + self, + session_id: str, + body: JSON, + *, + content_type: str = "application/json", + **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """ExecInSandbox runs a one-shot command inside the session's sandbox. + + Runs ``argv`` directly in the guest (no shell). Stdout/stderr are buffered and capped at 1 MiB + each; + truncation is indicated via response headers when present. A non-zero exit code is still HTTP + 200. + + :param session_id: The session UUID. Required. + :type session_id: str + :param body: Command to run inside the sandbox. Required. + :type body: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # JSON input template you can fill out and use as your body input. + body = { + "argv": [ + "str" # Program and arguments. Required and non-empty; run directly, + with no shell interpretation. Required. + ], + "stdin": bytes("bytes", encoding="utf-8"), # Optional. Optional base64 stdin + payload, delivered in full and then closed so the process sees EOF. One-shot only + "u2014 there is no streaming stdin on this endpoint. + "timeout_seconds": 0, # Optional. Optional timeout. Clamped server-side + (default 60, maximum 240) regardless of the value sent. + "workdir": "str" # Optional. Working directory inside the sandbox. Empty + uses the sandbox default (/workspace); not restricted to the workspace root. + } + + # response body for status code(s): 200 + response == { + "duration_ms": 0, # Optional. Wall-clock duration the guest reported for the + command. + "exit_code": 0, # Optional. The guest process's exit code. Non-zero is still + a 200 response. + "stderr": "str", # Optional. Buffered stderr, capped at 1 MiB. Check + X-Exec-Stderr-Truncated for drops. + "stdout": "str" # Optional. Buffered stdout, capped at 1 MiB. Check + X-Exec-Stdout-Truncated for drops. + } + # response body for status code(s): 400, 402, 403, 404, 409, 423 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + + @overload + async def post_session_sandbox_exec( + self, + session_id: str, + body: IO[bytes], + *, + content_type: str = "application/json", + **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """ExecInSandbox runs a one-shot command inside the session's sandbox. + + Runs ``argv`` directly in the guest (no shell). Stdout/stderr are buffered and capped at 1 MiB + each; + truncation is indicated via response headers when present. A non-zero exit code is still HTTP + 200. + + :param session_id: The session UUID. Required. + :type session_id: str + :param body: Command to run inside the sandbox. Required. + :type body: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 200 + response == { + "duration_ms": 0, # Optional. Wall-clock duration the guest reported for the + command. + "exit_code": 0, # Optional. The guest process's exit code. Non-zero is still + a 200 response. + "stderr": "str", # Optional. Buffered stderr, capped at 1 MiB. Check + X-Exec-Stderr-Truncated for drops. + "stdout": "str" # Optional. Buffered stdout, capped at 1 MiB. Check + X-Exec-Stdout-Truncated for drops. + } + # response body for status code(s): 400, 402, 403, 404, 409, 423 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + + @distributed_trace_async + async def post_session_sandbox_exec( + self, session_id: str, body: Union[JSON, IO[bytes]], **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """ExecInSandbox runs a one-shot command inside the session's sandbox. + + Runs ``argv`` directly in the guest (no shell). Stdout/stderr are buffered and capped at 1 MiB + each; + truncation is indicated via response headers when present. A non-zero exit code is still HTTP + 200. + + :param session_id: The session UUID. Required. + :type session_id: str + :param body: Command to run inside the sandbox. Is either a JSON type or a IO[bytes] type. + Required. + :type body: JSON or IO[bytes] + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # JSON input template you can fill out and use as your body input. + body = { + "argv": [ + "str" # Program and arguments. Required and non-empty; run directly, + with no shell interpretation. Required. + ], + "stdin": bytes("bytes", encoding="utf-8"), # Optional. Optional base64 stdin + payload, delivered in full and then closed so the process sees EOF. One-shot only + "u2014 there is no streaming stdin on this endpoint. + "timeout_seconds": 0, # Optional. Optional timeout. Clamped server-side + (default 60, maximum 240) regardless of the value sent. + "workdir": "str" # Optional. Working directory inside the sandbox. Empty + uses the sandbox default (/workspace); not restricted to the workspace root. + } + + # response body for status code(s): 200 + response == { + "duration_ms": 0, # Optional. Wall-clock duration the guest reported for the + command. + "exit_code": 0, # Optional. The guest process's exit code. Non-zero is still + a 200 response. + "stderr": "str", # Optional. Buffered stderr, capped at 1 MiB. Check + X-Exec-Stderr-Truncated for drops. + "stdout": "str" # Optional. Buffered stdout, capped at 1 MiB. Check + X-Exec-Stdout-Truncated for drops. + } + # response body for status code(s): 400, 402, 403, 404, 409, 423 + response == { + "id": "str", # A short identifier corresponding to the HTTP status code + returned. For example, the ID for a response returning a 404 status code would + be "not_found.". Required. + "message": "str", # A message providing additional information about the + error, including details to help resolve it when possible. Required. + "request_id": "str" # Optional. Optionally, some endpoints may include a + request ID that should be provided when reporting bugs or opening support + tickets to help identify the issue. + } + """ + error_map: MutableMapping[int, Type[HttpResponseError]] = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: cast( + Type[HttpResponseError], + lambda response: ClientAuthenticationError(response=response), + ), + 429: HttpResponseError, + 500: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop( + "content_type", _headers.pop("Content-Type", None) + ) + cls: ClsType[JSON] = kwargs.pop("cls", None) + + content_type = content_type or "application/json" + _json = None + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _json = body + + _request = build_harness_post_session_sandbox_exec_request( + session_id=session_id, + content_type=content_type, + json=_json, + content=_content, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [200, 400, 402, 403, 404, 409, 423]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + response_headers = {} + if response.status_code == 200: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 400: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 402: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 403: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 404: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 409: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 423: + response_headers["ratelimit-limit"] = self._deserialize( + "int", response.headers.get("ratelimit-limit") + ) + response_headers["ratelimit-remaining"] = self._deserialize( + "int", response.headers.get("ratelimit-remaining") + ) + response_headers["ratelimit-reset"] = self._deserialize( + "int", response.headers.get("ratelimit-reset") + ) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, cast(JSON, deserialized), response_headers) # type: ignore + + return cast(JSON, deserialized) # type: ignore + + @distributed_trace_async + async def get_session_workspace_download( + self, + session_id: str, + *, + path: str, + as_archive: Optional[bool] = None, + **kwargs: Any + ) -> Union[AsyncIterator[bytes], JSON]: + # pylint: disable=line-too-long + """Download a file or tar archive from the session's sandbox workspace. + + Streams the workspace payload back as ``application/octet-stream`` using chunked transfer + encoding, followed by a fixed integrity footer. + + Response body layout: ```` + ``