From 1d81c52950cd540b316d24ebdeb3c6df484aff35 Mon Sep 17 00:00:00 2001 From: zenfun Date: Mon, 16 Feb 2026 02:37:14 +0800 Subject: [PATCH] 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. --- astrbot/core/computer/booters/boxlite.py | 4 ++++ astrbot/core/computer/booters/shipyard.py | 23 ++++++++++++++++--- astrbot/core/computer/booters/shipyard_neo.py | 20 +++++++++++++++- astrbot/core/computer/computer_client.py | 17 ++++++++++++++ 4 files changed, 60 insertions(+), 4 deletions(-) diff --git a/astrbot/core/computer/booters/boxlite.py b/astrbot/core/computer/booters/boxlite.py index 373f6cee0..70064fdd4 100644 --- a/astrbot/core/computer/booters/boxlite.py +++ b/astrbot/core/computer/booters/boxlite.py @@ -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", diff --git a/astrbot/core/computer/booters/shipyard.py b/astrbot/core/computer/booters/shipyard.py index b13503431..6379d1e48 100644 --- a/astrbot/core/computer/booters/shipyard.py +++ b/astrbot/core/computer/booters/shipyard.py @@ -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}") diff --git a/astrbot/core/computer/booters/shipyard_neo.py b/astrbot/core/computer/booters/shipyard_neo.py index 1021c6df6..d43a4a7f7 100644 --- a/astrbot/core/computer/booters/shipyard_neo.py +++ b/astrbot/core/computer/booters/shipyard_neo.py @@ -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 diff --git a/astrbot/core/computer/computer_client.py b/astrbot/core/computer/computer_client.py index bf698b941..489078b5a 100644 --- a/astrbot/core/computer/computer_client.py +++ b/astrbot/core/computer/computer_client.py @@ -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():