Files
OpenHands/openhands/app_server/sandbox/sandbox_router.py
Tim O'Farrell 409df1287d Fix api key access (#13064)
Co-authored-by: openhands <openhands@all-hands.dev>
2026-02-26 06:22:23 -07:00

97 lines
3.1 KiB
Python

"""Runtime Containers router for OpenHands App Server."""
from typing import Annotated
from fastapi import APIRouter, HTTPException, Query, status
from openhands.agent_server.models import Success
from openhands.app_server.config import depends_sandbox_service
from openhands.app_server.sandbox.sandbox_models import SandboxInfo, SandboxPage
from openhands.app_server.sandbox.sandbox_service import (
SandboxService,
)
from openhands.server.dependencies import get_dependencies
# We use the get_dependencies method here to signal to the OpenAPI docs that this endpoint
# is protected. The actual protection is provided by SetAuthCookieMiddleware
router = APIRouter(
prefix='/sandboxes', tags=['Sandbox'], dependencies=get_dependencies()
)
sandbox_service_dependency = depends_sandbox_service()
# Read methods
@router.get('/search')
async def search_sandboxes(
page_id: Annotated[
str | None,
Query(title='Optional next_page_id from the previously returned page'),
] = None,
limit: Annotated[
int,
Query(title='The max number of results in the page', gt=0, lte=100),
] = 100,
sandbox_service: SandboxService = sandbox_service_dependency,
) -> SandboxPage:
"""Search / list sandboxes owned by the current user."""
assert limit > 0
assert limit <= 100
return await sandbox_service.search_sandboxes(page_id=page_id, limit=limit)
@router.get('')
async def batch_get_sandboxes(
id: Annotated[list[str], Query()],
sandbox_service: SandboxService = sandbox_service_dependency,
) -> list[SandboxInfo | None]:
"""Get a batch of sandboxes given their ids, returning null for any missing."""
assert len(id) < 100
sandboxes = await sandbox_service.batch_get_sandboxes(id)
return sandboxes
# Write Methods
@router.post('')
async def start_sandbox(
sandbox_spec_id: str | None = None,
sandbox_service: SandboxService = sandbox_service_dependency,
) -> SandboxInfo:
info = await sandbox_service.start_sandbox(sandbox_spec_id)
return info
@router.post('/{sandbox_id}/pause', responses={404: {'description': 'Item not found'}})
async def pause_sandbox(
sandbox_id: str,
sandbox_service: SandboxService = sandbox_service_dependency,
) -> Success:
exists = await sandbox_service.pause_sandbox(sandbox_id)
if not exists:
raise HTTPException(status.HTTP_404_NOT_FOUND)
return Success()
@router.post('/{sandbox_id}/resume', responses={404: {'description': 'Item not found'}})
async def resume_sandbox(
sandbox_id: str,
sandbox_service: SandboxService = sandbox_service_dependency,
) -> Success:
exists = await sandbox_service.resume_sandbox(sandbox_id)
if not exists:
raise HTTPException(status.HTTP_404_NOT_FOUND)
return Success()
@router.delete('/{id}', responses={404: {'description': 'Item not found'}})
async def delete_sandbox(
sandbox_id: str,
sandbox_service: SandboxService = sandbox_service_dependency,
) -> Success:
exists = await sandbox_service.delete_sandbox(sandbox_id)
if not exists:
raise HTTPException(status.HTTP_404_NOT_FOUND)
return Success()