1717 async_to_streamed_response_wrapper ,
1818)
1919from ..._constants import DEFAULT_TIMEOUT
20+ from ..._exceptions import APIStatusError
2021from ...lib .polling import PollingConfig , poll_until
2122from ..._base_client import make_request_options
2223from ...types .devboxes import execution_retrieve_params , execution_execute_sync_params , execution_execute_async_params
@@ -105,7 +106,7 @@ def await_completed(
105106 timeout : float | httpx .Timeout | None | NotGiven = NOT_GIVEN ,
106107 ) -> DevboxAsyncExecutionDetailView :
107108 """Wait for an execution to complete.
108-
109+
109110 Args:
110111 execution_id: The ID of the execution to wait for
111112 id: The ID of the devbox
@@ -121,20 +122,37 @@ def await_completed(
121122 Raises:
122123 PollingTimeout: If polling times out before execution completes
123124 """
124- def retrieve_execution () -> DevboxAsyncExecutionDetailView :
125- return self .retrieve (
126- execution_id ,
127- devbox_id = devbox_id ,
128- extra_headers = extra_headers ,
129- extra_query = extra_query ,
130- extra_body = extra_body ,
131- timeout = timeout
125+
126+ def wait_for_execution_status () -> DevboxAsyncExecutionDetailView :
127+ # This wait_for_status endpoint polls the execution status for 60 seconds until it reaches either completed.
128+ return self ._post (
129+ f"/v1/devboxes/{ devbox_id } /executions/{ execution_id } /wait_for_status" ,
130+ body = {"statuses" : ["completed" ]},
131+ options = make_request_options (
132+ extra_headers = extra_headers , extra_query = extra_query , extra_body = extra_body , timeout = timeout
133+ ),
134+ cast_to = DevboxAsyncExecutionDetailView ,
132135 )
133136
137+ def handle_timeout_error (error : Exception ) -> DevboxAsyncExecutionDetailView :
138+ # Handle 408 timeout errors by returning current execution state to continue polling
139+ if isinstance (error , APIStatusError ) and error .response .status_code == 408 :
140+ # Return a placeholder result to continue polling
141+ return DevboxAsyncExecutionDetailView (
142+ devbox_id = devbox_id ,
143+ execution_id = execution_id ,
144+ status = "queued" ,
145+ stdout = "" ,
146+ stderr = "" ,
147+ )
148+ else :
149+ # Re-raise other errors to stop polling
150+ raise error
151+
134152 def is_done (execution : DevboxAsyncExecutionDetailView ) -> bool :
135- return execution .status == ' completed'
153+ return execution .status == " completed"
136154
137- return poll_until (retrieve_execution , is_done , config )
155+ return poll_until (wait_for_execution_status , is_done , config , handle_timeout_error )
138156
139157 def execute_async (
140158 self ,
@@ -369,7 +387,7 @@ async def retrieve(
369387
370388 async def await_completed (
371389 self ,
372- execution_id : str ,
390+ execution_id : str ,
373391 * ,
374392 devbox_id : str ,
375393 polling_config : PollingConfig | None = None ,
@@ -381,7 +399,7 @@ async def await_completed(
381399 timeout : float | httpx .Timeout | None | NotGiven = NOT_GIVEN ,
382400 ) -> DevboxAsyncExecutionDetailView :
383401 """Wait for an execution to complete.
384-
402+
385403 Args:
386404 execution_id: The ID of the execution to wait for
387405 id: The ID of the devbox
@@ -397,20 +415,35 @@ async def await_completed(
397415 Raises:
398416 PollingTimeout: If polling times out before execution completes
399417 """
400- async def retrieve_execution () -> DevboxAsyncExecutionDetailView :
401- return await self .retrieve (
402- execution_id ,
403- devbox_id = devbox_id ,
404- extra_headers = extra_headers ,
405- extra_query = extra_query ,
406- extra_body = extra_body ,
407- timeout = timeout ,
408- )
418+
419+ async def wait_for_execution_status () -> DevboxAsyncExecutionDetailView :
420+ try :
421+ return await self ._post (
422+ f"/v1/devboxes/{ devbox_id } /executions/{ execution_id } /wait_for_status" ,
423+ body = {"statuses" : ["completed" ]},
424+ options = make_request_options (
425+ extra_headers = extra_headers , extra_query = extra_query , extra_body = extra_body , timeout = timeout
426+ ),
427+ cast_to = DevboxAsyncExecutionDetailView ,
428+ )
429+ except APIStatusError as error :
430+ if error .response .status_code == 408 :
431+ # Handle 408 timeout errors by returning current execution state to continue polling
432+ return DevboxAsyncExecutionDetailView (
433+ devbox_id = devbox_id ,
434+ execution_id = execution_id ,
435+ status = "queued" ,
436+ stdout = "" ,
437+ stderr = "" ,
438+ )
439+ else :
440+ # Re-raise other errors to stop polling
441+ raise
409442
410443 def is_done (execution : DevboxAsyncExecutionDetailView ) -> bool :
411- return execution .status == ' completed'
444+ return execution .status == " completed"
412445
413- return await async_poll_until (retrieve_execution , is_done , polling_config )
446+ return await async_poll_until (wait_for_execution_status , is_done , polling_config )
414447
415448 async def execute_async (
416449 self ,
0 commit comments