mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-25 00:51:20 +00:00
refactor: enhance error handling with structured logging across multiple modules
- Updated various modules including cli.py, run_agent.py, gateway, and tools to replace silent exception handling with structured logging. - Improved error messages to provide more context, aiding in debugging and monitoring. - Ensured consistent logging practices throughout the codebase, enhancing traceability and maintainability.
This commit is contained in:
parent
cbff1b818c
commit
748fd3db88
14 changed files with 134 additions and 110 deletions
8
cli.py
8
cli.py
|
|
@ -823,8 +823,8 @@ class HermesCLI:
|
|||
try:
|
||||
from hermes_state import SessionDB
|
||||
self._session_db = SessionDB()
|
||||
except Exception:
|
||||
pass # SQLite session store is optional
|
||||
except Exception as e:
|
||||
logger.debug("SQLite session store not available: %s", e)
|
||||
|
||||
try:
|
||||
self.agent = AIAgent(
|
||||
|
|
@ -2130,8 +2130,8 @@ class HermesCLI:
|
|||
if hasattr(self, '_session_db') and self._session_db and self.agent:
|
||||
try:
|
||||
self._session_db.end_session(self.agent.session_id, "cli_close")
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.debug("Could not close session in DB: %s", e)
|
||||
_run_cleanup()
|
||||
print("\nGoodbye! ⚕")
|
||||
|
||||
|
|
|
|||
|
|
@ -8,9 +8,12 @@ Uses discord.py library for:
|
|||
"""
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
import os
|
||||
from typing import Dict, List, Optional, Any
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
try:
|
||||
import discord
|
||||
from discord import Message as DiscordMessage, Intents
|
||||
|
|
@ -177,8 +180,8 @@ class DiscordAdapter(BasePlatformAdapter):
|
|||
try:
|
||||
ref_msg = await channel.fetch_message(int(reply_to))
|
||||
reference = ref_msg
|
||||
except Exception:
|
||||
pass # Ignore if we can't find the referenced message
|
||||
except Exception as e:
|
||||
logger.debug("Could not fetch reply-to message: %s", e)
|
||||
|
||||
for i, chunk in enumerate(chunks):
|
||||
msg = await channel.send(
|
||||
|
|
@ -363,8 +366,8 @@ class DiscordAdapter(BasePlatformAdapter):
|
|||
# Send a followup to close the interaction if needed
|
||||
try:
|
||||
await interaction.followup.send("Processing complete~", ephemeral=True)
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.debug("Discord followup failed: %s", e)
|
||||
|
||||
@tree.command(name="new", description="Start a new conversation")
|
||||
async def slash_new(interaction: discord.Interaction):
|
||||
|
|
@ -373,8 +376,8 @@ class DiscordAdapter(BasePlatformAdapter):
|
|||
await self.handle_message(event)
|
||||
try:
|
||||
await interaction.followup.send("New conversation started~", ephemeral=True)
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.debug("Discord followup failed: %s", e)
|
||||
|
||||
@tree.command(name="reset", description="Reset your Hermes session")
|
||||
async def slash_reset(interaction: discord.Interaction):
|
||||
|
|
@ -383,8 +386,8 @@ class DiscordAdapter(BasePlatformAdapter):
|
|||
await self.handle_message(event)
|
||||
try:
|
||||
await interaction.followup.send("Session reset~", ephemeral=True)
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.debug("Discord followup failed: %s", e)
|
||||
|
||||
@tree.command(name="model", description="Show or change the model")
|
||||
@discord.app_commands.describe(name="Model name (e.g. anthropic/claude-sonnet-4). Leave empty to see current.")
|
||||
|
|
@ -394,8 +397,8 @@ class DiscordAdapter(BasePlatformAdapter):
|
|||
await self.handle_message(event)
|
||||
try:
|
||||
await interaction.followup.send("Done~", ephemeral=True)
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.debug("Discord followup failed: %s", e)
|
||||
|
||||
@tree.command(name="personality", description="Set a personality")
|
||||
@discord.app_commands.describe(name="Personality name. Leave empty to list available.")
|
||||
|
|
@ -405,8 +408,8 @@ class DiscordAdapter(BasePlatformAdapter):
|
|||
await self.handle_message(event)
|
||||
try:
|
||||
await interaction.followup.send("Done~", ephemeral=True)
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.debug("Discord followup failed: %s", e)
|
||||
|
||||
@tree.command(name="retry", description="Retry your last message")
|
||||
async def slash_retry(interaction: discord.Interaction):
|
||||
|
|
@ -415,8 +418,8 @@ class DiscordAdapter(BasePlatformAdapter):
|
|||
await self.handle_message(event)
|
||||
try:
|
||||
await interaction.followup.send("Retrying~", ephemeral=True)
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.debug("Discord followup failed: %s", e)
|
||||
|
||||
@tree.command(name="undo", description="Remove the last exchange")
|
||||
async def slash_undo(interaction: discord.Interaction):
|
||||
|
|
@ -425,8 +428,8 @@ class DiscordAdapter(BasePlatformAdapter):
|
|||
await self.handle_message(event)
|
||||
try:
|
||||
await interaction.followup.send("Done~", ephemeral=True)
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.debug("Discord followup failed: %s", e)
|
||||
|
||||
@tree.command(name="status", description="Show Hermes session status")
|
||||
async def slash_status(interaction: discord.Interaction):
|
||||
|
|
@ -435,8 +438,8 @@ class DiscordAdapter(BasePlatformAdapter):
|
|||
await self.handle_message(event)
|
||||
try:
|
||||
await interaction.followup.send("Status sent~", ephemeral=True)
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.debug("Discord followup failed: %s", e)
|
||||
|
||||
@tree.command(name="stop", description="Stop the running Hermes agent")
|
||||
async def slash_stop(interaction: discord.Interaction):
|
||||
|
|
@ -445,8 +448,8 @@ class DiscordAdapter(BasePlatformAdapter):
|
|||
await self.handle_message(event)
|
||||
try:
|
||||
await interaction.followup.send("Stop requested~", ephemeral=True)
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.debug("Discord followup failed: %s", e)
|
||||
|
||||
def _build_slash_event(self, interaction: discord.Interaction, text: str) -> MessageEvent:
|
||||
"""Build a MessageEvent from a Discord slash command interaction."""
|
||||
|
|
|
|||
|
|
@ -17,10 +17,13 @@ with different backends via a bridge pattern.
|
|||
|
||||
import asyncio
|
||||
import json
|
||||
import logging
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Optional, Any
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
import sys
|
||||
sys.path.insert(0, str(__file__).rsplit("/", 3)[0])
|
||||
|
||||
|
|
@ -246,8 +249,8 @@ class WhatsAppAdapter(BasePlatformAdapter):
|
|||
"type": "group" if data.get("isGroup") else "dm",
|
||||
"participants": data.get("participants", []),
|
||||
}
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.debug("Could not get WhatsApp chat info for %s: %s", chat_id, e)
|
||||
|
||||
return {"name": chat_id, "type": "dm"}
|
||||
|
||||
|
|
|
|||
|
|
@ -479,8 +479,8 @@ class GatewayRunner:
|
|||
self._pending_approvals[session_key] = _last_pending_approval.copy()
|
||||
# Clear the global so it doesn't leak to other sessions
|
||||
_last_pending_approval.clear()
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.debug("Failed to check pending approvals: %s", e)
|
||||
|
||||
# Save the full conversation to the transcript, including tool calls.
|
||||
# This preserves the complete agent loop (tool_calls, tool results,
|
||||
|
|
@ -973,8 +973,8 @@ class GatewayRunner:
|
|||
with open(config_path, 'r') as f:
|
||||
user_config = yaml.safe_load(f) or {}
|
||||
platform_toolsets_config = user_config.get("platform_toolsets", {})
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.debug("Could not load platform_toolsets config: %s", e)
|
||||
|
||||
# Map platform enum to config key
|
||||
platform_config_key = {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ Handles:
|
|||
- Dynamic system prompt injection (agent knows its context)
|
||||
"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
import json
|
||||
import uuid
|
||||
|
|
@ -16,6 +17,8 @@ from datetime import datetime, timedelta
|
|||
from dataclasses import dataclass, field
|
||||
from typing import Dict, List, Optional, Any
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
from .config import (
|
||||
Platform,
|
||||
GatewayConfig,
|
||||
|
|
@ -388,8 +391,8 @@ class SessionStore:
|
|||
if self._db:
|
||||
try:
|
||||
self._db.end_session(entry.session_id, "session_reset")
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.debug("Session DB operation failed: %s", e)
|
||||
|
||||
# Create new session
|
||||
session_id = f"{now.strftime('%Y%m%d_%H%M%S')}_{uuid.uuid4().hex[:8]}"
|
||||
|
|
@ -443,8 +446,8 @@ class SessionStore:
|
|||
self._db.update_token_counts(
|
||||
entry.session_id, input_tokens, output_tokens
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.debug("Session DB operation failed: %s", e)
|
||||
|
||||
def reset_session(self, session_key: str) -> Optional[SessionEntry]:
|
||||
"""Force reset a session, creating a new session ID."""
|
||||
|
|
@ -459,8 +462,8 @@ class SessionStore:
|
|||
if self._db:
|
||||
try:
|
||||
self._db.end_session(old_entry.session_id, "session_reset")
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.debug("Session DB operation failed: %s", e)
|
||||
|
||||
now = datetime.now()
|
||||
session_id = f"{now.strftime('%Y%m%d_%H%M%S')}_{uuid.uuid4().hex[:8]}"
|
||||
|
|
@ -487,8 +490,8 @@ class SessionStore:
|
|||
source=old_entry.platform.value if old_entry.platform else "unknown",
|
||||
user_id=old_entry.origin.user_id if old_entry.origin else None,
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.debug("Session DB operation failed: %s", e)
|
||||
|
||||
return new_entry
|
||||
|
||||
|
|
@ -523,8 +526,8 @@ class SessionStore:
|
|||
tool_calls=message.get("tool_calls"),
|
||||
tool_call_id=message.get("tool_call_id"),
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.debug("Session DB operation failed: %s", e)
|
||||
|
||||
# Also write legacy JSONL (keeps existing tooling working during transition)
|
||||
transcript_path = self.get_transcript_path(session_id)
|
||||
|
|
@ -539,8 +542,8 @@ class SessionStore:
|
|||
messages = self._db.get_messages_as_conversation(session_id)
|
||||
if messages:
|
||||
return messages
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.debug("Could not load messages from DB: %s", e)
|
||||
|
||||
# Fall back to legacy JSONL
|
||||
transcript_path = self.get_transcript_path(session_id)
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ Architecture:
|
|||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import stat
|
||||
import time
|
||||
|
|
@ -32,6 +33,8 @@ import yaml
|
|||
from hermes_cli.config import get_hermes_home, get_config_path
|
||||
from hermes_constants import OPENROUTER_BASE_URL
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
try:
|
||||
import fcntl
|
||||
except Exception:
|
||||
|
|
@ -314,8 +317,8 @@ def resolve_provider(
|
|||
state = _load_provider_state(auth_store, active)
|
||||
if state and (state.get("access_token") or state.get("refresh_token")):
|
||||
return active
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.debug("Could not detect active auth provider: %s", e)
|
||||
|
||||
if os.getenv("OPENAI_API_KEY") or os.getenv("OPENROUTER_API_KEY"):
|
||||
return "openrouter"
|
||||
|
|
@ -578,8 +581,8 @@ def fetch_nous_models(
|
|||
try:
|
||||
err = response.json()
|
||||
description = str(err.get("error_description") or err.get("error") or description)
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.debug("Could not parse error response JSON: %s", e)
|
||||
raise AuthError(description, provider="nous", code="models_fetch_failed")
|
||||
|
||||
payload = response.json()
|
||||
|
|
|
|||
|
|
@ -39,9 +39,13 @@ env_path = PROJECT_ROOT / '.env'
|
|||
if env_path.exists():
|
||||
load_dotenv(dotenv_path=env_path)
|
||||
|
||||
import logging
|
||||
|
||||
from hermes_cli import __version__
|
||||
from hermes_constants import OPENROUTER_BASE_URL
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def cmd_chat(args):
|
||||
"""Run interactive chat CLI."""
|
||||
|
|
@ -512,8 +516,8 @@ def cmd_update(args):
|
|||
print(f" + {len(result['copied'])} new skill(s): {', '.join(result['copied'])}")
|
||||
else:
|
||||
print(" ✓ Skills are up to date")
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.debug("Skills sync during update failed: %s", e)
|
||||
|
||||
# Check for config migrations
|
||||
print()
|
||||
|
|
|
|||
|
|
@ -12,11 +12,14 @@ Guides users through:
|
|||
Config files are stored in ~/.hermes/ for easy access.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import Optional, Dict, Any
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
PROJECT_ROOT = Path(__file__).parent.parent.resolve()
|
||||
|
||||
# Import config helpers
|
||||
|
|
@ -488,8 +491,8 @@ def run_setup_wizard(args):
|
|||
inference_base_url=creds.get("base_url", ""),
|
||||
api_key=creds.get("api_key", ""),
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.debug("Could not fetch Nous models after login: %s", e)
|
||||
|
||||
except SystemExit:
|
||||
print_warning("Nous Portal login was cancelled or failed.")
|
||||
|
|
|
|||
56
run_agent.py
56
run_agent.py
|
|
@ -636,8 +636,8 @@ def build_skills_system_prompt() -> str:
|
|||
match = re.search(r"^---\s*\n.*?description:\s*(.+?)\s*\n.*?^---", content, re.MULTILINE | re.DOTALL)
|
||||
if match:
|
||||
category_descriptions[category] = match.group(1).strip()
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.debug("Could not read skill description %s: %s", desc_file, e)
|
||||
|
||||
index_lines = []
|
||||
for category in sorted(skills_by_category.keys()):
|
||||
|
|
@ -748,8 +748,8 @@ def build_context_files_prompt(cwd: str = None) -> str:
|
|||
if content:
|
||||
rel_path = agents_path.relative_to(cwd_path)
|
||||
total_agents_content += f"## {rel_path}\n\n{content}\n\n"
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.debug("Could not read %s: %s", agents_path, e)
|
||||
|
||||
if total_agents_content:
|
||||
total_agents_content = _truncate_content(total_agents_content, "AGENTS.md")
|
||||
|
|
@ -765,8 +765,8 @@ def build_context_files_prompt(cwd: str = None) -> str:
|
|||
content = cursorrules_file.read_text(encoding="utf-8").strip()
|
||||
if content:
|
||||
cursorrules_content += f"## .cursorrules\n\n{content}\n\n"
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.debug("Could not read .cursorrules: %s", e)
|
||||
|
||||
# Check for .cursor/rules/*.mdc files
|
||||
cursor_rules_dir = cwd_path / ".cursor" / "rules"
|
||||
|
|
@ -777,8 +777,8 @@ def build_context_files_prompt(cwd: str = None) -> str:
|
|||
content = mdc_file.read_text(encoding="utf-8").strip()
|
||||
if content:
|
||||
cursorrules_content += f"## .cursor/rules/{mdc_file.name}\n\n{content}\n\n"
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.debug("Could not read %s: %s", mdc_file, e)
|
||||
|
||||
if cursorrules_content:
|
||||
cursorrules_content = _truncate_content(cursorrules_content, ".cursorrules")
|
||||
|
|
@ -807,8 +807,8 @@ def build_context_files_prompt(cwd: str = None) -> str:
|
|||
content = _truncate_content(content, "SOUL.md")
|
||||
soul_content = f"## SOUL.md\n\nIf SOUL.md is present, embody its persona and tone. Avoid stiff, generic replies; follow its guidance unless higher-priority instructions override it.\n\n{content}"
|
||||
sections.append(soul_content)
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.debug("Could not read SOUL.md from %s: %s", soul_path, e)
|
||||
|
||||
# ----- Assemble -----
|
||||
if not sections:
|
||||
|
|
@ -1320,8 +1320,8 @@ class AIAgent:
|
|||
},
|
||||
user_id=None,
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.debug("Session DB create_session failed: %s", e)
|
||||
|
||||
# In-memory todo list for task planning (one per agent/session)
|
||||
from tools.todo_tool import TodoStore
|
||||
|
|
@ -1730,8 +1730,8 @@ class AIAgent:
|
|||
tool_call_id=msg.get("tool_call_id"),
|
||||
finish_reason=msg.get("finish_reason"),
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.debug("Session DB append_message failed: %s", e)
|
||||
|
||||
def _get_messages_up_to_last_assistant(self, messages: List[Dict]) -> List[Dict]:
|
||||
"""
|
||||
|
|
@ -2048,8 +2048,8 @@ class AIAgent:
|
|||
api_key = None
|
||||
try:
|
||||
api_key = getattr(self.client, "api_key", None)
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.debug("Could not extract API key for debug dump: %s", e)
|
||||
|
||||
dump_payload: Dict[str, Any] = {
|
||||
"timestamp": datetime.now().isoformat(),
|
||||
|
|
@ -2085,8 +2085,8 @@ class AIAgent:
|
|||
try:
|
||||
error_info["response_status"] = getattr(response_obj, "status_code", None)
|
||||
error_info["response_text"] = response_obj.text
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.debug("Could not extract error response details: %s", e)
|
||||
|
||||
dump_payload["error"] = error_info
|
||||
|
||||
|
|
@ -2174,8 +2174,8 @@ class AIAgent:
|
|||
for child in self._active_children:
|
||||
try:
|
||||
child.interrupt(message)
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.debug("Failed to propagate interrupt to child agent: %s", e)
|
||||
if not self.quiet_mode:
|
||||
print(f"\n⚡ Interrupt requested" + (f": '{message[:40]}...'" if message and len(message) > 40 else f": '{message}'" if message else ""))
|
||||
|
||||
|
|
@ -2346,8 +2346,8 @@ class AIAgent:
|
|||
if self._session_db:
|
||||
try:
|
||||
self._session_db.update_system_prompt(self.session_id, self._cached_system_prompt)
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.debug("Session DB update_system_prompt failed: %s", e)
|
||||
|
||||
active_system_prompt = self._cached_system_prompt
|
||||
|
||||
|
|
@ -2355,8 +2355,8 @@ class AIAgent:
|
|||
if self._session_db:
|
||||
try:
|
||||
self._session_db.append_message(self.session_id, "user", user_message)
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.debug("Session DB append_message failed: %s", e)
|
||||
|
||||
# Main conversation loop
|
||||
api_call_count = 0
|
||||
|
|
@ -2743,8 +2743,8 @@ class AIAgent:
|
|||
parent_session_id=old_session_id,
|
||||
)
|
||||
self._session_db.update_system_prompt(self.session_id, active_system_prompt)
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.debug("Session DB compression split failed: %s", e)
|
||||
print(f"{self.log_prefix} 🗜️ Compressed {original_len} → {len(messages)} messages, retrying...")
|
||||
continue # Retry with compressed messages
|
||||
else:
|
||||
|
|
@ -3175,8 +3175,8 @@ class AIAgent:
|
|||
parent_session_id=old_session_id,
|
||||
)
|
||||
self._session_db.update_system_prompt(self.session_id, active_system_prompt)
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.debug("Session DB compression split failed: %s", e)
|
||||
|
||||
# Save session log incrementally (so progress is visible even if interrupted)
|
||||
self._session_messages = messages
|
||||
|
|
|
|||
|
|
@ -31,6 +31,8 @@ import uuid
|
|||
from typing import Any, Dict, List, Optional
|
||||
|
||||
# Availability gate: UDS requires a POSIX OS
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
SANDBOX_AVAILABLE = sys.platform != "win32"
|
||||
|
||||
# The 7 tools allowed inside the sandbox. The intersection of this list
|
||||
|
|
@ -488,8 +490,8 @@ def execute_code(
|
|||
try:
|
||||
import shutil
|
||||
shutil.rmtree(tmpdir, ignore_errors=True)
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.debug("Could not clean temp dir: %s", e)
|
||||
try:
|
||||
os.unlink(sock_path)
|
||||
except OSError:
|
||||
|
|
@ -503,8 +505,8 @@ def _kill_process_group(proc, escalate: bool = False):
|
|||
except (ProcessLookupError, PermissionError):
|
||||
try:
|
||||
proc.kill()
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.debug("Could not kill process: %s", e)
|
||||
|
||||
if escalate:
|
||||
# Give the process 5s to exit after SIGTERM, then SIGKILL
|
||||
|
|
@ -516,8 +518,8 @@ def _kill_process_group(proc, escalate: bool = False):
|
|||
except (ProcessLookupError, PermissionError):
|
||||
try:
|
||||
proc.kill()
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.debug("Could not kill process: %s", e)
|
||||
|
||||
|
||||
def _load_config() -> dict:
|
||||
|
|
|
|||
|
|
@ -277,14 +277,14 @@ class ProcessRegistry:
|
|||
session.output_buffer += chunk
|
||||
if len(session.output_buffer) > session.max_output_chars:
|
||||
session.output_buffer = session.output_buffer[-session.max_output_chars:]
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.debug("Process stdout reader ended: %s", e)
|
||||
|
||||
# Process exited
|
||||
try:
|
||||
session.process.wait(timeout=5)
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.debug("Process wait timed out or failed: %s", e)
|
||||
session.exited = True
|
||||
session.exit_code = session.process.returncode
|
||||
self._move_to_finished(session)
|
||||
|
|
@ -351,14 +351,14 @@ class ProcessRegistry:
|
|||
break
|
||||
except Exception:
|
||||
break
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.debug("PTY stdout reader ended: %s", e)
|
||||
|
||||
# Process exited
|
||||
try:
|
||||
pty.wait()
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.debug("PTY wait timed out or failed: %s", e)
|
||||
session.exited = True
|
||||
session.exit_code = pty.exitstatus if hasattr(pty, 'exitstatus') else -1
|
||||
self._move_to_finished(session)
|
||||
|
|
@ -719,8 +719,8 @@ class ProcessRegistry:
|
|||
# Clear the checkpoint (will be rewritten as processes finish)
|
||||
try:
|
||||
CHECKPOINT_PATH.write_text("[]", encoding="utf-8")
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.debug("Could not write checkpoint file: %s", e)
|
||||
|
||||
return recovered
|
||||
|
||||
|
|
|
|||
|
|
@ -154,8 +154,8 @@ class GitHubAuth:
|
|||
)
|
||||
if result.returncode == 0 and result.stdout.strip():
|
||||
return result.stdout.strip()
|
||||
except (FileNotFoundError, subprocess.TimeoutExpired):
|
||||
pass
|
||||
except (FileNotFoundError, subprocess.TimeoutExpired) as e:
|
||||
logger.debug("gh CLI token lookup failed: %s", e)
|
||||
return None
|
||||
|
||||
def _try_github_app(self) -> Optional[str]:
|
||||
|
|
@ -438,8 +438,8 @@ class GitHubSource(SkillSource):
|
|||
)
|
||||
if resp.status_code == 200:
|
||||
return resp.text
|
||||
except httpx.HTTPError:
|
||||
pass
|
||||
except httpx.HTTPError as e:
|
||||
logger.debug("GitHub contents API fetch failed: %s", e)
|
||||
return None
|
||||
|
||||
def _read_cache(self, key: str) -> Optional[list]:
|
||||
|
|
@ -461,8 +461,8 @@ class GitHubSource(SkillSource):
|
|||
cache_file = INDEX_CACHE_DIR / f"{key}.json"
|
||||
try:
|
||||
cache_file.write_text(json.dumps(data, ensure_ascii=False))
|
||||
except OSError:
|
||||
pass
|
||||
except OSError as e:
|
||||
logger.debug("Could not write cache: %s", e)
|
||||
|
||||
@staticmethod
|
||||
def _meta_to_dict(meta: SkillMeta) -> dict:
|
||||
|
|
@ -826,8 +826,8 @@ class LobeHubSource(SkillSource):
|
|||
resp = httpx.get(url, timeout=15)
|
||||
if resp.status_code == 200:
|
||||
return resp.json()
|
||||
except (httpx.HTTPError, json.JSONDecodeError):
|
||||
pass
|
||||
except (httpx.HTTPError, json.JSONDecodeError) as e:
|
||||
logger.debug("LobeHub agent fetch failed: %s", e)
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
|
|
@ -890,8 +890,8 @@ def _write_index_cache(key: str, data: Any) -> None:
|
|||
cache_file = INDEX_CACHE_DIR / f"{key}.json"
|
||||
try:
|
||||
cache_file.write_text(json.dumps(data, ensure_ascii=False, default=str))
|
||||
except OSError:
|
||||
pass
|
||||
except OSError as e:
|
||||
logger.debug("Could not write cache: %s", e)
|
||||
|
||||
|
||||
def _skill_meta_to_dict(meta: SkillMeta) -> dict:
|
||||
|
|
@ -1037,8 +1037,8 @@ def append_audit_log(action: str, skill_name: str, source: str,
|
|||
try:
|
||||
with open(AUDIT_LOG, "a") as f:
|
||||
f.write(line)
|
||||
except OSError:
|
||||
pass
|
||||
except OSError as e:
|
||||
logger.debug("Could not write audit log: %s", e)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -13,11 +13,14 @@ newline-delimited list of skill names that have been offered to the user.
|
|||
"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
from typing import List, Tuple
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
HERMES_HOME = Path(os.getenv("HERMES_HOME", Path.home() / ".hermes"))
|
||||
SKILLS_DIR = HERMES_HOME / "skills"
|
||||
|
|
@ -131,8 +134,8 @@ def sync_skills(quiet: bool = False) -> dict:
|
|||
try:
|
||||
dest_desc.parent.mkdir(parents=True, exist_ok=True)
|
||||
shutil.copy2(desc_md, dest_desc)
|
||||
except (OSError, IOError):
|
||||
pass
|
||||
except (OSError, IOError) as e:
|
||||
logger.debug("Could not copy %s: %s", desc_md, e)
|
||||
|
||||
_write_manifest(manifest)
|
||||
|
||||
|
|
|
|||
|
|
@ -122,8 +122,8 @@ def _convert_to_opus(mp3_path: str) -> Optional[str]:
|
|||
)
|
||||
if os.path.exists(ogg_path) and os.path.getsize(ogg_path) > 0:
|
||||
return ogg_path
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.warning("ffmpeg OGG conversion failed: %s", e)
|
||||
return None
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue