deepctl-cmd-debug-browser 0.1.10__tar.gz → 0.1.11__tar.gz

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.
Files changed (16) hide show
  1. {deepctl_cmd_debug_browser-0.1.10 → deepctl_cmd_debug_browser-0.1.11}/PKG-INFO +45 -2
  2. deepctl_cmd_debug_browser-0.1.11/README.md +46 -0
  3. {deepctl_cmd_debug_browser-0.1.10 → deepctl_cmd_debug_browser-0.1.11}/pyproject.toml +1 -1
  4. {deepctl_cmd_debug_browser-0.1.10 → deepctl_cmd_debug_browser-0.1.11}/src/deepctl_cmd_debug_browser/command.py +61 -44
  5. {deepctl_cmd_debug_browser-0.1.10 → deepctl_cmd_debug_browser-0.1.11}/src/deepctl_cmd_debug_browser/models.py +2 -0
  6. {deepctl_cmd_debug_browser-0.1.10 → deepctl_cmd_debug_browser-0.1.11}/src/deepctl_cmd_debug_browser/static/debug.html +48 -0
  7. {deepctl_cmd_debug_browser-0.1.10 → deepctl_cmd_debug_browser-0.1.11}/src/deepctl_cmd_debug_browser.egg-info/PKG-INFO +45 -2
  8. deepctl_cmd_debug_browser-0.1.10/README.md +0 -3
  9. {deepctl_cmd_debug_browser-0.1.10 → deepctl_cmd_debug_browser-0.1.11}/setup.cfg +0 -0
  10. {deepctl_cmd_debug_browser-0.1.10 → deepctl_cmd_debug_browser-0.1.11}/src/deepctl_cmd_debug_browser/__init__.py +0 -0
  11. {deepctl_cmd_debug_browser-0.1.10 → deepctl_cmd_debug_browser-0.1.11}/src/deepctl_cmd_debug_browser/static/__init__.py +0 -0
  12. {deepctl_cmd_debug_browser-0.1.10 → deepctl_cmd_debug_browser-0.1.11}/src/deepctl_cmd_debug_browser.egg-info/SOURCES.txt +0 -0
  13. {deepctl_cmd_debug_browser-0.1.10 → deepctl_cmd_debug_browser-0.1.11}/src/deepctl_cmd_debug_browser.egg-info/dependency_links.txt +0 -0
  14. {deepctl_cmd_debug_browser-0.1.10 → deepctl_cmd_debug_browser-0.1.11}/src/deepctl_cmd_debug_browser.egg-info/entry_points.txt +0 -0
  15. {deepctl_cmd_debug_browser-0.1.10 → deepctl_cmd_debug_browser-0.1.11}/src/deepctl_cmd_debug_browser.egg-info/requires.txt +0 -0
  16. {deepctl_cmd_debug_browser-0.1.10 → deepctl_cmd_debug_browser-0.1.11}/src/deepctl_cmd_debug_browser.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: deepctl-cmd-debug-browser
3
- Version: 0.1.10
3
+ Version: 0.1.11
4
4
  Summary: Browser debug subcommand for deepctl
5
5
  Author-email: Deepgram <devrel@deepgram.com>
6
6
  Maintainer-email: Deepgram <devrel@deepgram.com>
@@ -26,4 +26,47 @@ Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
26
26
 
27
27
  # deepctl-cmd-debug-browser
28
28
 
29
- Browser debug subcommand for the Deepgram CLI (deepctl). This package provides the `browser` subcommand under the `debug` command group, allowing users to debug browser-related issues when using Deepgram's web-based features
29
+ > Part of [deepctl](https://github.com/deepgram/cli) — Official Deepgram CLI
30
+
31
+ Browser debug subcommand for deepctl
32
+
33
+ This is a subcommand of `deepctl debug`.
34
+
35
+ ## Installation
36
+
37
+ This package is included with deepctl and does not need to be installed separately.
38
+
39
+ ### Install deepctl
40
+
41
+ ```bash
42
+ # Install with pip
43
+ pip install deepctl
44
+
45
+ # Or install with uv
46
+ uv tool install deepctl
47
+
48
+ # Or install with pipx
49
+ pipx install deepctl
50
+
51
+ # Or run without installing
52
+ uvx deepctl --help
53
+ pipx run deepctl --help
54
+ ```
55
+
56
+ ## Commands
57
+
58
+ | Command | Entry Point |
59
+ |---------|-------------|
60
+ | `deepctl debug browser` | `deepctl_cmd_debug_browser.command:BrowserCommand` |
61
+
62
+ ## Dependencies
63
+
64
+ - `click>=8.0.0`
65
+ - `rich>=13.0.0`
66
+ - `pydantic>=2.0.0`
67
+ - `httpx>=0.24.0`
68
+ - `aiohttp>=3.9.0`
69
+
70
+ ## License
71
+
72
+ MIT — see [LICENSE](../../LICENSE)
@@ -0,0 +1,46 @@
1
+ # deepctl-cmd-debug-browser
2
+
3
+ > Part of [deepctl](https://github.com/deepgram/cli) — Official Deepgram CLI
4
+
5
+ Browser debug subcommand for deepctl
6
+
7
+ This is a subcommand of `deepctl debug`.
8
+
9
+ ## Installation
10
+
11
+ This package is included with deepctl and does not need to be installed separately.
12
+
13
+ ### Install deepctl
14
+
15
+ ```bash
16
+ # Install with pip
17
+ pip install deepctl
18
+
19
+ # Or install with uv
20
+ uv tool install deepctl
21
+
22
+ # Or install with pipx
23
+ pipx install deepctl
24
+
25
+ # Or run without installing
26
+ uvx deepctl --help
27
+ pipx run deepctl --help
28
+ ```
29
+
30
+ ## Commands
31
+
32
+ | Command | Entry Point |
33
+ |---------|-------------|
34
+ | `deepctl debug browser` | `deepctl_cmd_debug_browser.command:BrowserCommand` |
35
+
36
+ ## Dependencies
37
+
38
+ - `click>=8.0.0`
39
+ - `rich>=13.0.0`
40
+ - `pydantic>=2.0.0`
41
+ - `httpx>=0.24.0`
42
+ - `aiohttp>=3.9.0`
43
+
44
+ ## License
45
+
46
+ MIT — see [LICENSE](../../LICENSE)
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "deepctl-cmd-debug-browser"
7
- version = "0.1.10"
7
+ version = "0.1.11" # x-release-please-version
8
8
  description = "Browser debug subcommand for deepctl"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -36,6 +36,16 @@ class BrowserCommand(BaseCommand):
36
36
 
37
37
  # Browser debug doesn't require auth
38
38
  requires_auth = False
39
+
40
+ examples = [
41
+ "dg debug browser",
42
+ "dg debug browser --port 8080",
43
+ ]
44
+ agent_help = (
45
+ "Launch a local web page that tests browser capabilities for "
46
+ "Deepgram services — WebSocket support, microphone access, "
47
+ "audio encoding, and WebRTC. Opens a browser automatically."
48
+ )
39
49
  requires_project = False
40
50
  ci_friendly = False # This command opens a browser
41
51
 
@@ -68,13 +78,18 @@ class BrowserCommand(BaseCommand):
68
78
  {
69
79
  "names": ["--timeout"],
70
80
  "help": (
71
- "Timeout in seconds to wait for browser connection "
72
- "(default: 60)"
81
+ "Timeout in seconds to wait for browser connection (default: 60)"
73
82
  ),
74
83
  "type": int,
75
84
  "default": 60,
76
85
  "is_option": True,
77
86
  },
87
+ {
88
+ "names": ["--save-report"],
89
+ "help": "Save debug report to a JSON file",
90
+ "type": str,
91
+ "is_option": True,
92
+ },
78
93
  ]
79
94
 
80
95
  def find_available_port(self, start_port: int = 3000) -> int:
@@ -87,9 +102,7 @@ class BrowserCommand(BaseCommand):
87
102
  return port
88
103
  except OSError:
89
104
  continue
90
- raise RuntimeError(
91
- f"Could not find available port starting from {start_port}"
92
- )
105
+ raise RuntimeError(f"Could not find available port starting from {start_port}")
93
106
 
94
107
  async def websocket_handler(self, request: Any) -> Any:
95
108
  """Handle WebSocket connections from the browser."""
@@ -113,8 +126,8 @@ class BrowserCommand(BaseCommand):
113
126
  cap_data = ws_msg.data.get("result", {})
114
127
  cap_key = ws_msg.data.get("capability")
115
128
  if cap_key and cap_data:
116
- self.capabilities_data[cap_key] = (
117
- BrowserCapability(**cap_data)
129
+ self.capabilities_data[cap_key] = BrowserCapability(
130
+ **cap_data
118
131
  )
119
132
 
120
133
  elif ws_msg.type == MessageType.COMPLETE:
@@ -134,29 +147,25 @@ class BrowserCommand(BaseCommand):
134
147
  "console_api",
135
148
  "timer_apis",
136
149
  "secure_context",
150
+ "microphone_access",
151
+ "websocket_deepgram",
137
152
  ]:
138
153
  if key in caps:
139
- cap_dict[key] = BrowserCapability(
140
- **caps[key]
141
- )
154
+ cap_dict[key] = BrowserCapability(**caps[key])
142
155
  elif key in self.capabilities_data:
143
156
  cap_dict[key] = self.capabilities_data[key]
144
157
 
145
158
  # Store complete capabilities
146
159
  self.capabilities_data = {
147
160
  **cap_dict,
148
- "user_agent": caps.get(
149
- "user_agent", "Unknown"
150
- ),
161
+ "user_agent": caps.get("user_agent", "Unknown"),
151
162
  "overall_compatible": caps.get(
152
163
  "overall_compatible", False
153
164
  ),
154
165
  }
155
166
  self.debug_complete = True
156
167
  elif msg.type == WSMsgType.ERROR:
157
- console.print(
158
- f"[red]WebSocket error: {ws.exception()}[/red]"
159
- )
168
+ console.print(f"[red]WebSocket error: {ws.exception()}[/red]")
160
169
  finally:
161
170
  self.ws_clients.discard(ws)
162
171
  await ws.close()
@@ -192,8 +201,7 @@ class BrowserCommand(BaseCommand):
192
201
  await site.start()
193
202
 
194
203
  console.print(
195
- f"\n[green]✓[/green] Debug server started on port "
196
- f"[cyan]{port}[/cyan]"
204
+ f"\n[green]✓[/green] Debug server started on port [cyan]{port}[/cyan]"
197
205
  )
198
206
 
199
207
  # Wait for debug to complete or timeout
@@ -271,6 +279,8 @@ class BrowserCommand(BaseCommand):
271
279
  ("console_api", "Console API"),
272
280
  ("timer_apis", "Timer APIs"),
273
281
  ("secure_context", "Secure Context"),
282
+ ("microphone_access", "Microphone Access"),
283
+ ("websocket_deepgram", "WebSocket to Deepgram"),
274
284
  ]
275
285
 
276
286
  for field, display_name in capability_fields:
@@ -280,17 +290,14 @@ class BrowserCommand(BaseCommand):
280
290
  status_color = "green" if cap.supported else "red"
281
291
  cap_table.add_row(
282
292
  display_name,
283
- f"[{status_color}]{status_icon} "
284
- f"{cap.details}[/{status_color}]",
293
+ f"[{status_color}]{status_icon} {cap.details}[/{status_color}]",
285
294
  cap.version or "",
286
295
  )
287
296
 
288
297
  console.print(cap_table)
289
298
 
290
299
  # Overall compatibility
291
- console.print(
292
- f"\n[bold]User Agent:[/bold] {capabilities.user_agent}"
293
- )
300
+ console.print(f"\n[bold]User Agent:[/bold] {capabilities.user_agent}")
294
301
  if capabilities.overall_compatible:
295
302
  console.print(
296
303
  "[bold green]✓ Browser is fully compatible with "
@@ -302,6 +309,22 @@ class BrowserCommand(BaseCommand):
302
309
  "with some Deepgram features[/bold red]"
303
310
  )
304
311
 
312
+ def _save_report(self, result: BrowserDebugResult, filename: str) -> None:
313
+ """Save debug report to a JSON file."""
314
+ import json
315
+ from datetime import datetime
316
+
317
+ try:
318
+ report = {
319
+ "timestamp": datetime.now().isoformat(),
320
+ "result": result.model_dump(),
321
+ }
322
+ with open(filename, "w") as f:
323
+ json.dump(report, f, indent=2, default=str)
324
+ console.print(f"\n[green]Report saved to: {filename}[/green]")
325
+ except Exception as e:
326
+ console.print(f"\n[red]Failed to save report: {e!s}[/red]")
327
+
305
328
  def handle(
306
329
  self,
307
330
  config: Config,
@@ -313,6 +336,7 @@ class BrowserCommand(BaseCommand):
313
336
  port = kwargs.get("port")
314
337
  no_browser = kwargs.get("no_browser", False)
315
338
  timeout = kwargs.get("timeout", 60)
339
+ save_report = kwargs.get("save_report")
316
340
 
317
341
  self.start_time = time.time()
318
342
 
@@ -335,8 +359,7 @@ class BrowserCommand(BaseCommand):
335
359
  # Prompt user to open browser
336
360
  if not no_browser:
337
361
  console.print(
338
- "\n[yellow]Press Enter to open the debugger in your "
339
- "browser...[/yellow]"
362
+ "\n[yellow]Press Enter to open the debugger in your browser...[/yellow]"
340
363
  )
341
364
  input()
342
365
  webbrowser.open(url)
@@ -355,18 +378,14 @@ class BrowserCommand(BaseCommand):
355
378
  console=console,
356
379
  transient=True,
357
380
  ) as progress:
358
- task = progress.add_task(
359
- "Waiting for browser connection...", total=None
360
- )
381
+ task = progress.add_task("Waiting for browser connection...", total=None)
361
382
 
362
383
  # Run servers
363
384
  loop = asyncio.new_event_loop()
364
385
  asyncio.set_event_loop(loop)
365
386
 
366
387
  try:
367
- result = loop.run_until_complete(
368
- self.run_servers(port, timeout)
369
- )
388
+ result = loop.run_until_complete(self.run_servers(port, timeout))
370
389
  finally:
371
390
  loop.close()
372
391
 
@@ -374,10 +393,7 @@ class BrowserCommand(BaseCommand):
374
393
 
375
394
  # Process results
376
395
  capabilities = None
377
- if (
378
- self.capabilities_data
379
- and "overall_compatible" in self.capabilities_data
380
- ):
396
+ if self.capabilities_data and "overall_compatible" in self.capabilities_data:
381
397
  # Build BrowserCapabilities object
382
398
  cap_dict = {}
383
399
  for key in [
@@ -398,9 +414,7 @@ class BrowserCommand(BaseCommand):
398
414
  if all(key in cap_dict for key in cap_dict):
399
415
  capabilities = BrowserCapabilities(
400
416
  **cap_dict,
401
- user_agent=self.capabilities_data.get(
402
- "user_agent", "Unknown"
403
- ),
417
+ user_agent=self.capabilities_data.get("user_agent", "Unknown"),
404
418
  overall_compatible=self.capabilities_data.get(
405
419
  "overall_compatible", False
406
420
  ),
@@ -408,14 +422,11 @@ class BrowserCommand(BaseCommand):
408
422
 
409
423
  # Display results
410
424
  if result["completed"]:
411
- console.print(
412
- "\n[green]✓ Debug session completed successfully![/green]"
413
- )
425
+ console.print("\n[green]✓ Debug session completed successfully![/green]")
414
426
  self.display_results(capabilities, self.messages)
415
427
  elif result["timed_out"]:
416
428
  console.print(
417
- f"\n[red]✗ Debug session timed out after {timeout} "
418
- f"seconds[/red]"
429
+ f"\n[red]✗ Debug session timed out after {timeout} seconds[/red]"
419
430
  )
420
431
  console.print("[dim]No browser connection was established.[/dim]")
421
432
  else:
@@ -433,7 +444,7 @@ class BrowserCommand(BaseCommand):
433
444
  if msg.type == MessageType.WARNING and msg.message
434
445
  ]
435
446
 
436
- return BrowserDebugResult(
447
+ debug_result = BrowserDebugResult(
437
448
  status="success" if result["completed"] else "timeout",
438
449
  port=port,
439
450
  capabilities=capabilities,
@@ -443,3 +454,9 @@ class BrowserCommand(BaseCommand):
443
454
  duration_seconds=result["duration"],
444
455
  browser_opened=browser_opened,
445
456
  )
457
+
458
+ # Save report if requested
459
+ if save_report:
460
+ self._save_report(debug_result, save_report)
461
+
462
+ return debug_result
@@ -41,6 +41,8 @@ class BrowserCapabilities(BaseModel):
41
41
  console_api: BrowserCapability
42
42
  timer_apis: BrowserCapability
43
43
  secure_context: BrowserCapability
44
+ microphone_access: BrowserCapability | None = None
45
+ websocket_deepgram: BrowserCapability | None = None
44
46
  user_agent: str
45
47
  overall_compatible: bool
46
48
 
@@ -390,6 +390,54 @@
390
390
  name: 'Secure Context (HTTPS/localhost)',
391
391
  key: 'secure_context',
392
392
  check: () => window.isSecureContext === true
393
+ },
394
+ {
395
+ name: 'Microphone Access',
396
+ key: 'microphone_access',
397
+ check: async () => {
398
+ try {
399
+ if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
400
+ return false;
401
+ }
402
+ const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
403
+ stream.getTracks().forEach(t => t.stop());
404
+ return true;
405
+ } catch (e) {
406
+ return false;
407
+ }
408
+ }
409
+ },
410
+ {
411
+ name: 'WebSocket to Deepgram',
412
+ key: 'websocket_deepgram',
413
+ check: async () => {
414
+ return new Promise((resolve) => {
415
+ try {
416
+ const testWs = new WebSocket('wss://api.deepgram.com/v1/listen');
417
+ const timer = setTimeout(() => {
418
+ testWs.close();
419
+ resolve(false);
420
+ }, 5000);
421
+ testWs.onopen = () => {
422
+ clearTimeout(timer);
423
+ testWs.close();
424
+ resolve(true);
425
+ };
426
+ testWs.onerror = () => {
427
+ clearTimeout(timer);
428
+ resolve(true); // 401 still means reachable
429
+ };
430
+ testWs.onclose = (e) => {
431
+ clearTimeout(timer);
432
+ // Code 1006 with no open = network blocked
433
+ // Code 1008/4xx = auth error = reachable
434
+ resolve(e.code !== 1006 || testWs.readyState !== WebSocket.CONNECTING);
435
+ };
436
+ } catch (e) {
437
+ resolve(false);
438
+ }
439
+ });
440
+ }
393
441
  }
394
442
  ];
395
443
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: deepctl-cmd-debug-browser
3
- Version: 0.1.10
3
+ Version: 0.1.11
4
4
  Summary: Browser debug subcommand for deepctl
5
5
  Author-email: Deepgram <devrel@deepgram.com>
6
6
  Maintainer-email: Deepgram <devrel@deepgram.com>
@@ -26,4 +26,47 @@ Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
26
26
 
27
27
  # deepctl-cmd-debug-browser
28
28
 
29
- Browser debug subcommand for the Deepgram CLI (deepctl). This package provides the `browser` subcommand under the `debug` command group, allowing users to debug browser-related issues when using Deepgram's web-based features
29
+ > Part of [deepctl](https://github.com/deepgram/cli) — Official Deepgram CLI
30
+
31
+ Browser debug subcommand for deepctl
32
+
33
+ This is a subcommand of `deepctl debug`.
34
+
35
+ ## Installation
36
+
37
+ This package is included with deepctl and does not need to be installed separately.
38
+
39
+ ### Install deepctl
40
+
41
+ ```bash
42
+ # Install with pip
43
+ pip install deepctl
44
+
45
+ # Or install with uv
46
+ uv tool install deepctl
47
+
48
+ # Or install with pipx
49
+ pipx install deepctl
50
+
51
+ # Or run without installing
52
+ uvx deepctl --help
53
+ pipx run deepctl --help
54
+ ```
55
+
56
+ ## Commands
57
+
58
+ | Command | Entry Point |
59
+ |---------|-------------|
60
+ | `deepctl debug browser` | `deepctl_cmd_debug_browser.command:BrowserCommand` |
61
+
62
+ ## Dependencies
63
+
64
+ - `click>=8.0.0`
65
+ - `rich>=13.0.0`
66
+ - `pydantic>=2.0.0`
67
+ - `httpx>=0.24.0`
68
+ - `aiohttp>=3.9.0`
69
+
70
+ ## License
71
+
72
+ MIT — see [LICENSE](../../LICENSE)
@@ -1,3 +0,0 @@
1
- # deepctl-cmd-debug-browser
2
-
3
- Browser debug subcommand for the Deepgram CLI (deepctl). This package provides the `browser` subcommand under the `debug` command group, allowing users to debug browser-related issues when using Deepgram's web-based features