strix-agent 0.1.9__py3-none-any.whl → 0.1.10__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,173 +5,133 @@ import sys
5
5
  import threading
6
6
  from typing import Any
7
7
 
8
- from .terminal_instance import TerminalInstance
8
+ from .terminal_session import TerminalSession
9
9
 
10
10
 
11
11
  class TerminalManager:
12
12
  def __init__(self) -> None:
13
- self.terminals: dict[str, TerminalInstance] = {}
13
+ self.sessions: dict[str, TerminalSession] = {}
14
14
  self._lock = threading.Lock()
15
15
  self.default_terminal_id = "default"
16
+ self.default_timeout = 30.0
16
17
 
17
18
  self._register_cleanup_handlers()
18
19
 
19
- def create_terminal(
20
- self, terminal_id: str | None = None, inputs: list[str] | None = None
20
+ def execute_command(
21
+ self,
22
+ command: str,
23
+ is_input: bool = False,
24
+ timeout: float | None = None,
25
+ terminal_id: str | None = None,
26
+ no_enter: bool = False,
21
27
  ) -> dict[str, Any]:
22
28
  if terminal_id is None:
23
29
  terminal_id = self.default_terminal_id
24
30
 
25
- with self._lock:
26
- if terminal_id in self.terminals:
27
- raise ValueError(f"Terminal '{terminal_id}' already exists")
28
-
29
- initial_command = None
30
- if inputs:
31
- command_parts: list[str] = []
32
- for input_item in inputs:
33
- if input_item == "Enter":
34
- initial_command = " ".join(command_parts) + "\n"
35
- break
36
- if input_item.startswith("literal:"):
37
- command_parts.append(input_item[8:])
38
- elif input_item not in [
39
- "Space",
40
- "Tab",
41
- "Backspace",
42
- ]:
43
- command_parts.append(input_item)
44
-
45
- try:
46
- terminal = TerminalInstance(terminal_id, initial_command)
47
- self.terminals[terminal_id] = terminal
48
-
49
- if inputs and not initial_command:
50
- terminal.send_input(inputs)
51
- result = terminal.wait(2.0)
52
- else:
53
- result = terminal.wait(1.0)
54
-
55
- result["message"] = f"Terminal '{terminal_id}' created successfully"
56
-
57
- except (OSError, ValueError, RuntimeError) as e:
58
- raise RuntimeError(f"Failed to create terminal '{terminal_id}': {e}") from e
59
- else:
60
- return result
61
-
62
- def send_input(
63
- self, terminal_id: str | None = None, inputs: list[str] | None = None
64
- ) -> dict[str, Any]:
65
- if terminal_id is None:
66
- terminal_id = self.default_terminal_id
67
-
68
- if not inputs:
69
- raise ValueError("No inputs provided")
70
-
71
- with self._lock:
72
- if terminal_id not in self.terminals:
73
- raise ValueError(f"Terminal '{terminal_id}' not found")
74
-
75
- terminal = self.terminals[terminal_id]
31
+ session = self._get_or_create_session(terminal_id)
76
32
 
77
33
  try:
78
- terminal.send_input(inputs)
79
- result = terminal.wait(2.0)
80
- result["message"] = f"Input sent to terminal '{terminal_id}'"
81
- except (OSError, ValueError, RuntimeError) as e:
82
- raise RuntimeError(f"Failed to send input to terminal '{terminal_id}': {e}") from e
83
- else:
84
- return result
34
+ result = session.execute(command, is_input, timeout or self.default_timeout, no_enter)
85
35
 
86
- def wait_terminal(
87
- self, terminal_id: str | None = None, duration: float = 1.0
88
- ) -> dict[str, Any]:
89
- if terminal_id is None:
90
- terminal_id = self.default_terminal_id
91
-
92
- with self._lock:
93
- if terminal_id not in self.terminals:
94
- raise ValueError(f"Terminal '{terminal_id}' not found")
36
+ return {
37
+ "content": result["content"],
38
+ "command": command,
39
+ "terminal_id": terminal_id,
40
+ "status": result["status"],
41
+ "exit_code": result.get("exit_code"),
42
+ "working_dir": result.get("working_dir"),
43
+ }
95
44
 
96
- terminal = self.terminals[terminal_id]
45
+ except RuntimeError as e:
46
+ return {
47
+ "error": str(e),
48
+ "command": command,
49
+ "terminal_id": terminal_id,
50
+ "content": "",
51
+ "status": "error",
52
+ "exit_code": None,
53
+ "working_dir": None,
54
+ }
55
+ except OSError as e:
56
+ return {
57
+ "error": f"System error: {e}",
58
+ "command": command,
59
+ "terminal_id": terminal_id,
60
+ "content": "",
61
+ "status": "error",
62
+ "exit_code": None,
63
+ "working_dir": None,
64
+ }
97
65
 
98
- try:
99
- result = terminal.wait(duration)
100
- result["message"] = f"Waited {duration}s on terminal '{terminal_id}'"
101
- except (OSError, ValueError, RuntimeError) as e:
102
- raise RuntimeError(f"Failed to wait on terminal '{terminal_id}': {e}") from e
103
- else:
104
- return result
66
+ def _get_or_create_session(self, terminal_id: str) -> TerminalSession:
67
+ with self._lock:
68
+ if terminal_id not in self.sessions:
69
+ self.sessions[terminal_id] = TerminalSession(terminal_id)
70
+ return self.sessions[terminal_id]
105
71
 
106
- def close_terminal(self, terminal_id: str | None = None) -> dict[str, Any]:
72
+ def close_session(self, terminal_id: str | None = None) -> dict[str, Any]:
107
73
  if terminal_id is None:
108
74
  terminal_id = self.default_terminal_id
109
75
 
110
76
  with self._lock:
111
- if terminal_id not in self.terminals:
112
- raise ValueError(f"Terminal '{terminal_id}' not found")
77
+ if terminal_id not in self.sessions:
78
+ return {
79
+ "terminal_id": terminal_id,
80
+ "message": f"Terminal '{terminal_id}' not found",
81
+ "status": "not_found",
82
+ }
113
83
 
114
- terminal = self.terminals.pop(terminal_id)
84
+ session = self.sessions.pop(terminal_id)
115
85
 
116
86
  try:
117
- terminal.close()
118
- except (OSError, ValueError, RuntimeError) as e:
119
- raise RuntimeError(f"Failed to close terminal '{terminal_id}': {e}") from e
87
+ session.close()
88
+ except (RuntimeError, OSError) as e:
89
+ return {
90
+ "terminal_id": terminal_id,
91
+ "error": f"Failed to close terminal '{terminal_id}': {e}",
92
+ "status": "error",
93
+ }
120
94
  else:
121
95
  return {
122
96
  "terminal_id": terminal_id,
123
97
  "message": f"Terminal '{terminal_id}' closed successfully",
124
- "snapshot": "",
125
- "is_running": False,
98
+ "status": "closed",
126
99
  }
127
100
 
128
- def get_terminal_snapshot(self, terminal_id: str | None = None) -> dict[str, Any]:
129
- if terminal_id is None:
130
- terminal_id = self.default_terminal_id
131
-
132
- with self._lock:
133
- if terminal_id not in self.terminals:
134
- raise ValueError(f"Terminal '{terminal_id}' not found")
135
-
136
- terminal = self.terminals[terminal_id]
137
-
138
- return terminal.get_snapshot()
139
-
140
- def list_terminals(self) -> dict[str, Any]:
101
+ def list_sessions(self) -> dict[str, Any]:
141
102
  with self._lock:
142
- terminal_info = {}
143
- for tid, terminal in self.terminals.items():
144
- terminal_info[tid] = {
145
- "is_running": terminal.is_running,
146
- "is_alive": terminal.is_alive(),
147
- "process_id": terminal.process.pid if terminal.process else None,
103
+ session_info: dict[str, dict[str, Any]] = {}
104
+ for tid, session in self.sessions.items():
105
+ session_info[tid] = {
106
+ "is_running": session.is_running(),
107
+ "working_dir": session.get_working_dir(),
148
108
  }
149
109
 
150
- return {"terminals": terminal_info, "total_count": len(terminal_info)}
110
+ return {"sessions": session_info, "total_count": len(session_info)}
151
111
 
152
- def cleanup_dead_terminals(self) -> None:
112
+ def cleanup_dead_sessions(self) -> None:
153
113
  with self._lock:
154
- dead_terminals = []
155
- for tid, terminal in self.terminals.items():
156
- if not terminal.is_alive():
157
- dead_terminals.append(tid)
114
+ dead_sessions: list[str] = []
115
+ for tid, session in self.sessions.items():
116
+ if not session.is_running():
117
+ dead_sessions.append(tid)
158
118
 
159
- for tid in dead_terminals:
160
- terminal = self.terminals.pop(tid)
119
+ for tid in dead_sessions:
120
+ session = self.sessions.pop(tid)
161
121
  with contextlib.suppress(Exception):
162
- terminal.close()
122
+ session.close()
163
123
 
164
- def close_all_terminals(self) -> None:
124
+ def close_all_sessions(self) -> None:
165
125
  with self._lock:
166
- terminals_to_close = list(self.terminals.values())
167
- self.terminals.clear()
126
+ sessions_to_close = list(self.sessions.values())
127
+ self.sessions.clear()
168
128
 
169
- for terminal in terminals_to_close:
129
+ for session in sessions_to_close:
170
130
  with contextlib.suppress(Exception):
171
- terminal.close()
131
+ session.close()
172
132
 
173
133
  def _register_cleanup_handlers(self) -> None:
174
- atexit.register(self.close_all_terminals)
134
+ atexit.register(self.close_all_sessions)
175
135
 
176
136
  signal.signal(signal.SIGTERM, self._signal_handler)
177
137
  signal.signal(signal.SIGINT, self._signal_handler)
@@ -180,7 +140,7 @@ class TerminalManager:
180
140
  signal.signal(signal.SIGHUP, self._signal_handler)
181
141
 
182
142
  def _signal_handler(self, _signum: int, _frame: Any) -> None:
183
- self.close_all_terminals()
143
+ self.close_all_sessions()
184
144
  sys.exit(0)
185
145
 
186
146