108108from ...types .shared_params .launch_parameters import LaunchParameters
109109from ...types .devbox_async_execution_detail_view import DevboxAsyncExecutionDetailView
110110from ...types .shared_params .code_mount_parameters import CodeMountParameters
111+ from ...types .shared .launch_parameters import LaunchParameters as SharedLaunchParameters
111112
112113__all__ = ["DevboxesResource" , "AsyncDevboxesResource" ]
113114
114- DEVBOX_BOOTING_STATES = frozenset (('provisioning' , 'initializing' ))
115+ DEVBOX_BOOTING_STATES = frozenset (("provisioning" , "initializing" ))
116+
115117
116118class DevboxesResource (SyncAPIResource ):
117119 @cached_property
@@ -357,7 +359,7 @@ def update(
357359 def await_running (
358360 self ,
359361 id : str ,
360- * ,
362+ * ,
361363 polling_config : PollingConfig | None = None ,
362364 # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
363365 # The extra values given here take precedence over values defined on the client or passed to this method.
@@ -367,7 +369,7 @@ def await_running(
367369 timeout : float | httpx .Timeout | None | NotGiven = NOT_GIVEN ,
368370 ) -> DevboxView :
369371 """Wait for a devbox to be in running state.
370-
372+
371373 Args:
372374 id: The ID of the devbox to wait for
373375 config: Optional polling configuration
@@ -383,24 +385,43 @@ def await_running(
383385 PollingTimeout: If polling times out before devbox is running
384386 RunloopError: If devbox enters a non-running terminal state
385387 """
386- def retrieve_devbox () -> DevboxView :
387- return self .retrieve (
388- id ,
389- extra_headers = extra_headers ,
390- extra_query = extra_query ,
391- extra_body = extra_body ,
392- timeout = timeout
388+
389+ def wait_for_devbox_status () -> DevboxView :
390+ # This wait_for_status endpoint polls the devbox status for 10 seconds until it reaches either running or failure.
391+ # If it's neither, it will throw an error.
392+ return self ._post (
393+ f"/v1/devboxes/{ id } /wait_for_status" ,
394+ body = {"statuses" : ["running" , "failure" ]},
395+ options = make_request_options (
396+ extra_headers = extra_headers , extra_query = extra_query , extra_body = extra_body , timeout = timeout
397+ ),
398+ cast_to = DevboxView ,
393399 )
394400
401+ def handle_timeout_error (error : Exception ) -> DevboxView :
402+ # Handle 408 timeout errors by returning current devbox state to continue polling
403+ if isinstance (error , httpx .HTTPStatusError ) and error .response .status_code == 408 :
404+ # Return a placeholder result to continue polling
405+ return DevboxView (
406+ id = id ,
407+ status = "provisioning" ,
408+ capabilities = [],
409+ create_time_ms = 0 ,
410+ launch_parameters = SharedLaunchParameters (),
411+ metadata = {},
412+ state_transitions = [],
413+ )
414+ else :
415+ # Re-raise other errors to stop polling
416+ raise error
417+
395418 def is_done_booting (devbox : DevboxView ) -> bool :
396419 return devbox .status not in DEVBOX_BOOTING_STATES
397420
398- devbox = poll_until (retrieve_devbox , is_done_booting , polling_config )
421+ devbox = poll_until (wait_for_devbox_status , is_done_booting , polling_config , handle_timeout_error )
399422
400423 if devbox .status != "running" :
401- raise RunloopError (
402- f"Devbox entered non-running terminal state: { devbox .status } "
403- )
424+ raise RunloopError (f"Devbox entered non-running terminal state: { devbox .status } " )
404425
405426 return devbox
406427
@@ -427,7 +448,7 @@ def create_and_await_running(
427448 timeout : float | httpx .Timeout | None | NotGiven = NOT_GIVEN ,
428449 ) -> DevboxView :
429450 """Create a new devbox and wait for it to be in running state.
430-
451+
431452 Args:
432453 blueprint_id: The ID of the blueprint to use
433454 blueprint_name: The name of the blueprint to use
@@ -1660,7 +1681,7 @@ async def await_running(
16601681 timeout : float | httpx .Timeout | None | NotGiven = NOT_GIVEN ,
16611682 ) -> DevboxView :
16621683 """Wait for a devbox to be in running state.
1663-
1684+
16641685 Args:
16651686 id: The ID of the devbox to wait for
16661687 config: Optional polling configuration
@@ -1676,27 +1697,45 @@ async def await_running(
16761697 PollingTimeout: If polling times out before devbox is running
16771698 RunloopError: If devbox enters a non-running terminal state
16781699 """
1679- async def retrieve_devbox () -> DevboxView :
1680- return await self .retrieve (
1681- id ,
1682- extra_headers = extra_headers ,
1683- extra_query = extra_query ,
1684- extra_body = extra_body ,
1685- timeout = timeout
1686- )
1687-
1700+
1701+ async def wait_for_devbox_status () -> DevboxView :
1702+ # This wait_for_status endpoint polls the devbox status for 10 seconds until it reaches either running or failure.
1703+ # If it's neither, it will throw an error.
1704+ try :
1705+ return await self ._post (
1706+ f"/v1/devboxes/{ id } /wait_for_status" ,
1707+ body = {"statuses" : ["running" , "failure" ]},
1708+ options = make_request_options (
1709+ extra_headers = extra_headers , extra_query = extra_query , extra_body = extra_body , timeout = timeout
1710+ ),
1711+ cast_to = DevboxView ,
1712+ )
1713+ except httpx .HTTPStatusError as error :
1714+ if error .response .status_code == 408 :
1715+ # Handle 408 timeout errors by returning a placeholder result to continue polling
1716+ return DevboxView (
1717+ id = id ,
1718+ status = "provisioning" ,
1719+ capabilities = [],
1720+ create_time_ms = 0 ,
1721+ launch_parameters = SharedLaunchParameters (),
1722+ metadata = {},
1723+ state_transitions = [],
1724+ )
1725+ else :
1726+ # Re-raise other errors to stop polling
1727+ raise
1728+
16881729 def is_done_booting (devbox : DevboxView ) -> bool :
16891730 return devbox .status not in DEVBOX_BOOTING_STATES
16901731
1691- devbox = await async_poll_until (retrieve_devbox , is_done_booting , polling_config )
1732+ devbox = await async_poll_until (wait_for_devbox_status , is_done_booting , polling_config )
16921733
16931734 if devbox .status != "running" :
1694- raise RunloopError (
1695- f"Devbox entered non-running terminal state: { devbox .status } "
1696- )
1735+ raise RunloopError (f"Devbox entered non-running terminal state: { devbox .status } " )
16971736
16981737 return devbox
1699-
1738+
17001739 async def update (
17011740 self ,
17021741 id : str ,
0 commit comments