Start the `langgraph dev` server and wait for it to be healthy. Args: timeout: Max seconds to wait for the server to become healthy. Raises: RuntimeError: If the server fails to start or become healthy.
(
self,
*,
timeout: float = _HEALTH_TIMEOUT, # noqa: ASYNC109
)
| 441 | return "" |
| 442 | |
| 443 | async def start( |
| 444 | self, |
| 445 | *, |
| 446 | timeout: float = _HEALTH_TIMEOUT, # noqa: ASYNC109 |
| 447 | ) -> None: |
| 448 | """Start the `langgraph dev` server and wait for it to be healthy. |
| 449 | |
| 450 | Args: |
| 451 | timeout: Max seconds to wait for the server to become healthy. |
| 452 | |
| 453 | Raises: |
| 454 | RuntimeError: If the server fails to start or become healthy. |
| 455 | """ |
| 456 | if self.running: |
| 457 | return |
| 458 | |
| 459 | work_dir = self.config_dir |
| 460 | if work_dir is None: |
| 461 | self._temp_dir = tempfile.TemporaryDirectory(prefix="deepagents_server_") |
| 462 | work_dir = Path(self._temp_dir.name) |
| 463 | |
| 464 | config_path = work_dir / "langgraph.json" |
| 465 | if not config_path.exists() and self._scaffold is not None: |
| 466 | # The config can vanish between the initial boot and a later |
| 467 | # `/restart` (e.g. the OS tmp reaper purging the temp work dir). |
| 468 | # Rebuild it rather than failing the restart. |
| 469 | logger.info("langgraph.json missing in %s; rescaffolding", work_dir) |
| 470 | try: |
| 471 | work_dir.mkdir(parents=True, exist_ok=True) |
| 472 | self._scaffold(work_dir) |
| 473 | except OSError as exc: |
| 474 | # Surface the failure with restart context instead of letting a |
| 475 | # bare OSError (e.g. ENOSPC/EACCES on a degraded temp fs) escape |
| 476 | # stripped of the recovery framing. Chained so the root cause |
| 477 | # stays in the traceback. |
| 478 | msg = f"Failed to rescaffold server workspace at {work_dir}: {exc}" |
| 479 | raise RuntimeError(msg) from exc |
| 480 | if not config_path.exists(): |
| 481 | if self._scaffold is not None: |
| 482 | # The scaffold hook ran but produced no langgraph.json (a silent |
| 483 | # no-op or a write to the wrong path). The "call |
| 484 | # generate_langgraph_json() first" advice below would misdirect, |
| 485 | # since the scaffold is exactly that call run internally. |
| 486 | contents = sorted(p.name for p in work_dir.iterdir()) |
| 487 | msg = ( |
| 488 | f"Rescaffolding {work_dir} did not produce langgraph.json " |
| 489 | f"(directory contents: {contents})." |
| 490 | ) |
| 491 | else: |
| 492 | msg = ( |
| 493 | f"langgraph.json not found in {work_dir}. " |
| 494 | "Call generate_langgraph_json() first." |
| 495 | ) |
| 496 | raise RuntimeError(msg) |
| 497 | |
| 498 | if self.port == _EPHEMERAL_PORT: |
| 499 | self.port = _find_free_port(self.host) |
| 500 | logger.info("Using ephemeral port %d for langgraph dev server", self.port) |