aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--idatui/__init__.py5
-rw-r--r--idatui/app.py3
-rw-r--r--idatui/client.py75
-rw-r--r--idatui/domain.py7
-rw-r--r--idatui/errors.py76
-rw-r--r--idatui/worker_client.py2
6 files changed, 99 insertions, 69 deletions
diff --git a/idatui/__init__.py b/idatui/__init__.py
index a52ba8b..92d8b5d 100644
--- a/idatui/__init__.py
+++ b/idatui/__init__.py
@@ -1,7 +1,6 @@
"""idatui — a minimal keyboard-first TUI for IDA Pro over ida-pro-mcp (idalib)."""
-from .client import (
- IDAClient,
+from .errors import (
IDAError,
IDAConnectionError,
IDATimeoutError,
@@ -10,8 +9,8 @@ from .client import (
IDAToolError,
IDASessionError,
Session,
- KeepAlive,
)
+from .client import IDAClient, KeepAlive # deprecated mcp transport
from .domain import (
Program,
FunctionIndex,
diff --git a/idatui/app.py b/idatui/app.py
index 1936153..712b4de 100644
--- a/idatui/app.py
+++ b/idatui/app.py
@@ -42,7 +42,8 @@ from textual.widgets.option_list import Option
from .highlight import highlight_c
-from .client import IDAClient, IDAToolError, IDAConnectionError
+from .errors import IDAToolError, IDAConnectionError
+from .client import IDAClient # deprecated mcp transport (worker path uses WorkerClient)
from .domain import DisasmModel, Func, Head, ListingModel, Program, Struct
# Styles for the disassembly listing.
diff --git a/idatui/client.py b/idatui/client.py
index f39a10d..2f74cbe 100644
--- a/idatui/client.py
+++ b/idatui/client.py
@@ -66,68 +66,19 @@ SESSION_AGNOSTIC_TOOLS = frozenset({"idb_list", "idb_open", "int_convert"})
_STALE_SESSION_MARKERS = ("session not found", "database is required")
-# --------------------------------------------------------------------------- #
-# Exceptions
-# --------------------------------------------------------------------------- #
-class IDAError(Exception):
- """Base class for all client errors."""
-
-
-class IDAConnectionError(IDAError):
- """The transport could not be established or was lost."""
-
-
-class IDATimeoutError(IDAError):
- """A request exceeded its deadline."""
-
-
-class IDAProtocolError(IDAError):
- """Malformed or unexpected HTTP / JSON-RPC framing."""
-
-
-class IDARPCError(IDAError):
- """The JSON-RPC envelope carried an ``error`` object."""
-
- def __init__(self, code: int, message: str, data: Any = None):
- super().__init__(f"JSON-RPC error {code}: {message}")
- self.code = code
- self.message = message
- self.data = data
-
-
-class IDAToolError(IDAError):
- """A tool call returned ``result.isError == true`` (a hard failure)."""
-
- def __init__(self, tool: str, message: str):
- super().__init__(f"tool {tool!r} failed: {message}")
- self.tool = tool
- self.message = message
-
-
-class IDASessionError(IDAError):
- """No IDB session is open, or several are and none was pinned."""
-
-
-# --------------------------------------------------------------------------- #
-# Session model
-# --------------------------------------------------------------------------- #
-@dataclass(frozen=True)
-class Session:
- session_id: str
- filename: str
- input_path: str
- is_active: bool = False
- is_analyzing: bool = False
-
- @classmethod
- def from_dict(cls, d: dict) -> "Session":
- return cls(
- session_id=d.get("session_id", ""),
- filename=d.get("filename", ""),
- input_path=d.get("input_path", ""),
- is_active=bool(d.get("is_active", False)),
- is_analyzing=bool(d.get("is_analyzing", False)),
- )
+# The error hierarchy and Session model now live in errors.py (transport-
+# agnostic, shared with the idalib worker path); re-exported here so the
+# deprecated mcp tooling and the stress tests keep importing them from client.
+from .errors import ( # noqa: E402,F401
+ IDAError,
+ IDAConnectionError,
+ IDATimeoutError,
+ IDAProtocolError,
+ IDARPCError,
+ IDAToolError,
+ IDASessionError,
+ Session,
+)
# --------------------------------------------------------------------------- #
diff --git a/idatui/domain.py b/idatui/domain.py
index 2a3fc2b..4f72f5b 100644
--- a/idatui/domain.py
+++ b/idatui/domain.py
@@ -28,9 +28,12 @@ import threading
import urllib.request
from concurrent.futures import ThreadPoolExecutor
from dataclasses import dataclass, field, replace
-from typing import Callable
+from typing import Callable, TYPE_CHECKING
-from .client import IDAClient, IDAToolError
+from .errors import IDAToolError
+
+if TYPE_CHECKING: # type hint only; the runtime client is mcp IDAClient or worker
+ from .client import IDAClient # noqa: F401
# Clamps derived from measured caps (list ~700, disasm ~500). Margin included.
LIST_PAGE = 500
diff --git a/idatui/errors.py b/idatui/errors.py
new file mode 100644
index 0000000..29b09ae
--- /dev/null
+++ b/idatui/errors.py
@@ -0,0 +1,76 @@
+"""Transport-agnostic error hierarchy and the Session model.
+
+These were originally defined in client.py (the ida-pro-mcp HTTP client), but the
+idalib worker path (worker_client / domain / app) needs the same exception types
+and Session dataclass without dragging in the HTTP transport. They live here so
+both backends share one definition; client.py re-exports them for backwards
+compatibility with the (deprecated) mcp tooling and the stress tests.
+"""
+from __future__ import annotations
+
+from dataclasses import dataclass
+from typing import Any
+
+
+# --------------------------------------------------------------------------- #
+# Exceptions
+# --------------------------------------------------------------------------- #
+class IDAError(Exception):
+ """Base class for all client errors."""
+
+
+class IDAConnectionError(IDAError):
+ """The transport could not be established or was lost."""
+
+
+class IDATimeoutError(IDAError):
+ """A request exceeded its deadline."""
+
+
+class IDAProtocolError(IDAError):
+ """Malformed or unexpected HTTP / JSON-RPC framing."""
+
+
+class IDARPCError(IDAError):
+ """The JSON-RPC envelope carried an ``error`` object."""
+
+ def __init__(self, code: int, message: str, data: Any = None):
+ super().__init__(f"JSON-RPC error {code}: {message}")
+ self.code = code
+ self.message = message
+ self.data = data
+
+
+class IDAToolError(IDAError):
+ """A tool call returned ``result.isError == true`` (a hard failure)."""
+
+ def __init__(self, tool: str, message: str):
+ super().__init__(f"tool {tool!r} failed: {message}")
+ self.tool = tool
+ self.message = message
+
+
+class IDASessionError(IDAError):
+ """No IDB session is open, or several are and none was pinned."""
+
+
+# --------------------------------------------------------------------------- #
+# Session model
+# --------------------------------------------------------------------------- #
+@dataclass(frozen=True)
+class Session:
+ session_id: str
+ filename: str
+ input_path: str
+ is_active: bool = False
+ is_analyzing: bool = False
+
+ @classmethod
+ def from_dict(cls, d: dict) -> "Session":
+ return cls(
+ session_id=d.get("session_id", ""),
+ filename=d.get("filename", ""),
+ input_path=d.get("input_path", ""),
+ is_active=bool(d.get("is_active", False)),
+ is_analyzing=bool(d.get("is_analyzing", False)),
+ )
diff --git a/idatui/worker_client.py b/idatui/worker_client.py
index 8dd8491..27fa660 100644
--- a/idatui/worker_client.py
+++ b/idatui/worker_client.py
@@ -23,7 +23,7 @@ import time
import uuid
from typing import Any
-from .client import IDAToolError, IDAConnectionError, Session
+from .errors import IDAToolError, IDAConnectionError, Session
from .worker import recv as _recv
from .worker import send as _send