mcp-ticketer 0.1.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.

Potentially problematic release.


This version of mcp-ticketer might be problematic. Click here for more details.

@@ -0,0 +1,285 @@
1
+ """Queue-related CLI commands."""
2
+
3
+ import typer
4
+ from rich.console import Console
5
+ from rich.table import Table
6
+ from rich import print as rprint
7
+ from datetime import datetime
8
+
9
+ from ..queue import Queue, QueueStatus, WorkerManager, Worker
10
+
11
+ app = typer.Typer(
12
+ name="queue",
13
+ help="Queue management commands"
14
+ )
15
+ console = Console()
16
+
17
+
18
+ @app.command("list")
19
+ def list_queue(
20
+ status: QueueStatus = typer.Option(
21
+ None,
22
+ "--status",
23
+ "-s",
24
+ help="Filter by status"
25
+ ),
26
+ limit: int = typer.Option(
27
+ 25,
28
+ "--limit",
29
+ "-l",
30
+ help="Maximum items to show"
31
+ )
32
+ ):
33
+ """List queue items."""
34
+ queue = Queue()
35
+ items = queue.list_items(status=status, limit=limit)
36
+
37
+ if not items:
38
+ console.print("[yellow]No items in queue[/yellow]")
39
+ return
40
+
41
+ # Create table
42
+ table = Table(title=f"Queue Items ({len(items)} shown)")
43
+ table.add_column("Queue ID", style="cyan", no_wrap=True)
44
+ table.add_column("Operation", style="white")
45
+ table.add_column("Adapter", style="blue")
46
+ table.add_column("Status", style="green")
47
+ table.add_column("Created", style="yellow")
48
+ table.add_column("Retries", style="red")
49
+
50
+ for item in items:
51
+ # Format status with color
52
+ if item.status == QueueStatus.COMPLETED:
53
+ status_str = f"[green]{item.status}[/green]"
54
+ elif item.status == QueueStatus.FAILED:
55
+ status_str = f"[red]{item.status}[/red]"
56
+ elif item.status == QueueStatus.PROCESSING:
57
+ status_str = f"[yellow]{item.status}[/yellow]"
58
+ else:
59
+ status_str = item.status
60
+
61
+ # Format time
62
+ created_str = item.created_at.strftime("%Y-%m-%d %H:%M:%S")
63
+
64
+ table.add_row(
65
+ item.id,
66
+ item.operation,
67
+ item.adapter,
68
+ status_str,
69
+ created_str,
70
+ str(item.retry_count)
71
+ )
72
+
73
+ console.print(table)
74
+
75
+ # Show summary
76
+ stats = queue.get_stats()
77
+ console.print("\n[bold]Queue Summary:[/bold]")
78
+ console.print(f" Pending: {stats.get('pending', 0)}")
79
+ console.print(f" Processing: {stats.get('processing', 0)}")
80
+ console.print(f" Completed: {stats.get('completed', 0)}")
81
+ console.print(f" Failed: {stats.get('failed', 0)}")
82
+
83
+
84
+ @app.command("retry")
85
+ def retry_item(
86
+ queue_id: str = typer.Argument(..., help="Queue ID to retry")
87
+ ):
88
+ """Retry a failed queue item."""
89
+ queue = Queue()
90
+ item = queue.get_item(queue_id)
91
+
92
+ if not item:
93
+ console.print(f"[red]Queue item not found: {queue_id}[/red]")
94
+ raise typer.Exit(1)
95
+
96
+ if item.status != QueueStatus.FAILED:
97
+ console.print(f"[yellow]Item {queue_id} is not failed (status: {item.status})[/yellow]")
98
+ raise typer.Exit(1)
99
+
100
+ # Reset to pending
101
+ queue.update_status(queue_id, QueueStatus.PENDING, error_message=None)
102
+ console.print(f"[green]✓[/green] Queue item {queue_id} reset for retry")
103
+
104
+ # Start worker if needed
105
+ manager = WorkerManager()
106
+ if manager.start_if_needed():
107
+ console.print("[dim]Worker started to process retry[/dim]")
108
+
109
+
110
+ @app.command("clear")
111
+ def clear_queue(
112
+ status: QueueStatus = typer.Option(
113
+ None,
114
+ "--status",
115
+ "-s",
116
+ help="Clear only items with this status"
117
+ ),
118
+ days: int = typer.Option(
119
+ 7,
120
+ "--days",
121
+ "-d",
122
+ help="Clear items older than this many days"
123
+ ),
124
+ confirm: bool = typer.Option(
125
+ False,
126
+ "--yes",
127
+ "-y",
128
+ help="Skip confirmation"
129
+ )
130
+ ):
131
+ """Clear old queue items."""
132
+ queue = Queue()
133
+
134
+ if not confirm:
135
+ if status:
136
+ msg = f"Clear all {status} items older than {days} days?"
137
+ else:
138
+ msg = f"Clear all completed/failed items older than {days} days?"
139
+
140
+ if not typer.confirm(msg):
141
+ console.print("[yellow]Cancelled[/yellow]")
142
+ raise typer.Exit(0)
143
+
144
+ queue.cleanup_old(days=days)
145
+ console.print(f"[green]✓[/green] Cleared old queue items")
146
+
147
+
148
+ # Worker commands
149
+ worker_app = typer.Typer(
150
+ name="worker",
151
+ help="Worker management commands"
152
+ )
153
+
154
+
155
+ @worker_app.command("start")
156
+ def start_worker():
157
+ """Start the background worker."""
158
+ manager = WorkerManager()
159
+
160
+ if manager.is_running():
161
+ console.print("[yellow]Worker is already running[/yellow]")
162
+ status = manager.get_status()
163
+ console.print(f"PID: {status.get('pid')}")
164
+ return
165
+
166
+ if manager.start():
167
+ console.print("[green]✓[/green] Worker started successfully")
168
+ status = manager.get_status()
169
+ console.print(f"PID: {status.get('pid')}")
170
+ else:
171
+ console.print("[red]✗[/red] Failed to start worker")
172
+ raise typer.Exit(1)
173
+
174
+
175
+ @worker_app.command("stop")
176
+ def stop_worker():
177
+ """Stop the background worker."""
178
+ manager = WorkerManager()
179
+
180
+ if not manager.is_running():
181
+ console.print("[yellow]Worker is not running[/yellow]")
182
+ return
183
+
184
+ if manager.stop():
185
+ console.print("[green]✓[/green] Worker stopped successfully")
186
+ else:
187
+ console.print("[red]✗[/red] Failed to stop worker")
188
+ raise typer.Exit(1)
189
+
190
+
191
+ @worker_app.command("restart")
192
+ def restart_worker():
193
+ """Restart the background worker."""
194
+ manager = WorkerManager()
195
+
196
+ if manager.restart():
197
+ console.print("[green]✓[/green] Worker restarted successfully")
198
+ status = manager.get_status()
199
+ console.print(f"PID: {status.get('pid')}")
200
+ else:
201
+ console.print("[red]✗[/red] Failed to restart worker")
202
+ raise typer.Exit(1)
203
+
204
+
205
+ @worker_app.command("status")
206
+ def worker_status():
207
+ """Check worker status."""
208
+ manager = WorkerManager()
209
+ status = manager.get_status()
210
+
211
+ if status["running"]:
212
+ console.print(f"[green]● Worker is running[/green]")
213
+ console.print(f" PID: {status.get('pid')}")
214
+
215
+ if "cpu_percent" in status:
216
+ console.print(f" CPU: {status['cpu_percent']:.1f}%")
217
+ console.print(f" Memory: {status['memory_mb']:.1f} MB")
218
+
219
+ # Format uptime
220
+ if "create_time" in status:
221
+ uptime = datetime.now().timestamp() - status["create_time"]
222
+ hours = int(uptime // 3600)
223
+ minutes = int((uptime % 3600) // 60)
224
+ console.print(f" Uptime: {hours}h {minutes}m")
225
+ else:
226
+ console.print("[red]○ Worker is not running[/red]")
227
+
228
+ # Show queue stats
229
+ if "queue" in status:
230
+ console.print("\n[bold]Queue Status:[/bold]")
231
+ queue_stats = status["queue"]
232
+ console.print(f" Pending: {queue_stats.get('pending', 0)}")
233
+ console.print(f" Processing: {queue_stats.get('processing', 0)}")
234
+ console.print(f" Completed: {queue_stats.get('completed', 0)}")
235
+ console.print(f" Failed: {queue_stats.get('failed', 0)}")
236
+
237
+
238
+ @worker_app.command("logs")
239
+ def worker_logs(
240
+ lines: int = typer.Option(
241
+ 50,
242
+ "--lines",
243
+ "-n",
244
+ help="Number of lines to show"
245
+ ),
246
+ follow: bool = typer.Option(
247
+ False,
248
+ "--follow",
249
+ "-f",
250
+ help="Follow log output"
251
+ )
252
+ ):
253
+ """View worker logs."""
254
+ import time
255
+ from pathlib import Path
256
+
257
+ log_file = Path.home() / ".mcp-ticketer" / "logs" / "worker.log"
258
+
259
+ if not log_file.exists():
260
+ console.print("[yellow]No log file found[/yellow]")
261
+ raise typer.Exit(1)
262
+
263
+ if follow:
264
+ # Follow mode - like tail -f
265
+ console.print("[dim]Following worker logs (Ctrl+C to stop)...[/dim]\n")
266
+ try:
267
+ with open(log_file, "r") as f:
268
+ # Go to end of file
269
+ f.seek(0, 2)
270
+ while True:
271
+ line = f.readline()
272
+ if line:
273
+ console.print(line, end="")
274
+ else:
275
+ time.sleep(0.1)
276
+ except KeyboardInterrupt:
277
+ console.print("\n[dim]Stopped following logs[/dim]")
278
+ else:
279
+ # Show last N lines
280
+ logs = Worker.get_logs(lines=lines)
281
+ console.print(logs)
282
+
283
+
284
+ # Add worker subcommand to queue app
285
+ app.add_typer(worker_app, name="worker")