diff --git a/apps/api/src/api/auth.py b/apps/api/src/api/auth.py index a3bcc42..d83a2cf 100644 --- a/apps/api/src/api/auth.py +++ b/apps/api/src/api/auth.py @@ -48,7 +48,7 @@ async def login(next: str = "/") -> RedirectResponse: redirect_uri=redirect_uri, state=state, ) - logger.info("Auth login initiated: redirect_uri=%s, next=%s", redirect_uri, next) + logger.debug("Auth login initiated: redirect_uri=%s, next=%s", redirect_uri, next) response = RedirectResponse(location) response.set_cookie("auth_state", state, httponly=True, samesite="lax") response.set_cookie("auth_next", next, httponly=True, samesite="lax") @@ -63,7 +63,7 @@ async def callback( auth_next: str | None = Cookie(default="/"), session: AsyncSession = Depends(get_db_session), ) -> RedirectResponse: - logger.info("Auth callback received: code=%s... state=%s", code[:10] if code else "None", state[:10] if state else "None") + logger.debug("Auth callback received: code=%s... state=%s", code[:10] if code else "None", state[:10] if state else "None") if auth_state is None or auth_state != state: logger.warning("State mismatch: cookie=%s, param=%s", auth_state, state) @@ -71,7 +71,7 @@ async def callback( settings = Settings() redirect_uri = f"{settings.api_base_url}/auth/callback" - logger.info("Exchanging code for tokens (redirect_uri=%s)", redirect_uri) + logger.debug("Exchanging code for tokens (redirect_uri=%s)", redirect_uri) async with httpx.AsyncClient() as client: try: @@ -92,7 +92,7 @@ async def callback( access_token=token_payload["access_token"], client=client, ) - logger.info("User info fetched successfully") + logger.debug("User info fetched successfully") except Exception as exc: logger.error("User info fetch failed: %s", exc) raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="failed to fetch user info") @@ -100,19 +100,19 @@ async def callback( authentik_id = str(user_info.get("sub", "")) email = str(user_info.get("email", f"{authentik_id}@authentik.local")) name = str(user_info.get("name", email)) - logger.info("User info: authentik_id=%s, email=%s, name=%s", authentik_id, email, name) + logger.debug("User info: authentik_id=%s, email=%s, name=%s", authentik_id, email, name) try: user = await session.scalar(select(User).where(User.authentik_id == authentik_id)) if user is None: - logger.info("Creating new user: authentik_id=%s", authentik_id) + logger.debug("Creating new user: authentik_id=%s", authentik_id) user = User(email=email, name=name, authentik_id=authentik_id, avatar_url=None) session.add(user) await session.commit() await session.refresh(user) logger.info("New user created: id=%s", user.id) else: - logger.info("Existing user found: id=%s, updating info", user.id) + logger.debug("Existing user found: id=%s, updating info", user.id) user.email = email user.name = name await session.commit() @@ -165,20 +165,20 @@ async def me( session_cookie: str | None = Cookie(default=None, alias="session"), session: AsyncSession = Depends(get_db_session), ) -> dict[str, Any]: - logger.info("Auth /me called, cookie present: %s", bool(session_cookie)) + logger.debug("Auth /me called, cookie present: %s", bool(session_cookie)) if not session_cookie: logger.warning("Auth /me: missing session cookie") raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="missing session") settings = Settings() - logger.info("Auth /me: cookie_domain=%s, cookie_secure=%s, cookie_samesite=%s", + logger.debug("Auth /me: cookie_domain=%s, cookie_secure=%s, cookie_samesite=%s", settings.cookie_domain, settings.cookie_secure, settings.cookie_samesite) try: payload = decode_session_cookie(settings=settings, cookie_value=session_cookie) user_id = payload["user_id"] - logger.info("Auth /me: decoded session for user_id=%s", user_id) + logger.debug("Auth /me: decoded session for user_id=%s", user_id) except ValueError as exc: logger.warning("Auth /me: invalid session: %s", exc) raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=str(exc)) diff --git a/apps/api/src/api/config_profiles.py b/apps/api/src/api/config_profiles.py index d36c71b..63bb7fe 100644 --- a/apps/api/src/api/config_profiles.py +++ b/apps/api/src/api/config_profiles.py @@ -420,7 +420,7 @@ async def create_config_profile( ) profile = result.scalar_one() - logger.info("Created config profile %s for user %s", profile.id, user_uuid) + logger.debug("Created config profile %s for user %s", profile.id, user_uuid) return _profile_to_response(profile) @@ -521,7 +521,7 @@ async def update_config_profile( ) profile = result.scalar_one() - logger.info("Updated config profile %s", profile.id) + logger.debug("Updated config profile %s", profile.id) return _profile_to_response(profile) @@ -541,7 +541,7 @@ async def delete_config_profile( await session.delete(profile) await session.commit() - logger.info("Deleted config profile %s", profile_id) + logger.debug("Deleted config profile %s", profile_id) return None @@ -626,7 +626,7 @@ async def update_profile_includes( ) direct_includes = inc_result.scalars().all() - logger.info("Updated includes for config profile %s", profile.id) + logger.debug("Updated includes for config profile %s", profile.id) return _profile_to_response(profile, list(direct_includes)) diff --git a/apps/api/src/api/terminal.py b/apps/api/src/api/terminal.py index c065d41..1c902cd 100644 --- a/apps/api/src/api/terminal.py +++ b/apps/api/src/api/terminal.py @@ -44,9 +44,9 @@ async def terminal_websocket( Returns: None. Communicates via WebSocket messages. """ - logger.info("Terminal WebSocket connection attempt for instance %s", instance_id) + logger.debug("Terminal WebSocket connection attempt for instance %s", instance_id) await websocket.accept() - logger.info("Terminal WebSocket accepted for instance %s", instance_id) + logger.debug("Terminal WebSocket accepted for instance %s", instance_id) try: # Parse instance_id @@ -80,13 +80,13 @@ async def terminal_websocket( await websocket.close(code=4004, reason="Instance not running") return - logger.info("Terminal auth passed for instance %s, user %s", instance_id, user_id) + logger.debug("Terminal auth passed for instance %s, user %s", instance_id, user_id) # Fetch tool type to get startup_command tool_type = await db_session.get(ToolType, instance.tool_type_id) startup_command = tool_type.startup_command if tool_type else None if startup_command: - logger.info("Using startup command for instance %s: %s", instance_id, startup_command) + logger.debug("Using startup command for instance %s: %s", instance_id, startup_command) # Get or create terminal session try: @@ -95,15 +95,15 @@ async def terminal_websocket( instance.container_id, startup_command=startup_command, ) - logger.info("Terminal session ready for instance %s (session_id=%s)", instance_id, session.session_id) + logger.debug("Terminal session ready for instance %s (session_id=%s)", instance_id, session.session_id) # Attach WebSocket to session await terminal_manager.attach_websocket(session, websocket) - logger.info("WebSocket attached to session for instance %s", instance_id) + logger.debug("WebSocket attached to session for instance %s", instance_id) # Send connected status await websocket.send_json({"type": "status", "status": "connected"}) - logger.info("Sent connected status for instance %s", instance_id) + logger.debug("Sent connected status for instance %s", instance_id) # Use mutable session reference so loops can survive reset session_ref = SessionRef(session) @@ -112,7 +112,7 @@ async def terminal_websocket( read_task = asyncio.create_task(_read_loop(session_ref, websocket)) write_task = asyncio.create_task(_write_loop(session_ref, websocket, instance_id)) heartbeat_task = asyncio.create_task(_heartbeat_loop(websocket)) - logger.info("Started terminal loops for instance %s", instance_id) + logger.debug("Started terminal loops for instance %s", instance_id) # Wait for either task to complete (indicating disconnect or error) done, pending = await asyncio.wait( @@ -120,7 +120,7 @@ async def terminal_websocket( return_when=asyncio.FIRST_COMPLETED, ) - logger.info("Terminal loop completed for instance %s, done=%s", instance_id, len(done)) + logger.debug("Terminal loop completed for instance %s, done=%s", instance_id, len(done)) # Cancel remaining tasks for task in pending: @@ -134,7 +134,7 @@ async def terminal_websocket( try: if 'session' in locals(): await terminal_manager.detach_websocket(session, websocket) - logger.info("WebSocket detached from session for instance %s", instance_id) + logger.debug("WebSocket detached from session for instance %s", instance_id) except Exception: pass @@ -183,11 +183,11 @@ async def _write_loop(session_ref: SessionRef, websocket, instance_id: str) -> N if msg_type == "resize": cols = ctrl.get("cols", 80) rows = ctrl.get("rows", 24) - logger.info(f"Received resize message for instance {instance_id}: {cols}x{rows}") + logger.debug(f"Received resize message for instance {instance_id}: {cols}x{rows}") await session.resize(cols, rows) elif msg_type == "reset": # Reset terminal session - logger.info("Resetting terminal session for instance %s", session.instance_id) + logger.debug("Resetting terminal session for instance %s", session.instance_id) await websocket.send_json({"type": "status", "status": "resetting"}) # Reset the session diff --git a/apps/api/src/api/tool_instances.py b/apps/api/src/api/tool_instances.py index 30bad13..97d08f0 100644 --- a/apps/api/src/api/tool_instances.py +++ b/apps/api/src/api/tool_instances.py @@ -120,7 +120,7 @@ async def _resolve_single_git_mount( ) return [] target_path = os.path.join(working_directory, target_path) - logger.info("Resolved relative target path to %s", target_path) + logger.debug("Resolved relative target path to %s", target_path) if not instance_dir: logger.warning("Git mount skipped: no instance_dir provided for cloning") @@ -143,7 +143,7 @@ async def _resolve_single_git_mount( os.path.dirname(clone_dir), branch or "main", ) - logger.info("Cloned git mount repository %s to %s", remote_url, repo_path) + logger.debug("Cloned git mount repository %s to %s", remote_url, repo_path) except Exception as exc: logger.warning("Clone failed for git mount %s: %s", remote_url, exc) return [] @@ -151,7 +151,7 @@ async def _resolve_single_git_mount( # Repo exists - pull latest updates try: await asyncio.to_thread(_pull_repository_updates, repo_path, remote_url) - logger.info("Pulled updates for git mount %s", remote_url) + logger.debug("Pulled updates for git mount %s", remote_url) except Exception as exc: logger.warning("Failed to pull updates for %s: %s", remote_url, exc) @@ -159,7 +159,7 @@ async def _resolve_single_git_mount( if branch and repo_path: success = await asyncio.to_thread(_checkout_branch, repo_path, branch) if success: - logger.info("Checked out branch %s for %s", branch, remote_url) + logger.debug("Checked out branch %s for %s", branch, remote_url) else: logger.warning( "Branch %s not found in %s, using current branch", @@ -198,7 +198,7 @@ async def _resolve_single_git_mount( "target": final_target, "type": "bind", }) - logger.info("Added git mount: %s -> %s (url: %s)", matched_path, final_target, remote_url) + logger.debug("Added git mount: %s -> %s (url: %s)", matched_path, final_target, remote_url) return volume_mounts @@ -461,7 +461,7 @@ async def create_instance( Returns: Dictionary with instance details. """ - logger.info( + logger.debug( "Creating instance: project_id=%s, repo_id=%s, tool_type_id=%s, display_name=%s", project_id, repo_id, @@ -555,7 +555,7 @@ async def create_instance( if not repo_contents or (len(repo_contents) == 1 and repo_contents[0] == ".git"): logger.error("Cloned repository at %s appears empty", repo_path) raise RuntimeError("Cloned repository is empty") - logger.info("Verified cloned repo at %s has %d items", repo_path, len(repo_contents)) + logger.debug("Verified cloned repo at %s has %d items", repo_path, len(repo_contents)) except Exception as exc: logger.exception("Failed to verify cloned repository: %s", exc) raise HTTPException( @@ -574,7 +574,7 @@ async def create_instance( if result.returncode != 0: logger.error("Failed to create branch %s: %s", data.new_branch, result.stderr) raise RuntimeError(f"Failed to create branch: {result.stderr}") - logger.info("Created local branch %s in cloned repository", data.new_branch) + logger.debug("Created local branch %s in cloned repository", data.new_branch) except Exception as exc: logger.exception("Failed to create local branch: %s", exc) raise HTTPException( @@ -895,7 +895,7 @@ async def start_instance( config_result = await session.execute(config_query) configs = config_result.scalars().all() - logger.info("Found %d tool configs for instance %s", len(configs), instance.id) + logger.debug("Found %d tool configs for instance %s", len(configs), instance.id) for config in configs: if config.config_type == "env": @@ -942,7 +942,7 @@ async def start_instance( working_directory = profile_hints["working_directory"] if profile_hints.get("port_override"): port_override = profile_hints["port_override"] - logger.info( + logger.debug( "Applied config profile %s to instance %s (env=%d, files=%d, mounts=%d, git_mounts=%d)", resolved.profile_name, instance.id, @@ -958,18 +958,18 @@ async def start_instance( detail=f"Config profile cycle detected: {exc}", ) else: - logger.info("No config profile selected for instance %s", instance.id) + logger.debug("No config profile selected for instance %s", instance.id) # Write env file and config files env_file_path = None if env_vars: env_file_path = write_env_file(instance_dir, env_vars) - logger.info("Wrote env file for instance %s: %s", instance.id, env_file_path) + logger.debug("Wrote env file for instance %s: %s", instance.id, env_file_path) if config_files: write_config_files(instance_dir, config_files) - logger.info("Wrote %d config files for instance %s", len(config_files), instance.id) + logger.debug("Wrote %d config files for instance %s", len(config_files), instance.id) # Mount SSH key for clone-mode instances if instance.clone_mode == "clone": @@ -984,21 +984,21 @@ async def start_instance( "target": "/root/.ssh", "type": "ro", }) - logger.info("Mounted SSH key for clone-mode instance %s", instance.id) + logger.debug("Mounted SSH key for clone-mode instance %s", instance.id) except Exception as exc: logger.error("Failed to prepare SSH key for instance %s: %s", instance.id, exc) # Modify compose file if needed (port override, start command, working dir, volumes) if port_override or start_command or working_directory or extra_volumes: _modify_compose_file(instance.compose_path, port_override, start_command, working_directory, extra_volumes) - logger.info("Modified compose file for instance %s", instance.id) + logger.debug("Modified compose file for instance %s", instance.id) # Execute docker compose up with env file - logger.info("Running docker compose up for instance %s (compose_path=%s)", instance.id, instance.compose_path) + logger.debug("Running docker compose up for instance %s (compose_path=%s)", instance.id, instance.compose_path) returncode, stdout, stderr = execute_compose_command( instance.compose_path, "up", env_file=env_file_path ) - logger.info("Docker compose up completed for instance %s: returncode=%d, stdout=%s, stderr=%s", + logger.debug("Docker compose up completed for instance %s: returncode=%d, stdout=%s, stderr=%s", instance.id, returncode, stdout[:200] if stdout else "", stderr[:500] if stderr else "") if returncode != 0: @@ -1014,18 +1014,18 @@ async def start_instance( container_id = get_container_id(instance.name) if container_id: instance.container_id = container_id - logger.info("Container ID for instance %s: %s", instance.id, container_id) + logger.debug("Container ID for instance %s: %s", instance.id, container_id) container_name = get_container_name(instance.name) if container_name: instance.container_name = container_name - logger.info("Container name for instance %s: %s", instance.id, container_name) + logger.debug("Container name for instance %s: %s", instance.id, container_name) # Connect container to backend network so API can reach it - logger.info("Connecting container %s to backend network...", container_name) + logger.debug("Connecting container %s to backend network...", container_name) connected = connect_container_to_network(container_name, "backend") if connected: - logger.info("Successfully connected %s to backend network", container_name) + logger.debug("Successfully connected %s to backend network", container_name) else: logger.warning("Failed to connect %s to backend network", container_name) @@ -1034,7 +1034,7 @@ async def start_instance( instance.status = "starting" instance.last_started_at = datetime.now() await session.commit() - logger.info("Instance %s: verifying container startup...", instance.id) + logger.debug("Instance %s: verifying container startup...", instance.id) startup_result = wait_for_container_running(instance.container_id, timeout=30, interval=2.0) @@ -1062,7 +1062,7 @@ async def start_instance( "logs": logs, } - logger.info( + logger.debug( "Instance %s container started successfully after %.1fs", instance.id, startup_result["waited_seconds"], @@ -1090,7 +1090,7 @@ async def start_instance( if probe_command: instance.status = "probing" await session.commit() - logger.info( + logger.debug( "Executing readiness probe for instance %s: command='%s', timeout=%d, interval=%d", instance.id, probe_command, probe_timeout, probe_interval ) @@ -1143,14 +1143,14 @@ async def start_instance( } instance_port = tool_type.default_port or 0 - logger.info("Tool type for instance %s: name=%s, default_port=%s, interface_type=%s", + logger.debug("Tool type for instance %s: name=%s, default_port=%s, interface_type=%s", instance.id, tool_type.name, instance_port, tool_type.interface_type) # Only create Cloudflare tunnel for web-enabled tools if tool_type.interface_type == "web": # Create temporary Cloudflare tunnel for public access try: - logger.info("Creating temporary tunnel for instance %s (container=%s, port=%d)", + logger.debug("Creating temporary tunnel for instance %s (container=%s, port=%d)", instance.id, instance.container_name, instance_port) tunnel_info = start_cloudflared_tunnel( container_name=instance.container_name or instance.name, @@ -1160,7 +1160,7 @@ async def start_instance( instance.public_url = tunnel_info["url"] instance.url = tunnel_info["url"] await session.commit() - logger.info( + logger.debug( "Created temporary tunnel for instance %s: pid=%s, url=%s", instance.id, tunnel_info["pid"], @@ -1230,7 +1230,7 @@ async def stop_instance( if instance.tunnel_id: try: stop_cloudflared_tunnel(instance.tunnel_id) - logger.info("Stopped tunnel for instance %s (pid=%s)", instance.id, instance.tunnel_id) + logger.debug("Stopped tunnel for instance %s (pid=%s)", instance.id, instance.tunnel_id) except Exception as exc: logger.warning("Failed to stop tunnel for instance %s: %s", instance.id, exc) @@ -1284,7 +1284,7 @@ async def restart_instance( if instance.tunnel_id: try: stop_cloudflared_tunnel(instance.tunnel_id) - logger.info("Stopped old tunnel for instance %s (pid=%s)", instance.id, instance.tunnel_id) + logger.debug("Stopped old tunnel for instance %s (pid=%s)", instance.id, instance.tunnel_id) except Exception as exc: logger.warning("Failed to stop old tunnel for instance %s: %s", instance.id, exc) @@ -1300,7 +1300,7 @@ async def restart_instance( # Write env file with resolved profile env vars if profile_env: write_env_file(instance_dir, profile_env) - logger.info( + logger.debug( "Re-applied config profile %s on restart for instance %s", resolved.profile_name, instance.id, @@ -1345,7 +1345,7 @@ async def restart_instance( instance.tunnel_id = tunnel_info["pid"] instance.public_url = tunnel_info["url"] instance.url = tunnel_info["url"] - logger.info( + logger.debug( "Created new tunnel for instance %s: %s", instance.id, tunnel_info["url"], @@ -1431,7 +1431,7 @@ async def delete_instance( if instance.tunnel_id: try: stop_cloudflared_tunnel(instance.tunnel_id) - logger.info("Stopped tunnel for instance %s (pid=%s)", instance.id, instance.tunnel_id) + logger.debug("Stopped tunnel for instance %s (pid=%s)", instance.id, instance.tunnel_id) except Exception as exc: logger.warning("Failed to stop tunnel for instance %s: %s", instance.id, exc) @@ -1556,7 +1556,7 @@ async def recreate_tunnel_endpoint( instance.public_url = tunnel_info["url"] instance.url = tunnel_info["url"] await session.commit() - logger.info( + logger.debug( "Recreated tunnel for instance %s: pid=%s, url=%s", instance.id, tunnel_info["pid"], diff --git a/apps/api/src/api/user_config.py b/apps/api/src/api/user_config.py index 23943e9..384464d 100644 --- a/apps/api/src/api/user_config.py +++ b/apps/api/src/api/user_config.py @@ -103,11 +103,11 @@ async def update_user_config( # Merge updates update_data = data.model_dump(exclude_unset=True) - logger.info("Updating user config for user %s: %s", user_id, update_data) + logger.debug("Updating user config for user %s: %s", user_id, update_data) # SQLAlchemy JSON doesn't track dict mutations, so we replace the whole dict config.config = {**config.config, **update_data} await session.commit() await session.refresh(config) - logger.info("Updated config: %s", config.config) + logger.debug("Updated config: %s", config.config) return UserConfigResponse.model_validate(config.config) diff --git a/apps/api/src/services/clone.py b/apps/api/src/services/clone.py index 743054b..362d149 100644 --- a/apps/api/src/services/clone.py +++ b/apps/api/src/services/clone.py @@ -42,7 +42,7 @@ def clone_repository( str(clone_path), ] - logger.info("Cloning repository %s (branch: %s) into %s", remote_url, branch, clone_path) + logger.debug("Cloning repository %s (branch: %s) into %s", remote_url, branch, clone_path) result = subprocess.run( cmd, capture_output=True, @@ -55,7 +55,7 @@ def clone_repository( logger.error("Git clone failed: %s", result.stderr) raise RuntimeError(f"Failed to clone repository: {result.stderr}") - logger.info("Successfully cloned repository into %s", clone_path) + logger.debug("Successfully cloned repository into %s", clone_path) return str(clone_path) @@ -94,4 +94,4 @@ def remove_clone_directory(instance_dir: str) -> None: if clone_path.exists(): import shutil shutil.rmtree(clone_path) - logger.info("Removed clone directory: %s", clone_path) + logger.debug("Removed clone directory: %s", clone_path) diff --git a/apps/api/src/services/docker_build.py b/apps/api/src/services/docker_build.py index efd1eb8..d697986 100644 --- a/apps/api/src/services/docker_build.py +++ b/apps/api/src/services/docker_build.py @@ -24,7 +24,7 @@ def build_image(instance_dir: str, dockerfile: str, tag: str, build_context: dic # Write Dockerfile dockerfile_path = Path(instance_dir) / "Dockerfile" dockerfile_path.write_text(dockerfile) - logger.info("Wrote Dockerfile to %s", dockerfile_path) + logger.debug("Wrote Dockerfile to %s", dockerfile_path) # Write build context files if build_context: @@ -39,10 +39,10 @@ def build_image(instance_dir: str, dockerfile: str, tag: str, build_context: dic full_path.parent.mkdir(parents=True, exist_ok=True) full_path.write_text(content) - logger.info("Wrote build context file: %s", full_path) + logger.debug("Wrote build context file: %s", full_path) # Build image - logger.info("Building Docker image with tag: %s", tag) + logger.debug("Building Docker image with tag: %s", tag) cmd = [ "docker", "build", "-t", tag, @@ -57,7 +57,7 @@ def build_image(instance_dir: str, dockerfile: str, tag: str, build_context: dic text=True, timeout=300, # 5 minute timeout for builds ) - logger.info("Docker build completed: returncode=%d", result.returncode) + logger.debug("Docker build completed: returncode=%d", result.returncode) if result.returncode != 0: logger.error("Docker build failed: %s", result.stderr[:1000]) return result.returncode, result.stdout, result.stderr diff --git a/apps/api/src/services/terminal_manager.py b/apps/api/src/services/terminal_manager.py index 4f5413d..925eb19 100644 --- a/apps/api/src/services/terminal_manager.py +++ b/apps/api/src/services/terminal_manager.py @@ -72,11 +72,11 @@ class TerminalManager: # Check if session is still alive if session.is_alive(): - logger.info("Reattaching to existing terminal session for instance %s", instance_id) + logger.debug("Reattaching to existing terminal session for instance %s", instance_id) return session else: # Session died, clean it up - logger.info("Existing session for instance %s is dead, cleaning up", instance_id) + logger.debug("Existing session for instance %s is dead, cleaning up", instance_id) await session.close() del self._sessions[instance_id_str] @@ -97,7 +97,7 @@ class TerminalManager: """Attach a WebSocket to an existing session.""" # Handle concurrent connections - close existing ones if session.has_websockets(): - logger.info("Closing existing WebSocket connections for instance %s", session.instance_id) + logger.debug("Closing existing WebSocket connections for instance %s", session.instance_id) for ws in list(session._websockets): try: await ws.close(code=4000, reason="New connection established") @@ -135,7 +135,7 @@ class TerminalManager: # Close existing session if any if instance_id_str in self._sessions: - logger.info("Resetting terminal session for instance %s", instance_id) + logger.debug("Resetting terminal session for instance %s", instance_id) old_session = self._sessions.pop(instance_id_str) await old_session.close() diff --git a/apps/api/src/services/terminal_session.py b/apps/api/src/services/terminal_session.py index edf54a5..e27c999 100644 --- a/apps/api/src/services/terminal_session.py +++ b/apps/api/src/services/terminal_session.py @@ -60,12 +60,12 @@ class TerminalSession: # Set the terminal size initially self._set_terminal_size(self._cols, self._rows) - logger.info(f"Starting terminal session {self.session_id} for container {self.container_id} with initial size {self._cols}x{self._rows}") + logger.debug(f"Starting terminal session {self.session_id} for container {self.container_id} with initial size {self._cols}x{self._rows}") # Build the shell command if startup_command: shell_cmd = f'bash -c "{startup_command}" || true; exec bash -il' - logger.info(f"Using startup command for session {self.session_id}: {startup_command}") + logger.debug(f"Using startup command for session {self.session_id}: {startup_command}") else: shell_cmd = "bash -il" @@ -102,7 +102,7 @@ class TerminalSession: size = struct.pack('HHHH', rows, cols, 0, 0) try: fcntl.ioctl(self._master_fd, TIOCSWINSZ, size) - logger.info(f"Resized PTY to {cols}x{rows} (fd={self._master_fd})") + logger.debug(f"Resized PTY to {cols}x{rows} (fd={self._master_fd})") except (OSError, IOError) as e: logger.error(f"Failed to resize PTY: {e}") @@ -159,7 +159,7 @@ class TerminalSession: self._cols = cols self._rows = rows - logger.info(f"resize() called for session {self.session_id}: {cols}x{rows}") + logger.debug(f"resize() called for session {self.session_id}: {cols}x{rows}") self._set_terminal_size(cols, rows) # Docker exec -it creates its own PTY inside the container, diff --git a/apps/web/src/components/icon.tsx b/apps/web/src/components/icon.tsx index 675094e..8f114a3 100644 --- a/apps/web/src/components/icon.tsx +++ b/apps/web/src/components/icon.tsx @@ -150,7 +150,6 @@ export const Icon: React.FC = ({ const sizeValue = sizeMap[size]; if (!IconComponent) { - console.warn(`Icon "${name}" not found`); return null; } diff --git a/apps/web/src/components/terminal.tsx b/apps/web/src/components/terminal.tsx index cafa024..e902927 100644 --- a/apps/web/src/components/terminal.tsx +++ b/apps/web/src/components/terminal.tsx @@ -73,12 +73,11 @@ export const TerminalComponent: React.FC = ({ const wsHost = apiUrl.replace(/^https?:\/\//, "").replace(/\/+$/, ""); const wsUrl = `${wsProtocol}//${wsHost}/ws/tool-instances/${instanceId}/terminal`; - console.log(`[Terminal WS] Connecting to ${wsUrl} (attempt ${reconnectAttemptsRef.current + 1}/${RECONNECT_ATTEMPTS + 1})`); + // WebSocket connection established const ws = new WebSocket(wsUrl); wsRef.current = ws; ws.onopen = () => { - console.log(`[Terminal WS] Connected successfully`); setStatus("connected"); setError(null); reconnectAttemptsRef.current = 0; @@ -99,10 +98,8 @@ export const TerminalComponent: React.FC = ({ } heartbeatCheckRef.current = window.setInterval(() => { const elapsed = Date.now() - lastPingRef.current; - console.log(`[Terminal WS] Heartbeat check: lastPing=${elapsed}ms ago`); if (elapsed > 60000) { // No ping for 60 seconds, connection may be dead - console.warn("[Terminal WS] Heartbeat timeout (>60s), closing connection"); ws.close(4000, "Heartbeat timeout"); } }, 30000); @@ -143,7 +140,6 @@ export const TerminalComponent: React.FC = ({ } else if (msg.type === "ping") { // Respond with pong and update last ping time lastPingRef.current = Date.now(); - console.log(`[Terminal WS] Received ping, sending pong`); if (ws.readyState === WebSocket.OPEN) { ws.send(JSON.stringify({ type: "pong" })); } @@ -155,7 +151,6 @@ export const TerminalComponent: React.FC = ({ }; ws.onclose = (event) => { - console.log(`[Terminal WS] Connection closed: code=${event.code}, reason="${event.reason}", wasClean=${event.wasClean}, attempts=${reconnectAttemptsRef.current}`); setStatus("disconnected"); // Clean up heartbeat check @@ -171,30 +166,24 @@ export const TerminalComponent: React.FC = ({ if (reconnectAttemptsRef.current < RECONNECT_ATTEMPTS) { reconnectAttemptsRef.current++; const delay = RECONNECT_DELAY_BASE * Math.pow(2, reconnectAttemptsRef.current - 1); - console.log(`[Terminal WS] Will retry in ${delay}ms (attempt ${reconnectAttemptsRef.current}/${RECONNECT_ATTEMPTS})`); setTimeout(() => { if (isUnmountingRef.current) { - console.log(`[Terminal WS] Component unmounting, skipping reconnect`); return; } if (document.visibilityState !== "hidden") { connectWebSocket(); } else { - console.log(`[Terminal WS] Tab hidden, skipping reconnect`); } }, delay); } else { - console.log(`[Terminal WS] Max reconnection attempts (${RECONNECT_ATTEMPTS}) reached`); } } else if (event.code === 4000) { // Server closed old connection for concurrent connection - don't reconnect // The new connection is already established - console.log(`[Terminal WS] Server closed old connection (concurrent/heartbeat)`); } }; ws.onerror = (error) => { - console.error(`[Terminal WS] Error event fired`, error); setStatus("error"); setError("WebSocket error"); }; @@ -259,7 +248,6 @@ export const TerminalComponent: React.FC = ({ return; } const { cols, rows } = termRef.current; - console.log(`[Terminal] fit() result: ${cols}x${rows} (was ${oldCols}x${oldRows})`); // Force refresh if dimensions are valid if (cols > 0 && rows > 0) { try { @@ -285,14 +273,11 @@ export const TerminalComponent: React.FC = ({ fitAttempts++; // Ensure container has dimensions before fitting if (container.clientWidth > 0 && container.clientHeight > 0) { - console.log(`[Terminal] Container ready: ${container.clientWidth}x${container.clientHeight} (attempt ${fitAttempts})`); fitTerminal(); } else if (fitAttempts < 50) { // Container not ready yet, try again (max 50 attempts ~ 1s) - console.log(`[Terminal] Container not ready: ${container.clientWidth}x${container.clientHeight} (attempt ${fitAttempts})`); requestAnimationFrame(doInitialFit); } else { - console.warn(`[Terminal] Container never got dimensions after ${fitAttempts} attempts`); } }; requestAnimationFrame(doInitialFit); @@ -379,9 +364,7 @@ export const TerminalComponent: React.FC = ({ // Visibility API for reconnection const handleVisibilityChange = () => { - console.log(`[Terminal WS] Visibility changed to: ${document.visibilityState}, wsState=${ws?.readyState}`); if (document.visibilityState === "visible" && ws && ws.readyState !== WebSocket.OPEN) { - console.log(`[Terminal WS] Tab visible, resetting reconnect attempts and reconnecting`); reconnectAttemptsRef.current = 0; connectWebSocket(); }