llmshell-cli 0.0.1__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.
gpt_shell/utils.py ADDED
@@ -0,0 +1,305 @@
1
+ """Utility functions for llmshell."""
2
+
3
+ import subprocess
4
+ import sys
5
+ from typing import Optional, Tuple
6
+ from rich.console import Console
7
+ from rich.syntax import Syntax
8
+ from rich.panel import Panel
9
+ from rich.table import Table
10
+ from rich import print as rprint
11
+
12
+
13
+ console = Console()
14
+
15
+
16
+ def print_success(message: str) -> None:
17
+ """
18
+ Print success message.
19
+
20
+ Args:
21
+ message: Message to print
22
+ """
23
+ console.print(f"[green]✓[/green] {message}")
24
+
25
+
26
+ def print_error(message: str) -> None:
27
+ """
28
+ Print error message.
29
+
30
+ Args:
31
+ message: Message to print
32
+ """
33
+ console.print(f"[red]✗[/red] {message}", style="red")
34
+
35
+
36
+ def print_warning(message: str) -> None:
37
+ """
38
+ Print warning message.
39
+
40
+ Args:
41
+ message: Message to print
42
+ """
43
+ console.print(f"[yellow]⚠[/yellow] {message}", style="yellow")
44
+
45
+
46
+ def print_info(message: str) -> None:
47
+ """
48
+ Print info message.
49
+
50
+ Args:
51
+ message: Message to print
52
+ """
53
+ console.print(f"[blue]ℹ[/blue] {message}", style="blue")
54
+
55
+
56
+ def print_command(command: str, title: str = "Generated Command") -> None:
57
+ """
58
+ Print command with syntax highlighting.
59
+
60
+ Args:
61
+ command: Command to display
62
+ title: Panel title
63
+ """
64
+ syntax = Syntax(command, "bash", theme="monokai", line_numbers=False)
65
+ panel = Panel(syntax, title=title, border_style="cyan")
66
+ console.print(panel)
67
+
68
+
69
+ def print_config_table(config_dict: dict, title: str = "Configuration") -> None:
70
+ """
71
+ Print configuration as a table.
72
+
73
+ Args:
74
+ config_dict: Configuration dictionary
75
+ title: Table title
76
+ """
77
+ table = Table(title=title, show_header=True, header_style="bold cyan")
78
+ table.add_column("Key", style="cyan", no_wrap=True)
79
+ table.add_column("Value", style="white")
80
+
81
+ def flatten_dict(d: dict, parent_key: str = "") -> dict:
82
+ """Flatten nested dictionary."""
83
+ items = []
84
+ for k, v in d.items():
85
+ new_key = f"{parent_key}.{k}" if parent_key else k
86
+ if isinstance(v, dict):
87
+ items.extend(flatten_dict(v, new_key).items())
88
+ else:
89
+ items.append((new_key, v))
90
+ return dict(items)
91
+
92
+ flat_config = flatten_dict(config_dict)
93
+ for key, value in flat_config.items():
94
+ # Mask sensitive values
95
+ if any(sensitive in key.lower() for sensitive in ["key", "token", "password"]):
96
+ if value and value is not None:
97
+ display_value = "****" + str(value)[-4:] if len(str(value)) > 4 else "****"
98
+ else:
99
+ display_value = "[dim]not set[/dim]"
100
+ else:
101
+ display_value = str(value) if value is not None else "[dim]not set[/dim]"
102
+
103
+ table.add_row(key, display_value)
104
+
105
+ console.print(table)
106
+
107
+
108
+ def print_backend_status(backends: list) -> None:
109
+ """
110
+ Print backend status table.
111
+
112
+ Args:
113
+ backends: List of tuples (name, available, status)
114
+ """
115
+ table = Table(title="Backend Status", show_header=True, header_style="bold cyan")
116
+ table.add_column("Backend", style="cyan", no_wrap=True)
117
+ table.add_column("Status", style="white")
118
+ table.add_column("Details", style="dim")
119
+
120
+ for name, available, status in backends:
121
+ status_icon = "[green]✓ Available[/green]" if available else "[red]✗ Unavailable[/red]"
122
+ table.add_row(name, status_icon, status)
123
+
124
+ console.print(table)
125
+
126
+
127
+ def confirm_execution(command: str) -> bool:
128
+ """
129
+ Ask user to confirm command execution.
130
+
131
+ Args:
132
+ command: Command to execute
133
+
134
+ Returns:
135
+ True if user confirms, False otherwise
136
+ """
137
+ print_command(command, "Command to Execute")
138
+
139
+ response = console.input("\n[yellow]Execute this command?[/yellow] [dim](y/n)[/dim]: ")
140
+ return response.lower() in ["y", "yes"]
141
+
142
+
143
+ def execute_command(command: str, dry_run: bool = False) -> Tuple[int, str, str]:
144
+ """
145
+ Execute shell command.
146
+
147
+ Args:
148
+ command: Command to execute
149
+ dry_run: If True, don't actually execute
150
+
151
+ Returns:
152
+ Tuple of (return_code, stdout, stderr)
153
+ """
154
+ if dry_run:
155
+ print_info(f"Dry run mode - command not executed: {command}")
156
+ return (0, "", "")
157
+
158
+ try:
159
+ result = subprocess.run(
160
+ command,
161
+ shell=True,
162
+ capture_output=True,
163
+ text=True,
164
+ timeout=30,
165
+ )
166
+ return (result.returncode, result.stdout, result.stderr)
167
+ except subprocess.TimeoutExpired:
168
+ return (1, "", "Command timed out after 30 seconds")
169
+ except Exception as e:
170
+ return (1, "", str(e))
171
+
172
+
173
+ def print_execution_result(returncode: int, stdout: str, stderr: str) -> None:
174
+ """
175
+ Print command execution result.
176
+
177
+ Args:
178
+ returncode: Command return code
179
+ stdout: Standard output
180
+ stderr: Standard error
181
+ """
182
+ if returncode == 0:
183
+ print_success("Command executed successfully")
184
+ if stdout:
185
+ console.print("\n[bold]Output:[/bold]")
186
+ console.print(stdout)
187
+ else:
188
+ print_error(f"Command failed with exit code {returncode}")
189
+ if stderr:
190
+ console.print("\n[bold red]Error:[/bold red]")
191
+ console.print(stderr, style="red")
192
+ if stdout:
193
+ console.print("\n[bold]Output:[/bold]")
194
+ console.print(stdout)
195
+
196
+
197
+ def check_command_exists(command: str) -> bool:
198
+ """
199
+ Check if a command exists in PATH.
200
+
201
+ Args:
202
+ command: Command name to check
203
+
204
+ Returns:
205
+ True if command exists, False otherwise
206
+ """
207
+ try:
208
+ result = subprocess.run(
209
+ ["which", command],
210
+ capture_output=True,
211
+ text=True,
212
+ )
213
+ return result.returncode == 0
214
+ except Exception:
215
+ return False
216
+
217
+
218
+ def get_shell_type() -> str:
219
+ """
220
+ Detect current shell type.
221
+
222
+ Returns:
223
+ Shell name (bash, zsh, fish, etc.)
224
+ """
225
+ shell = subprocess.run(
226
+ ["echo", "$SHELL"],
227
+ capture_output=True,
228
+ text=True,
229
+ shell=True,
230
+ )
231
+ shell_path = shell.stdout.strip()
232
+
233
+ if "zsh" in shell_path:
234
+ return "zsh"
235
+ elif "bash" in shell_path:
236
+ return "bash"
237
+ elif "fish" in shell_path:
238
+ return "fish"
239
+ else:
240
+ return "unknown"
241
+
242
+
243
+ def format_file_size(size_bytes: int) -> str:
244
+ """
245
+ Format file size in human-readable format.
246
+
247
+ Args:
248
+ size_bytes: Size in bytes
249
+
250
+ Returns:
251
+ Formatted size string
252
+ """
253
+ for unit in ["B", "KB", "MB", "GB", "TB"]:
254
+ if size_bytes < 1024.0:
255
+ return f"{size_bytes:.1f} {unit}"
256
+ size_bytes /= 1024.0
257
+ return f"{size_bytes:.1f} PB"
258
+
259
+
260
+ def is_dangerous_command(command: str) -> bool:
261
+ """
262
+ Check if command is potentially dangerous.
263
+
264
+ Args:
265
+ command: Command to check
266
+
267
+ Returns:
268
+ True if command is dangerous, False otherwise
269
+ """
270
+ dangerous_patterns = [
271
+ "rm -rf /",
272
+ "rm -rf /*",
273
+ "mkfs",
274
+ "dd if=/dev/zero",
275
+ "> /dev/sda",
276
+ ":(){ :|:& };:", # Fork bomb
277
+ "chmod -R 777 /",
278
+ ]
279
+
280
+ command_lower = command.lower().strip()
281
+
282
+ for pattern in dangerous_patterns:
283
+ if pattern in command_lower:
284
+ return True
285
+
286
+ return False
287
+
288
+
289
+ def print_danger_warning(command: str) -> None:
290
+ """
291
+ Print warning for dangerous command.
292
+
293
+ Args:
294
+ command: Dangerous command
295
+ """
296
+ console.print(
297
+ "\n[bold red]⚠️ WARNING: POTENTIALLY DANGEROUS COMMAND ⚠️[/bold red]\n",
298
+ style="on red"
299
+ )
300
+ console.print(
301
+ "This command may cause system damage or data loss.\n"
302
+ "Please review it carefully before execution.\n",
303
+ style="bold yellow"
304
+ )
305
+ print_command(command, "⚠️ Dangerous Command")