feat(computer): add INFO-level lifecycle logging to booter implementations
Add [Computer] prefixed INFO logs to: - shipyard_neo.py: shutdown, upload_file, download_file, available - shipyard.py: shutdown, upload_file, download_file, available - boxlite.py: upload_file success path - computer_client.py: sync_skills_to_active_sandboxes, _sync_skills_to_sandbox Improves traceability of sandbox lifecycle events.
This commit is contained in:
@@ -64,6 +64,10 @@ class MockShipyardSandboxClient:
|
||||
async with aiohttp.ClientSession(timeout=timeout) as session:
|
||||
async with session.post(url, data=data) as response:
|
||||
if response.status == 200:
|
||||
logger.info(
|
||||
"[Computer] File uploaded to Boxlite sandbox: %s",
|
||||
remote_path,
|
||||
)
|
||||
return {
|
||||
"success": True,
|
||||
"message": "File uploaded successfully",
|
||||
|
||||
@@ -31,7 +31,7 @@ class ShipyardBooter(ComputerBooter):
|
||||
self._ship = ship
|
||||
|
||||
async def shutdown(self) -> None:
|
||||
pass
|
||||
logger.info("[Computer] Shipyard booter shutdown.")
|
||||
|
||||
@property
|
||||
def fs(self) -> FileSystemComponent:
|
||||
@@ -47,11 +47,19 @@ class ShipyardBooter(ComputerBooter):
|
||||
|
||||
async def upload_file(self, path: str, file_name: str) -> dict:
|
||||
"""Upload file to sandbox"""
|
||||
return await self._ship.upload_file(path, file_name)
|
||||
result = await self._ship.upload_file(path, file_name)
|
||||
logger.info("[Computer] File uploaded to Shipyard sandbox: %s", file_name)
|
||||
return result
|
||||
|
||||
async def download_file(self, remote_path: str, local_path: str):
|
||||
"""Download file from sandbox."""
|
||||
return await self._ship.download_file(remote_path, local_path)
|
||||
result = await self._ship.download_file(remote_path, local_path)
|
||||
logger.info(
|
||||
"[Computer] File downloaded from Shipyard sandbox: %s -> %s",
|
||||
remote_path,
|
||||
local_path,
|
||||
)
|
||||
return result
|
||||
|
||||
async def available(self) -> bool:
|
||||
"""Check if the sandbox is available."""
|
||||
@@ -59,8 +67,17 @@ class ShipyardBooter(ComputerBooter):
|
||||
ship_id = self._ship.id
|
||||
data = await self._sandbox_client.get_ship(ship_id)
|
||||
if not data:
|
||||
logger.info(
|
||||
"[Computer] Shipyard sandbox health check: id=%s, healthy=False (no data)",
|
||||
ship_id,
|
||||
)
|
||||
return False
|
||||
health = bool(data.get("status", 0) == 1)
|
||||
logger.info(
|
||||
"[Computer] Shipyard sandbox health check: id=%s, healthy=%s",
|
||||
ship_id,
|
||||
health,
|
||||
)
|
||||
return health
|
||||
except Exception as e:
|
||||
logger.error(f"Error checking Shipyard sandbox availability: {e}")
|
||||
|
||||
@@ -305,9 +305,14 @@ class ShipyardNeoBooter(ComputerBooter):
|
||||
|
||||
async def shutdown(self) -> None:
|
||||
if self._client is not None:
|
||||
sandbox_id = getattr(self._sandbox, "id", "unknown")
|
||||
logger.info(
|
||||
"[Computer] Shutting down Shipyard Neo sandbox: id=%s", sandbox_id
|
||||
)
|
||||
await self._client.__aexit__(None, None, None)
|
||||
self._client = None
|
||||
self._sandbox = None
|
||||
logger.info("[Computer] Shipyard Neo sandbox shut down: id=%s", sandbox_id)
|
||||
|
||||
@property
|
||||
def fs(self) -> FileSystemComponent:
|
||||
@@ -340,6 +345,7 @@ class ShipyardNeoBooter(ComputerBooter):
|
||||
content = f.read()
|
||||
remote_path = file_name.lstrip("/")
|
||||
await self._sandbox.filesystem.upload(remote_path, content)
|
||||
logger.info("[Computer] File uploaded to Neo sandbox: %s", remote_path)
|
||||
return {
|
||||
"success": True,
|
||||
"message": "File uploaded successfully",
|
||||
@@ -355,6 +361,11 @@ class ShipyardNeoBooter(ComputerBooter):
|
||||
os.makedirs(local_dir, exist_ok=True)
|
||||
with open(local_path, "wb") as f:
|
||||
f.write(cast(bytes, content))
|
||||
logger.info(
|
||||
"[Computer] File downloaded from Neo sandbox: %s -> %s",
|
||||
remote_path,
|
||||
local_path,
|
||||
)
|
||||
|
||||
async def available(self) -> bool:
|
||||
if self._sandbox is None:
|
||||
@@ -362,7 +373,14 @@ class ShipyardNeoBooter(ComputerBooter):
|
||||
try:
|
||||
await self._sandbox.refresh()
|
||||
status = getattr(self._sandbox.status, "value", str(self._sandbox.status))
|
||||
return status not in {"failed", "expired"}
|
||||
healthy = status not in {"failed", "expired"}
|
||||
logger.info(
|
||||
"[Computer] Neo sandbox health check: id=%s, status=%s, healthy=%s",
|
||||
getattr(self._sandbox, "id", "unknown"),
|
||||
status,
|
||||
healthy,
|
||||
)
|
||||
return healthy
|
||||
except Exception as e:
|
||||
logger.error(f"Error checking Shipyard Neo sandbox availability: {e}")
|
||||
return False
|
||||
|
||||
@@ -231,6 +231,11 @@ async def _sync_skills_to_sandbox(booter: ComputerBooter) -> None:
|
||||
)
|
||||
payload = _decode_sync_payload(str(sync_result.get("stdout", "") or ""))
|
||||
_update_sandbox_skills_cache(payload)
|
||||
managed = payload.get("managed_skills", []) if isinstance(payload, dict) else []
|
||||
logger.info(
|
||||
"[Computer] Sandbox skill sync complete: managed=%d",
|
||||
len(managed),
|
||||
)
|
||||
finally:
|
||||
if zip_path.exists():
|
||||
try:
|
||||
@@ -255,6 +260,9 @@ async def get_booter(
|
||||
session_booter.pop(session_id, None)
|
||||
if session_id not in session_booter:
|
||||
uuid_str = uuid.uuid5(uuid.NAMESPACE_DNS, session_id).hex
|
||||
logger.info(
|
||||
f"[Computer] Initializing booter: type={booter_type}, session={session_id}"
|
||||
)
|
||||
if booter_type == "shipyard":
|
||||
from .booters.shipyard import ShipyardBooter
|
||||
|
||||
@@ -273,6 +281,9 @@ async def get_booter(
|
||||
token = sandbox_cfg.get("shipyard_neo_access_token", "")
|
||||
ttl = sandbox_cfg.get("shipyard_neo_ttl", 3600)
|
||||
profile = sandbox_cfg.get("shipyard_neo_profile", "python-default")
|
||||
logger.info(
|
||||
f"[Computer] Shipyard Neo config: endpoint={ep}, profile={profile}, ttl={ttl}"
|
||||
)
|
||||
client = ShipyardNeoBooter(
|
||||
endpoint_url=ep,
|
||||
access_token=token,
|
||||
@@ -288,6 +299,9 @@ async def get_booter(
|
||||
|
||||
try:
|
||||
await client.boot(uuid_str)
|
||||
logger.info(
|
||||
f"[Computer] Sandbox booted successfully: type={booter_type}, session={session_id}"
|
||||
)
|
||||
await _sync_skills_to_sandbox(client)
|
||||
except Exception as e:
|
||||
logger.error(f"Error booting sandbox for session {session_id}: {e}")
|
||||
@@ -299,6 +313,9 @@ async def get_booter(
|
||||
|
||||
async def sync_skills_to_active_sandboxes() -> None:
|
||||
"""Best-effort skills synchronization for all active sandbox sessions."""
|
||||
logger.info(
|
||||
"[Computer] Syncing skills to %d active sandbox(es)", len(session_booter)
|
||||
)
|
||||
for session_id, booter in list(session_booter.items()):
|
||||
try:
|
||||
if not await booter.available():
|
||||
|
||||
Reference in New Issue
Block a user