deepctl 0.3.3__tar.gz → 0.4.1__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: deepctl
3
- Version: 0.3.3
3
+ Version: 0.4.1
4
4
  Summary: Official Deepgram CLI for speech recognition and audio intelligence
5
5
  Author-email: Deepgram <devrel@deepgram.com>
6
6
  Maintainer-email: Deepgram <devrel@deepgram.com>
@@ -27,35 +27,35 @@ Description-Content-Type: text/markdown
27
27
  License-File: LICENSE
28
28
  Requires-Dist: click>=8.0.0
29
29
  Requires-Dist: deepgram-sdk<8,>=7.7.0
30
- Requires-Dist: deepctl-core>=0.2.19
31
- Requires-Dist: deepctl-cmd-login>=0.1.20
32
- Requires-Dist: deepctl-cmd-projects>=0.2.0
30
+ Requires-Dist: deepctl-core>=0.3.1
31
+ Requires-Dist: deepctl-cmd-login>=0.2.1
32
+ Requires-Dist: deepctl-cmd-projects>=0.2.1
33
33
  Requires-Dist: deepctl-cmd-transcribe>=0.1.12
34
34
  Requires-Dist: deepctl-cmd-usage>=0.2.1
35
- Requires-Dist: deepctl-cmd-mcp>=0.1.15
36
- Requires-Dist: deepctl-cmd-api>=0.0.2
35
+ Requires-Dist: deepctl-cmd-mcp>=0.1.16
36
+ Requires-Dist: deepctl-cmd-api>=0.0.3
37
37
  Requires-Dist: deepctl-cmd-debug>=0.1.13
38
38
  Requires-Dist: deepctl-cmd-debug-browser>=0.1.12
39
39
  Requires-Dist: deepctl-cmd-debug-network>=0.1.12
40
- Requires-Dist: deepctl-cmd-debug-audio>=0.1.14
41
- Requires-Dist: deepctl-cmd-debug-probe>=0.0.2
42
- Requires-Dist: deepctl-cmd-debug-toolkit>=0.1.0
43
- Requires-Dist: deepctl-cmd-ffprobe>=0.0.3
44
- Requires-Dist: deepctl-cmd-update>=0.2.7
45
- Requires-Dist: deepctl-cmd-plugin>=0.1.14
46
- Requires-Dist: deepctl-cmd-skills>=0.0.9
47
- Requires-Dist: deepctl-cmd-init>=0.0.4
40
+ Requires-Dist: deepctl-cmd-debug-audio>=0.1.15
41
+ Requires-Dist: deepctl-cmd-debug-probe>=0.0.3
42
+ Requires-Dist: deepctl-cmd-debug-toolkit>=0.2.0
43
+ Requires-Dist: deepctl-cmd-ffprobe>=0.0.4
44
+ Requires-Dist: deepctl-cmd-update>=0.2.8
45
+ Requires-Dist: deepctl-cmd-plugin>=0.1.15
46
+ Requires-Dist: deepctl-cmd-skills>=0.0.10
47
+ Requires-Dist: deepctl-cmd-init>=0.1.1
48
48
  Requires-Dist: deepctl-cmd-models>=0.1.1
49
- Requires-Dist: deepctl-cmd-speak>=0.0.5
49
+ Requires-Dist: deepctl-cmd-speak>=0.0.6
50
50
  Requires-Dist: deepctl-cmd-keys>=0.1.0
51
51
  Requires-Dist: deepctl-cmd-read>=0.1.0
52
- Requires-Dist: deepctl-cmd-listen>=0.0.14
52
+ Requires-Dist: deepctl-cmd-listen>=0.0.15
53
53
  Requires-Dist: deepctl-cmd-requests>=0.1.0
54
54
  Requires-Dist: deepctl-cmd-billing>=0.1.0
55
55
  Requires-Dist: deepctl-cmd-members>=0.1.0
56
56
  Requires-Dist: deepctl-cmd-completion>=0.0.3
57
- Requires-Dist: deepctl-shared-utils>=0.1.13
58
- Requires-Dist: deepctl-telemetry>=0.0.7
57
+ Requires-Dist: deepctl-shared-utils>=0.1.14
58
+ Requires-Dist: deepctl-telemetry>=0.0.8
59
59
  Requires-Dist: pydantic>=2.0.0
60
60
  Requires-Dist: rich>=13.0.0
61
61
  Requires-Dist: httpx>=0.24.0
@@ -141,13 +141,22 @@ iwr https://deepgram.com/install.ps1 -useb | iex
141
141
  pip install deepctl # pip
142
142
  uv tool install deepctl # uv
143
143
  pipx install deepctl # pipx
144
+ npm install -g @deepgram/cli # npm (installs dg and deepgram)
144
145
  ```
145
146
 
147
+ The npm package is a launcher for the same Python CLI. It needs
148
+ Node.js 20+ and either [uv](https://docs.astral.sh/uv/) or Python 3.10-3.14
149
+ with `venv`, and sets the CLI up on first run. It installs prebuilt wheels
150
+ only, so it (and `npx`) fails on Intel Macs and Windows on ARM64, where
151
+ `cryptography` has none; on an Intel Mac, use Homebrew. See
152
+ [npm/README.md](npm/README.md).
153
+
146
154
  ### Try Without Installing
147
155
 
148
156
  ```bash
149
157
  uvx deepctl --help
150
158
  pipx run deepctl --help
159
+ npx @deepgram/cli --help
151
160
  ```
152
161
 
153
162
  ## Getting Started
@@ -242,7 +251,7 @@ dg requests --status failed --endpoint listen # Debug failed requests
242
251
  dg api /v1/projects --jq '.projects[0].name'
243
252
 
244
253
  # MCP server for AI editors (Claude Code, Cursor, etc.)
245
- dg mcp --transport sse --port 8000
254
+ dg mcp
246
255
  ```
247
256
 
248
257
  ## Features
@@ -352,11 +361,11 @@ dg debug probe --port 3100 # Live stream audio analysis
352
361
 
353
362
  ### MCP Server
354
363
 
355
- Connect Deepgram tools to AI coding assistants (Claude Code, Cursor, etc.).
364
+ Give AI coding assistants (Claude Code, Cursor, etc.) Deepgram documentation
365
+ search over stdio.
356
366
 
357
367
  ```bash
358
368
  dg mcp # Start MCP server (stdio)
359
- dg mcp --transport sse --port 8000 # SSE transport
360
369
  ```
361
370
 
362
371
  Add to your editor's MCP config:
@@ -507,57 +516,71 @@ dg plugin remove <package> # Remove
507
516
 
508
517
  Create your own — see the [plugin example](packages/deepctl-plugin-example).
509
518
 
510
- ## Configuration
511
-
512
- **Priority:** CLI flags > environment variables > profile config > project config
519
+ A plugin can't replace a built-in command such as `login` or `debug audio`.
520
+ dg runs the built-in and prints a warning that names the plugin's package.
521
+ A plugin whose entry point is named like a built-in command is skipped before
522
+ it's imported, so none of its code runs; give the entry point a different name.
523
+ To use a plugin's version anyway, list the command in the user config file
524
+ at its standard location (`~/.config/deepctl/config.yaml` on Linux, or
525
+ `$XDG_CONFIG_HOME/deepctl/config.yaml` when `XDG_CONFIG_HOME` is set;
526
+ `~/Library/Application Support/deepctl/config.yaml` on macOS;
527
+ `%LOCALAPPDATA%\deepgram\deepctl\config.yaml` on Windows):
513
528
 
514
- ```bash
515
- dg login # Interactive or --api-key
516
- dg login --profile staging --api-key SK # Named profiles
517
- dg profiles --list # List profiles
518
- dg profiles --switch staging # Switch profile
529
+ ```yaml
530
+ plugins:
531
+ allow_override:
532
+ - login
533
+ - debug audio
519
534
  ```
520
535
 
521
- For structured output, use `--output json|yaml|table|csv` after leaf commands
522
- that do not define their own output option, or before any command as a global
523
- flag: `dg --output json <command>`. `dg speak --output FILE` writes audio to
524
- `FILE`, so use `dg --output json speak ...` for Speak's structured output.
525
-
526
- ## Telemetry
527
-
528
- The CLI phones home anonymous error reports to help us catch crashes and regressions before users have to file an issue. It's **on by default** and easy to turn off.
536
+ dg reads this setting only from that file, never from `deepgram.yaml` or a
537
+ `--config` file. Only allow a plugin you trust: it receives everything you
538
+ pass to that command, including API keys.
529
539
 
530
- **What's collected:** Python exceptions, stack traces, the CLI version, and the Python runtime. Request bodies, headers, cookies, API keys, email addresses, IP addresses, and usernames are scrubbed before send. No performance traces, no profiling, no replays — errors only.
540
+ ## Configuration
531
541
 
532
- **Where it goes:** the `dx-cli` Sentry project owned by the Deepgram DX team.
542
+ **Priority:** CLI flags > environment variables > project `deepgram.yaml` > user
543
+ config. A project `deepgram.yaml` is read after the user config, but it can set
544
+ only `output.*` settings and a regional Deepgram `base_url` (see below).
533
545
 
534
- ### Opt out
546
+ The API key is the exception: dg uses `--api-key`, then the key `dg login` saved
547
+ in your system keyring, then `DEEPGRAM_API_KEY`, then an `api_key` in the config
548
+ file (where `dg login` saves it when no keyring is available).
535
549
 
536
- Persistent (recommended) — add this to your `config.yaml`
537
- (`~/.config/deepctl/config.yaml` on Linux,
538
- `~/Library/Application Support/deepctl/config.yaml` on macOS,
539
- `%LOCALAPPDATA%\deepgram\deepctl\config.yaml` on Windows):
550
+ dg sends your API key only to Deepgram API hosts, to hosts you list in
551
+ `api.allowed_hosts` in your own `config.yaml`, or to `DEEPGRAM_BASE_URL` (not in
552
+ an AI agent session). Anything else is refused before a request is made. Hosts
553
+ must use https: plain http works only for localhost, 127.0.0.0/8 and [::1]. For
554
+ a proxy or self-hosted deployment, add its host (and port, to approve only that
555
+ port):
540
556
 
541
557
  ```yaml
542
- telemetry:
543
- enabled: false
558
+ api:
559
+ allowed_hosts:
560
+ - my-proxy.example.com
561
+ - localhost:8080
544
562
  ```
545
563
 
546
- One-shot (CI, scripts, single command):
564
+ A project's `deepgram.yaml` can't set credentials, a project ID, a profile or a
565
+ non-Deepgram `base_url`, and can't approve hosts.
547
566
 
548
567
  ```bash
549
- DEEPCTL_TELEMETRY_DISABLED=1 dg listen recording.wav
568
+ dg login # Interactive or --api-key
569
+ dg login --profile staging --api-key SK # Named profiles
570
+ dg profiles --list # List profiles
571
+ dg profiles --switch staging # Switch profile
550
572
  ```
551
573
 
552
- ### Override the destination
574
+ For structured output, use `--output json|yaml|table|csv` after leaf commands
575
+ that do not define their own output option, or before any command as a global
576
+ flag: `dg --output json <command>`. `dg speak --output FILE` writes audio to
577
+ `FILE`, so use `dg --output json speak ...` for Speak's structured output.
553
578
 
554
- For forks or self-hosted Sentry, point telemetry at your own DSN:
579
+ ## Telemetry
555
580
 
556
- ```bash
557
- export DEEPCTL_TELEMETRY_DSN='https://<key>@<your-sentry>/<project>'
558
- ```
581
+ This version of dg sends no telemetry: no error reports, usage data or other telemetry, whether a command succeeds or fails. dg still contacts the Deepgram API for the commands you run, and pypi.org to check for updates.
559
582
 
560
- `DEEPCTL_TELEMETRY_DISABLED` always wins. The default DSN is baked into the package and only used when the override is unset.
583
+ The `DEEPCTL_TELEMETRY_DISABLED` environment variable and the `telemetry.enabled` setting in `config.yaml` are still accepted, and the new `--no-telemetry` flag is accepted too, but none of them has an effect in this version. `DEEPCTL_TELEMETRY_DSN` is now ignored unless it points at the local machine, which is for tests. The last line of `dg --help` says that dg sends no telemetry.
561
584
 
562
585
  ## Development
563
586
 
@@ -589,7 +612,7 @@ cli/
589
612
  │ ├── deepctl-cmd-keys/ # API keys management command for deepctl
590
613
  │ ├── deepctl-cmd-listen/ # Listen (live speech-to-text) command for deepctl
591
614
  │ ├── deepctl-cmd-login/ # Login command for deepctl
592
- │ ├── deepctl-cmd-mcp/ # MCP proxy command for deepctl — connects to Deepgram's developer API
615
+ │ ├── deepctl-cmd-mcp/ # MCP server for deepctl: Deepgram documentation search over stdio
593
616
  │ ├── deepctl-cmd-members/ # Members management command for deepctl
594
617
  │ ├── deepctl-cmd-models/ # Models command for deepctl
595
618
  │ ├── deepctl-cmd-plugin/ # Plugin management command for deepctl
@@ -604,7 +627,7 @@ cli/
604
627
  │ ├── deepctl-core/ # Core components for deepctl
605
628
  │ ├── deepctl-plugin-example/ # Example plugin for deepctl
606
629
  │ ├── deepctl-shared-utils/ # Shared utilities for deepctl
607
- │ └── deepctl-telemetry/ # Opt-out phone-home telemetry for deepctl
630
+ │ └── deepctl-telemetry/ # Error reporting for deepctl; this version sends no telemetry
608
631
  ├── tests/ # Integration tests
609
632
  └── Makefile # Development tasks
610
633
  ```
@@ -630,7 +653,7 @@ cli/
630
653
  | `deepctl listen` | Listen (live speech-to-text) command for deepctl |
631
654
  | `deepctl login` | Login command for deepctl |
632
655
  | `deepctl logout` | Login command for deepctl |
633
- | `deepctl mcp` | MCP proxy command for deepctl — connects to Deepgram's developer API |
656
+ | `deepctl mcp` | MCP server for deepctl: Deepgram documentation search over stdio |
634
657
  | `deepctl members` | Members management command for deepctl |
635
658
  | `deepctl models` | Models command for deepctl |
636
659
  | `deepctl plugin` | Plugin management command for deepctl |
@@ -665,7 +688,7 @@ cli/
665
688
  | [`deepctl-cmd-keys`](packages/deepctl-cmd-keys) | API keys management command for deepctl |
666
689
  | [`deepctl-cmd-listen`](packages/deepctl-cmd-listen) | Listen (live speech-to-text) command for deepctl |
667
690
  | [`deepctl-cmd-login`](packages/deepctl-cmd-login) | Login command for deepctl |
668
- | [`deepctl-cmd-mcp`](packages/deepctl-cmd-mcp) | MCP proxy command for deepctl — connects to Deepgram's developer API |
691
+ | [`deepctl-cmd-mcp`](packages/deepctl-cmd-mcp) | MCP server for deepctl: Deepgram documentation search over stdio |
669
692
  | [`deepctl-cmd-members`](packages/deepctl-cmd-members) | Members management command for deepctl |
670
693
  | [`deepctl-cmd-models`](packages/deepctl-cmd-models) | Models command for deepctl |
671
694
  | [`deepctl-cmd-plugin`](packages/deepctl-cmd-plugin) | Plugin management command for deepctl |
@@ -680,7 +703,7 @@ cli/
680
703
  | [`deepctl-core`](packages/deepctl-core) | Core components for deepctl |
681
704
  | [`deepctl-plugin-example`](packages/deepctl-plugin-example) | Example plugin for deepctl |
682
705
  | [`deepctl-shared-utils`](packages/deepctl-shared-utils) | Shared utilities for deepctl |
683
- | [`deepctl-telemetry`](packages/deepctl-telemetry) | Opt-out phone-home telemetry for deepctl |
706
+ | [`deepctl-telemetry`](packages/deepctl-telemetry) | Error reporting for deepctl; this version sends no telemetry |
684
707
  <!-- END:packages -->
685
708
 
686
709
  ## Release
@@ -57,13 +57,22 @@ iwr https://deepgram.com/install.ps1 -useb | iex
57
57
  pip install deepctl # pip
58
58
  uv tool install deepctl # uv
59
59
  pipx install deepctl # pipx
60
+ npm install -g @deepgram/cli # npm (installs dg and deepgram)
60
61
  ```
61
62
 
63
+ The npm package is a launcher for the same Python CLI. It needs
64
+ Node.js 20+ and either [uv](https://docs.astral.sh/uv/) or Python 3.10-3.14
65
+ with `venv`, and sets the CLI up on first run. It installs prebuilt wheels
66
+ only, so it (and `npx`) fails on Intel Macs and Windows on ARM64, where
67
+ `cryptography` has none; on an Intel Mac, use Homebrew. See
68
+ [npm/README.md](npm/README.md).
69
+
62
70
  ### Try Without Installing
63
71
 
64
72
  ```bash
65
73
  uvx deepctl --help
66
74
  pipx run deepctl --help
75
+ npx @deepgram/cli --help
67
76
  ```
68
77
 
69
78
  ## Getting Started
@@ -158,7 +167,7 @@ dg requests --status failed --endpoint listen # Debug failed requests
158
167
  dg api /v1/projects --jq '.projects[0].name'
159
168
 
160
169
  # MCP server for AI editors (Claude Code, Cursor, etc.)
161
- dg mcp --transport sse --port 8000
170
+ dg mcp
162
171
  ```
163
172
 
164
173
  ## Features
@@ -268,11 +277,11 @@ dg debug probe --port 3100 # Live stream audio analysis
268
277
 
269
278
  ### MCP Server
270
279
 
271
- Connect Deepgram tools to AI coding assistants (Claude Code, Cursor, etc.).
280
+ Give AI coding assistants (Claude Code, Cursor, etc.) Deepgram documentation
281
+ search over stdio.
272
282
 
273
283
  ```bash
274
284
  dg mcp # Start MCP server (stdio)
275
- dg mcp --transport sse --port 8000 # SSE transport
276
285
  ```
277
286
 
278
287
  Add to your editor's MCP config:
@@ -423,57 +432,71 @@ dg plugin remove <package> # Remove
423
432
 
424
433
  Create your own — see the [plugin example](packages/deepctl-plugin-example).
425
434
 
426
- ## Configuration
427
-
428
- **Priority:** CLI flags > environment variables > profile config > project config
435
+ A plugin can't replace a built-in command such as `login` or `debug audio`.
436
+ dg runs the built-in and prints a warning that names the plugin's package.
437
+ A plugin whose entry point is named like a built-in command is skipped before
438
+ it's imported, so none of its code runs; give the entry point a different name.
439
+ To use a plugin's version anyway, list the command in the user config file
440
+ at its standard location (`~/.config/deepctl/config.yaml` on Linux, or
441
+ `$XDG_CONFIG_HOME/deepctl/config.yaml` when `XDG_CONFIG_HOME` is set;
442
+ `~/Library/Application Support/deepctl/config.yaml` on macOS;
443
+ `%LOCALAPPDATA%\deepgram\deepctl\config.yaml` on Windows):
429
444
 
430
- ```bash
431
- dg login # Interactive or --api-key
432
- dg login --profile staging --api-key SK # Named profiles
433
- dg profiles --list # List profiles
434
- dg profiles --switch staging # Switch profile
445
+ ```yaml
446
+ plugins:
447
+ allow_override:
448
+ - login
449
+ - debug audio
435
450
  ```
436
451
 
437
- For structured output, use `--output json|yaml|table|csv` after leaf commands
438
- that do not define their own output option, or before any command as a global
439
- flag: `dg --output json <command>`. `dg speak --output FILE` writes audio to
440
- `FILE`, so use `dg --output json speak ...` for Speak's structured output.
441
-
442
- ## Telemetry
443
-
444
- The CLI phones home anonymous error reports to help us catch crashes and regressions before users have to file an issue. It's **on by default** and easy to turn off.
452
+ dg reads this setting only from that file, never from `deepgram.yaml` or a
453
+ `--config` file. Only allow a plugin you trust: it receives everything you
454
+ pass to that command, including API keys.
445
455
 
446
- **What's collected:** Python exceptions, stack traces, the CLI version, and the Python runtime. Request bodies, headers, cookies, API keys, email addresses, IP addresses, and usernames are scrubbed before send. No performance traces, no profiling, no replays — errors only.
456
+ ## Configuration
447
457
 
448
- **Where it goes:** the `dx-cli` Sentry project owned by the Deepgram DX team.
458
+ **Priority:** CLI flags > environment variables > project `deepgram.yaml` > user
459
+ config. A project `deepgram.yaml` is read after the user config, but it can set
460
+ only `output.*` settings and a regional Deepgram `base_url` (see below).
449
461
 
450
- ### Opt out
462
+ The API key is the exception: dg uses `--api-key`, then the key `dg login` saved
463
+ in your system keyring, then `DEEPGRAM_API_KEY`, then an `api_key` in the config
464
+ file (where `dg login` saves it when no keyring is available).
451
465
 
452
- Persistent (recommended) — add this to your `config.yaml`
453
- (`~/.config/deepctl/config.yaml` on Linux,
454
- `~/Library/Application Support/deepctl/config.yaml` on macOS,
455
- `%LOCALAPPDATA%\deepgram\deepctl\config.yaml` on Windows):
466
+ dg sends your API key only to Deepgram API hosts, to hosts you list in
467
+ `api.allowed_hosts` in your own `config.yaml`, or to `DEEPGRAM_BASE_URL` (not in
468
+ an AI agent session). Anything else is refused before a request is made. Hosts
469
+ must use https: plain http works only for localhost, 127.0.0.0/8 and [::1]. For
470
+ a proxy or self-hosted deployment, add its host (and port, to approve only that
471
+ port):
456
472
 
457
473
  ```yaml
458
- telemetry:
459
- enabled: false
474
+ api:
475
+ allowed_hosts:
476
+ - my-proxy.example.com
477
+ - localhost:8080
460
478
  ```
461
479
 
462
- One-shot (CI, scripts, single command):
480
+ A project's `deepgram.yaml` can't set credentials, a project ID, a profile or a
481
+ non-Deepgram `base_url`, and can't approve hosts.
463
482
 
464
483
  ```bash
465
- DEEPCTL_TELEMETRY_DISABLED=1 dg listen recording.wav
484
+ dg login # Interactive or --api-key
485
+ dg login --profile staging --api-key SK # Named profiles
486
+ dg profiles --list # List profiles
487
+ dg profiles --switch staging # Switch profile
466
488
  ```
467
489
 
468
- ### Override the destination
490
+ For structured output, use `--output json|yaml|table|csv` after leaf commands
491
+ that do not define their own output option, or before any command as a global
492
+ flag: `dg --output json <command>`. `dg speak --output FILE` writes audio to
493
+ `FILE`, so use `dg --output json speak ...` for Speak's structured output.
469
494
 
470
- For forks or self-hosted Sentry, point telemetry at your own DSN:
495
+ ## Telemetry
471
496
 
472
- ```bash
473
- export DEEPCTL_TELEMETRY_DSN='https://<key>@<your-sentry>/<project>'
474
- ```
497
+ This version of dg sends no telemetry: no error reports, usage data or other telemetry, whether a command succeeds or fails. dg still contacts the Deepgram API for the commands you run, and pypi.org to check for updates.
475
498
 
476
- `DEEPCTL_TELEMETRY_DISABLED` always wins. The default DSN is baked into the package and only used when the override is unset.
499
+ The `DEEPCTL_TELEMETRY_DISABLED` environment variable and the `telemetry.enabled` setting in `config.yaml` are still accepted, and the new `--no-telemetry` flag is accepted too, but none of them has an effect in this version. `DEEPCTL_TELEMETRY_DSN` is now ignored unless it points at the local machine, which is for tests. The last line of `dg --help` says that dg sends no telemetry.
477
500
 
478
501
  ## Development
479
502
 
@@ -505,7 +528,7 @@ cli/
505
528
  │ ├── deepctl-cmd-keys/ # API keys management command for deepctl
506
529
  │ ├── deepctl-cmd-listen/ # Listen (live speech-to-text) command for deepctl
507
530
  │ ├── deepctl-cmd-login/ # Login command for deepctl
508
- │ ├── deepctl-cmd-mcp/ # MCP proxy command for deepctl — connects to Deepgram's developer API
531
+ │ ├── deepctl-cmd-mcp/ # MCP server for deepctl: Deepgram documentation search over stdio
509
532
  │ ├── deepctl-cmd-members/ # Members management command for deepctl
510
533
  │ ├── deepctl-cmd-models/ # Models command for deepctl
511
534
  │ ├── deepctl-cmd-plugin/ # Plugin management command for deepctl
@@ -520,7 +543,7 @@ cli/
520
543
  │ ├── deepctl-core/ # Core components for deepctl
521
544
  │ ├── deepctl-plugin-example/ # Example plugin for deepctl
522
545
  │ ├── deepctl-shared-utils/ # Shared utilities for deepctl
523
- │ └── deepctl-telemetry/ # Opt-out phone-home telemetry for deepctl
546
+ │ └── deepctl-telemetry/ # Error reporting for deepctl; this version sends no telemetry
524
547
  ├── tests/ # Integration tests
525
548
  └── Makefile # Development tasks
526
549
  ```
@@ -546,7 +569,7 @@ cli/
546
569
  | `deepctl listen` | Listen (live speech-to-text) command for deepctl |
547
570
  | `deepctl login` | Login command for deepctl |
548
571
  | `deepctl logout` | Login command for deepctl |
549
- | `deepctl mcp` | MCP proxy command for deepctl — connects to Deepgram's developer API |
572
+ | `deepctl mcp` | MCP server for deepctl: Deepgram documentation search over stdio |
550
573
  | `deepctl members` | Members management command for deepctl |
551
574
  | `deepctl models` | Models command for deepctl |
552
575
  | `deepctl plugin` | Plugin management command for deepctl |
@@ -581,7 +604,7 @@ cli/
581
604
  | [`deepctl-cmd-keys`](packages/deepctl-cmd-keys) | API keys management command for deepctl |
582
605
  | [`deepctl-cmd-listen`](packages/deepctl-cmd-listen) | Listen (live speech-to-text) command for deepctl |
583
606
  | [`deepctl-cmd-login`](packages/deepctl-cmd-login) | Login command for deepctl |
584
- | [`deepctl-cmd-mcp`](packages/deepctl-cmd-mcp) | MCP proxy command for deepctl — connects to Deepgram's developer API |
607
+ | [`deepctl-cmd-mcp`](packages/deepctl-cmd-mcp) | MCP server for deepctl: Deepgram documentation search over stdio |
585
608
  | [`deepctl-cmd-members`](packages/deepctl-cmd-members) | Members management command for deepctl |
586
609
  | [`deepctl-cmd-models`](packages/deepctl-cmd-models) | Models command for deepctl |
587
610
  | [`deepctl-cmd-plugin`](packages/deepctl-cmd-plugin) | Plugin management command for deepctl |
@@ -596,7 +619,7 @@ cli/
596
619
  | [`deepctl-core`](packages/deepctl-core) | Core components for deepctl |
597
620
  | [`deepctl-plugin-example`](packages/deepctl-plugin-example) | Example plugin for deepctl |
598
621
  | [`deepctl-shared-utils`](packages/deepctl-shared-utils) | Shared utilities for deepctl |
599
- | [`deepctl-telemetry`](packages/deepctl-telemetry) | Opt-out phone-home telemetry for deepctl |
622
+ | [`deepctl-telemetry`](packages/deepctl-telemetry) | Error reporting for deepctl; this version sends no telemetry |
600
623
  <!-- END:packages -->
601
624
 
602
625
  ## Release
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "deepctl"
7
- version = "0.3.3" # x-release-please-version
7
+ version = "0.4.1" # x-release-please-version
8
8
  description = "Official Deepgram CLI for speech recognition and audio intelligence"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -35,35 +35,35 @@ requires-python = ">=3.10"
35
35
  dependencies = [
36
36
  "click>=8.0.0",
37
37
  "deepgram-sdk>=7.7.0,<8",
38
- "deepctl-core>=0.2.19",
39
- "deepctl-cmd-login>=0.1.20",
40
- "deepctl-cmd-projects>=0.2.0",
38
+ "deepctl-core>=0.3.1",
39
+ "deepctl-cmd-login>=0.2.1",
40
+ "deepctl-cmd-projects>=0.2.1",
41
41
  "deepctl-cmd-transcribe>=0.1.12",
42
42
  "deepctl-cmd-usage>=0.2.1",
43
- "deepctl-cmd-mcp>=0.1.15",
44
- "deepctl-cmd-api>=0.0.2",
43
+ "deepctl-cmd-mcp>=0.1.16",
44
+ "deepctl-cmd-api>=0.0.3",
45
45
  "deepctl-cmd-debug>=0.1.13",
46
46
  "deepctl-cmd-debug-browser>=0.1.12",
47
47
  "deepctl-cmd-debug-network>=0.1.12",
48
- "deepctl-cmd-debug-audio>=0.1.14",
49
- "deepctl-cmd-debug-probe>=0.0.2",
50
- "deepctl-cmd-debug-toolkit>=0.1.0",
51
- "deepctl-cmd-ffprobe>=0.0.3",
52
- "deepctl-cmd-update>=0.2.7",
53
- "deepctl-cmd-plugin>=0.1.14",
54
- "deepctl-cmd-skills>=0.0.9",
55
- "deepctl-cmd-init>=0.0.4",
48
+ "deepctl-cmd-debug-audio>=0.1.15",
49
+ "deepctl-cmd-debug-probe>=0.0.3",
50
+ "deepctl-cmd-debug-toolkit>=0.2.0",
51
+ "deepctl-cmd-ffprobe>=0.0.4",
52
+ "deepctl-cmd-update>=0.2.8",
53
+ "deepctl-cmd-plugin>=0.1.15",
54
+ "deepctl-cmd-skills>=0.0.10",
55
+ "deepctl-cmd-init>=0.1.1",
56
56
  "deepctl-cmd-models>=0.1.1",
57
- "deepctl-cmd-speak>=0.0.5",
57
+ "deepctl-cmd-speak>=0.0.6",
58
58
  "deepctl-cmd-keys>=0.1.0",
59
59
  "deepctl-cmd-read>=0.1.0",
60
- "deepctl-cmd-listen>=0.0.14",
60
+ "deepctl-cmd-listen>=0.0.15",
61
61
  "deepctl-cmd-requests>=0.1.0",
62
62
  "deepctl-cmd-billing>=0.1.0",
63
63
  "deepctl-cmd-members>=0.1.0",
64
64
  "deepctl-cmd-completion>=0.0.3",
65
- "deepctl-shared-utils>=0.1.13",
66
- "deepctl-telemetry>=0.0.7",
65
+ "deepctl-shared-utils>=0.1.14",
66
+ "deepctl-telemetry>=0.0.8",
67
67
  "pydantic>=2.0.0",
68
68
  "rich>=13.0.0",
69
69
  "httpx>=0.24.0",
@@ -148,11 +148,9 @@ module = [
148
148
  "sounddevice.*",
149
149
  "numpy",
150
150
  "numpy.*",
151
- "deepgram_mcp",
152
- "deepgram_mcp.*",
153
151
  ]
154
- # Optional microphone dependencies (sounddevice, numpy) and the MCP SDK
155
- # (deepgram_mcp) ship without type stubs.
152
+ # Optional microphone dependencies (sounddevice, numpy) ship without type
153
+ # stubs.
156
154
  ignore_missing_imports = true
157
155
  # numpy>=2.2 ships PEP 695 `type` aliases in its stubs, which mypy rejects
158
156
  # under python_version = "3.10" (aborting before our code is checked). Skip
@@ -1,7 +1,7 @@
1
1
  """deepctl - Official command-line interface for Deepgram's speech
2
2
  recognition API."""
3
3
 
4
- __version__ = "0.3.3" # x-release-please-version
4
+ __version__ = "0.4.1" # x-release-please-version
5
5
  __author__ = "Deepgram"
6
6
  __email__ = "devrel@deepgram.com"
7
7
  __license__ = "MIT"
@@ -3,15 +3,9 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import importlib.metadata
6
- import os
7
6
  import sys
8
- from contextlib import contextmanager
9
- from typing import TYPE_CHECKING
10
7
 
11
8
  import click
12
-
13
- if TYPE_CHECKING:
14
- from collections.abc import Iterator
15
9
  from deepctl_core import (
16
10
  Config,
17
11
  TimingContext,
@@ -19,11 +13,16 @@ from deepctl_core import (
19
13
  print_timing_summary,
20
14
  setup_output,
21
15
  )
16
+ from deepctl_core._launcher import capture_launcher
22
17
  from deepctl_core.output import stderr_console
23
18
  from rich.traceback import install
24
19
 
25
- # Install rich traceback for better error messages
26
- install(show_locals=True)
20
+ # Read DEEPCTL_LAUNCHER (set by the @deepgram/cli npm package) once and drop it
21
+ # from the environment, so processes deepctl starts do not inherit it.
22
+ LAUNCHER = capture_launcher()
23
+
24
+ # Install rich traceback for better error messages, without frame locals.
25
+ install(show_locals=False)
27
26
 
28
27
  # Diagnostics go to stderr so stdout carries only the requested payload -- a
29
28
  # crash or usage error must not corrupt `dg -o json ...` for a script piping
@@ -42,12 +41,49 @@ def _record_install_method_cb(
42
41
  try:
43
42
  config = Config()
44
43
  config._set_config_value("update.installation_method", value)
45
- config.save()
44
+ config.save(implicit=True)
45
+ except OSError as exc:
46
+ # A failed write must not look like success to the install script.
47
+ console.print(f"[red]Error:[/red] could not save the config file: {exc}")
48
+ # sys.exit, not ctx.exit: main() runs Click with standalone_mode=False,
49
+ # where ctx.exit(1) would only become an ignored return value.
50
+ sys.exit(1)
46
51
  except Exception:
47
52
  pass
48
53
  ctx.exit()
49
54
 
50
55
 
56
+ def _root_flag_opts() -> set[str]:
57
+ """Root options that take no value: flags, plus `--help`."""
58
+ opts = {"--help"}
59
+ for param in cli.params:
60
+ if isinstance(param, click.Option) and param.is_flag:
61
+ opts.update(param.opts)
62
+ opts.update(param.secondary_opts)
63
+ return opts
64
+
65
+
66
+ def _takes_next_token(arg: str, flag_opts: set[str]) -> bool:
67
+ """Whether root option `arg` takes the following token as its value.
68
+
69
+ Mirrors click: flags take nothing, `--opt=value` and `-Xvalue` carry
70
+ their value, and a short cluster such as `-qv` or `-qpprod` is read
71
+ letter by letter until the first option that takes a value.
72
+ """
73
+ if arg in flag_opts or "=" in arg:
74
+ return False
75
+ if arg.startswith("--") or len(arg) <= 2:
76
+ return True
77
+ letters = arg[1:]
78
+ for index, letter in enumerate(letters):
79
+ if f"-{letter}" in flag_opts:
80
+ continue
81
+ # The first option that takes a value: anything after it is the
82
+ # attached value; nothing after it means the next token is.
83
+ return index == len(letters) - 1
84
+ return False
85
+
86
+
51
87
  def preprocess_hyphenated_commands(args: list[str]) -> list[str]:
52
88
  """Convert hyphenated commands to nested commands.
53
89
 
@@ -85,40 +121,121 @@ def preprocess_hyphenated_commands(args: list[str]) -> list[str]:
85
121
  except Exception:
86
122
  pass
87
123
 
88
- # Process arguments
89
- new_args = []
90
- i = 0
124
+ flag_opts = _root_flag_opts()
91
125
 
126
+ # Walk the root options to find the command token. Only that token is
127
+ # expanded; everything after it belongs to the command and is kept
128
+ # verbatim (`dg listen debug-call.wav` must not be split).
129
+ new_args: list[str] = []
130
+ i = 0
92
131
  while i < len(args):
93
132
  arg = args[i]
94
-
95
- # Skip if it's an option (starts with -)
133
+ if arg == "--":
134
+ break
96
135
  if arg.startswith("-"):
97
136
  new_args.append(arg)
98
137
  i += 1
99
- # If it's an option with a value, include the next arg too
100
- if i < len(args) and not args[i].startswith("-"):
138
+ # An option that takes a value consumes the next token too,
139
+ # unless the value is attached (`--opt=value`, `-Xvalue`).
140
+ if (
141
+ _takes_next_token(arg, flag_opts)
142
+ and i < len(args)
143
+ and not args[i].startswith("-")
144
+ ):
101
145
  new_args.append(args[i])
102
146
  i += 1
103
147
  continue
104
148
 
105
- # Check if this could be a hyphenated command
106
- if "-" in arg and not arg.startswith("-"):
107
- parts = arg.split("-", 1)
108
- if len(parts) == 2:
109
- group_name, subcommand = parts
149
+ group_name, sep, subcommand = arg.partition("-")
150
+ if sep and group_name in group_commands:
151
+ new_args.extend([group_name, subcommand])
152
+ i += 1
153
+ break
154
+
155
+ return new_args + args[i:]
110
156
 
111
- # If the first part is a known group command, convert it
112
- if group_name in group_commands:
113
- new_args.extend([group_name, subcommand])
114
- i += 1
115
- continue
116
157
 
117
- # Otherwise, keep the argument as-is
118
- new_args.append(arg)
158
+ def _root_option_tokens(args: list[str]) -> list[str]:
159
+ """The option tokens before the subcommand (values skipped)."""
160
+ flag_opts = _root_flag_opts()
161
+ tokens: list[str] = []
162
+ i = 0
163
+ while i < len(args) and args[i].startswith("-"):
164
+ arg = args[i]
165
+ tokens.append(arg)
119
166
  i += 1
167
+ if _takes_next_token(arg, flag_opts):
168
+ i += 1 # skip the option's value
169
+ return tokens
120
170
 
121
- return new_args
171
+
172
+ def _no_telemetry_cb(ctx: click.Context, param: click.Parameter, value: bool) -> None:
173
+ """Turn telemetry off for this run. Eager, so root `--help` reflects it."""
174
+ if not value or ctx.resilient_parsing:
175
+ return
176
+ try:
177
+ from deepctl_telemetry import disable_for_run
178
+
179
+ disable_for_run()
180
+ except Exception:
181
+ pass
182
+
183
+
184
+ def _init_telemetry(config: Config) -> None:
185
+ """Decide telemetry from the resolved Config. Never loads the SDK."""
186
+ try:
187
+ from deepctl_telemetry import init_telemetry
188
+
189
+ init_telemetry(config)
190
+ except Exception:
191
+ pass
192
+
193
+
194
+ def _report_error(exc: BaseException) -> None:
195
+ """Send an error report if telemetry is on (the SDK loads only here).
196
+
197
+ A Ctrl-C while the report is sent is still a user cancellation: exit 2.
198
+ """
199
+ try:
200
+ from deepctl_telemetry import report_exception
201
+
202
+ report_exception(exc)
203
+ except KeyboardInterrupt:
204
+ _safe_console_print("\n[yellow]Stopped sending the error report.[/yellow]")
205
+ sys.exit(2)
206
+ except Exception:
207
+ pass
208
+
209
+
210
+ def _argv_config_path(args: list[str]) -> str | None:
211
+ """`--config` from the root arguments, only for the notice on root `--help`.
212
+
213
+ Root `--help` is eager and fires before the root callback resolves
214
+ `--config`; every other path uses the parsed value. Accepts
215
+ `--config X`, `--config=X`, `-c X`, `-cX` and clusters such as `-qcX`.
216
+ When the option repeats, the last value wins, as in click.
217
+ """
218
+ flag_opts = _root_flag_opts()
219
+ found: str | None = None
220
+ i = 0
221
+ while i < len(args) and args[i].startswith("-"):
222
+ arg = args[i]
223
+ i += 1
224
+ if arg.startswith("--config="):
225
+ found = arg.split("=", 1)[1]
226
+ elif arg == "--config":
227
+ found = args[i] if i < len(args) else None
228
+ elif not arg.startswith("--"):
229
+ for index, letter in enumerate(arg[1:]):
230
+ if f"-{letter}" in flag_opts:
231
+ continue
232
+ if letter == "c":
233
+ rest = arg[index + 2 :]
234
+ found = rest or (args[i] if i < len(args) else None)
235
+ break
236
+ if _takes_next_token(arg, flag_opts):
237
+ i += 1
238
+ return found
122
239
 
123
240
 
124
241
  # Create CLI group
@@ -140,7 +257,9 @@ def preprocess_hyphenated_commands(args: list[str]) -> list[str]:
140
257
  @click.option(
141
258
  "--base-url",
142
259
  help=(
143
- "Override the API base URL (e.g. https://api.staging.deepgram.com). "
260
+ "Override the API base URL (e.g. https://api.eu.deepgram.com). "
261
+ "Hosts other than Deepgram API hosts must be listed in "
262
+ "api.allowed_hosts. "
144
263
  "REST and WebSocket endpoints are derived from this host. "
145
264
  "Also settable via the DEEPGRAM_BASE_URL environment variable."
146
265
  ),
@@ -180,6 +299,14 @@ def preprocess_hyphenated_commands(args: list[str]) -> list[str]:
180
299
  is_flag=True,
181
300
  help="Show detailed performance timing information",
182
301
  )
302
+ @click.option(
303
+ "--no-telemetry",
304
+ is_flag=True,
305
+ is_eager=True,
306
+ expose_value=False,
307
+ callback=_no_telemetry_cb,
308
+ help="No effect: this version of dg sends no telemetry",
309
+ )
183
310
  @click.option(
184
311
  "--non-interactive",
185
312
  is_flag=True,
@@ -230,14 +357,14 @@ def cli(
230
357
  enable_timing()
231
358
 
232
359
  with TimingContext("cli_initialization"):
233
- # A --base-url flag overrides the configured/env base URL (flag wins).
234
- # Config reads DEEPGRAM_BASE_URL at init, so set it before constructing.
235
- if base_url:
236
- os.environ["DEEPGRAM_BASE_URL"] = base_url
237
-
238
- # Initialize configuration
360
+ # Initialize configuration. A --base-url flag overrides the
361
+ # configured/env base URL for this run only; Config records that it
362
+ # came from the command line, so it is never saved and gets
363
+ # credentials only if the host is trusted.
239
364
  ctx.ensure_object(dict)
240
- ctx.obj["config"] = Config(config_path=config, profile=profile)
365
+ ctx.obj["config"] = Config(
366
+ config_path=config, profile=profile, base_url=base_url
367
+ )
241
368
  # Global --api-key passthrough (highest-precedence explicit credential).
242
369
  ctx.obj["api_key"] = api_key
243
370
  ctx.obj["timing"] = timing or timing_detailed
@@ -250,6 +377,10 @@ def cli(
250
377
  verbose=verbose,
251
378
  )
252
379
 
380
+ # Telemetry is decided here, after option parsing, so --config and
381
+ # --no-telemetry are honored.
382
+ _init_telemetry(ctx.obj["config"])
383
+
253
384
 
254
385
  # Load commands from entry points
255
386
  def load_commands() -> None:
@@ -266,27 +397,6 @@ def load_commands() -> None:
266
397
  load_commands()
267
398
 
268
399
 
269
- @contextmanager
270
- def _telemetry_transaction() -> Iterator[None]:
271
- """Wrap CLI dispatch in a Sentry transaction (no-op when telemetry is off).
272
-
273
- The transaction is named generically ('cli') here. BaseCommand.execute
274
- renames it to the full Click command path (e.g. 'deepctl debug audio')
275
- once Click has dispatched — which is the single source of truth for
276
- the name. Trying to extract a command name from raw sys.argv is unsafe
277
- because flag values can look like command names ('--output json' makes
278
- 'json' look like a command).
279
- """
280
- try:
281
- import sentry_sdk
282
- except ImportError:
283
- yield
284
- return
285
-
286
- with sentry_sdk.start_transaction(op="cli.command", name="cli"):
287
- yield
288
-
289
-
290
400
  def _global_option_hint(message: str) -> str | None:
291
401
  """Build a placement hint when a 'No such option' names a global option.
292
402
 
@@ -333,8 +443,40 @@ def _safe_console_print(message: str) -> None:
333
443
  pass
334
444
 
335
445
 
446
+ # Error handlers that never raise on a character the encoding lacks.
447
+ # "strict" and "surrogateescape" (a C locale) both raise.
448
+ _SAFE_ERRORS = frozenset(
449
+ {"backslashreplace", "replace", "ignore", "namereplace", "xmlcharrefreplace"}
450
+ )
451
+
452
+
453
+ def safe_output_streams() -> None:
454
+ """Have stdout and stderr escape what their encoding can't write.
455
+
456
+ A redirected stream on Windows uses the ANSI code page (cp1252, say),
457
+ which has no block art: writing the --help banner raised
458
+ UnicodeEncodeError and ended the command. Such a stream now writes
459
+ ``\\u2588`` instead. A UTF-8 stream is left as it is, and the encoding
460
+ never changes, so a real console keeps what Python chose for it.
461
+ """
462
+ import codecs
463
+ import io
464
+
465
+ for stream in (sys.stdout, sys.stderr):
466
+ if not isinstance(stream, io.TextIOWrapper):
467
+ continue # None, or a stream that can't be reconfigured
468
+ try:
469
+ encoding = codecs.lookup(stream.encoding or "").name
470
+ if encoding == "utf-8" or stream.errors in _SAFE_ERRORS:
471
+ continue
472
+ stream.reconfigure(errors="backslashreplace")
473
+ except (LookupError, OSError, ValueError):
474
+ continue # an unknown encoding, or a closed stream
475
+
476
+
336
477
  def main() -> None:
337
478
  """Main entry point for the CLI."""
479
+ safe_output_streams()
338
480
  try:
339
481
  # Check if timing was requested and enable it early
340
482
  args = sys.argv[1:] # Skip the program name
@@ -345,17 +487,15 @@ def main() -> None:
345
487
  if timing_requested:
346
488
  enable_timing()
347
489
 
348
- # Initialize phone-home telemetry and install the --help footer.
349
- # Both are no-ops when the user has opted out via config or env.
490
+ # Install the --help telemetry footer. Telemetry itself is decided in
491
+ # the root callback, after option parsing. `--no-telemetry` is also
492
+ # applied here so it holds even after an eager `--help` before it.
350
493
  try:
351
- from deepctl_telemetry import (
352
- init_telemetry,
353
- install_help_notice,
354
- )
494
+ from deepctl_telemetry import disable_for_run, install_help_notice
355
495
 
356
- telemetry_config = Config()
357
- init_telemetry(telemetry_config)
358
- install_help_notice(telemetry_config)
496
+ if "--no-telemetry" in _root_option_tokens(args):
497
+ disable_for_run()
498
+ install_help_notice(lambda: Config(config_path=_argv_config_path(args)))
359
499
  except Exception:
360
500
  pass
361
501
 
@@ -400,7 +540,7 @@ def main() -> None:
400
540
  processed_args = preprocess_hyphenated_commands(args)
401
541
 
402
542
  exit_code = 0
403
- with TimingContext("cli_execution"), _telemetry_transaction():
543
+ with TimingContext("cli_execution"):
404
544
  try:
405
545
  cli(args=processed_args, standalone_mode=False)
406
546
  except SystemExit as exc:
@@ -446,6 +586,7 @@ def main() -> None:
446
586
  sys.exit(1)
447
587
  except Exception as e:
448
588
  _safe_console_print(f"[red]Error: {e}[/red]")
589
+ _report_error(e)
449
590
  sys.exit(1) # 1 = error; 2 is reserved for user interrupt
450
591
 
451
592
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: deepctl
3
- Version: 0.3.3
3
+ Version: 0.4.1
4
4
  Summary: Official Deepgram CLI for speech recognition and audio intelligence
5
5
  Author-email: Deepgram <devrel@deepgram.com>
6
6
  Maintainer-email: Deepgram <devrel@deepgram.com>
@@ -27,35 +27,35 @@ Description-Content-Type: text/markdown
27
27
  License-File: LICENSE
28
28
  Requires-Dist: click>=8.0.0
29
29
  Requires-Dist: deepgram-sdk<8,>=7.7.0
30
- Requires-Dist: deepctl-core>=0.2.19
31
- Requires-Dist: deepctl-cmd-login>=0.1.20
32
- Requires-Dist: deepctl-cmd-projects>=0.2.0
30
+ Requires-Dist: deepctl-core>=0.3.1
31
+ Requires-Dist: deepctl-cmd-login>=0.2.1
32
+ Requires-Dist: deepctl-cmd-projects>=0.2.1
33
33
  Requires-Dist: deepctl-cmd-transcribe>=0.1.12
34
34
  Requires-Dist: deepctl-cmd-usage>=0.2.1
35
- Requires-Dist: deepctl-cmd-mcp>=0.1.15
36
- Requires-Dist: deepctl-cmd-api>=0.0.2
35
+ Requires-Dist: deepctl-cmd-mcp>=0.1.16
36
+ Requires-Dist: deepctl-cmd-api>=0.0.3
37
37
  Requires-Dist: deepctl-cmd-debug>=0.1.13
38
38
  Requires-Dist: deepctl-cmd-debug-browser>=0.1.12
39
39
  Requires-Dist: deepctl-cmd-debug-network>=0.1.12
40
- Requires-Dist: deepctl-cmd-debug-audio>=0.1.14
41
- Requires-Dist: deepctl-cmd-debug-probe>=0.0.2
42
- Requires-Dist: deepctl-cmd-debug-toolkit>=0.1.0
43
- Requires-Dist: deepctl-cmd-ffprobe>=0.0.3
44
- Requires-Dist: deepctl-cmd-update>=0.2.7
45
- Requires-Dist: deepctl-cmd-plugin>=0.1.14
46
- Requires-Dist: deepctl-cmd-skills>=0.0.9
47
- Requires-Dist: deepctl-cmd-init>=0.0.4
40
+ Requires-Dist: deepctl-cmd-debug-audio>=0.1.15
41
+ Requires-Dist: deepctl-cmd-debug-probe>=0.0.3
42
+ Requires-Dist: deepctl-cmd-debug-toolkit>=0.2.0
43
+ Requires-Dist: deepctl-cmd-ffprobe>=0.0.4
44
+ Requires-Dist: deepctl-cmd-update>=0.2.8
45
+ Requires-Dist: deepctl-cmd-plugin>=0.1.15
46
+ Requires-Dist: deepctl-cmd-skills>=0.0.10
47
+ Requires-Dist: deepctl-cmd-init>=0.1.1
48
48
  Requires-Dist: deepctl-cmd-models>=0.1.1
49
- Requires-Dist: deepctl-cmd-speak>=0.0.5
49
+ Requires-Dist: deepctl-cmd-speak>=0.0.6
50
50
  Requires-Dist: deepctl-cmd-keys>=0.1.0
51
51
  Requires-Dist: deepctl-cmd-read>=0.1.0
52
- Requires-Dist: deepctl-cmd-listen>=0.0.14
52
+ Requires-Dist: deepctl-cmd-listen>=0.0.15
53
53
  Requires-Dist: deepctl-cmd-requests>=0.1.0
54
54
  Requires-Dist: deepctl-cmd-billing>=0.1.0
55
55
  Requires-Dist: deepctl-cmd-members>=0.1.0
56
56
  Requires-Dist: deepctl-cmd-completion>=0.0.3
57
- Requires-Dist: deepctl-shared-utils>=0.1.13
58
- Requires-Dist: deepctl-telemetry>=0.0.7
57
+ Requires-Dist: deepctl-shared-utils>=0.1.14
58
+ Requires-Dist: deepctl-telemetry>=0.0.8
59
59
  Requires-Dist: pydantic>=2.0.0
60
60
  Requires-Dist: rich>=13.0.0
61
61
  Requires-Dist: httpx>=0.24.0
@@ -141,13 +141,22 @@ iwr https://deepgram.com/install.ps1 -useb | iex
141
141
  pip install deepctl # pip
142
142
  uv tool install deepctl # uv
143
143
  pipx install deepctl # pipx
144
+ npm install -g @deepgram/cli # npm (installs dg and deepgram)
144
145
  ```
145
146
 
147
+ The npm package is a launcher for the same Python CLI. It needs
148
+ Node.js 20+ and either [uv](https://docs.astral.sh/uv/) or Python 3.10-3.14
149
+ with `venv`, and sets the CLI up on first run. It installs prebuilt wheels
150
+ only, so it (and `npx`) fails on Intel Macs and Windows on ARM64, where
151
+ `cryptography` has none; on an Intel Mac, use Homebrew. See
152
+ [npm/README.md](npm/README.md).
153
+
146
154
  ### Try Without Installing
147
155
 
148
156
  ```bash
149
157
  uvx deepctl --help
150
158
  pipx run deepctl --help
159
+ npx @deepgram/cli --help
151
160
  ```
152
161
 
153
162
  ## Getting Started
@@ -242,7 +251,7 @@ dg requests --status failed --endpoint listen # Debug failed requests
242
251
  dg api /v1/projects --jq '.projects[0].name'
243
252
 
244
253
  # MCP server for AI editors (Claude Code, Cursor, etc.)
245
- dg mcp --transport sse --port 8000
254
+ dg mcp
246
255
  ```
247
256
 
248
257
  ## Features
@@ -352,11 +361,11 @@ dg debug probe --port 3100 # Live stream audio analysis
352
361
 
353
362
  ### MCP Server
354
363
 
355
- Connect Deepgram tools to AI coding assistants (Claude Code, Cursor, etc.).
364
+ Give AI coding assistants (Claude Code, Cursor, etc.) Deepgram documentation
365
+ search over stdio.
356
366
 
357
367
  ```bash
358
368
  dg mcp # Start MCP server (stdio)
359
- dg mcp --transport sse --port 8000 # SSE transport
360
369
  ```
361
370
 
362
371
  Add to your editor's MCP config:
@@ -507,57 +516,71 @@ dg plugin remove <package> # Remove
507
516
 
508
517
  Create your own — see the [plugin example](packages/deepctl-plugin-example).
509
518
 
510
- ## Configuration
511
-
512
- **Priority:** CLI flags > environment variables > profile config > project config
519
+ A plugin can't replace a built-in command such as `login` or `debug audio`.
520
+ dg runs the built-in and prints a warning that names the plugin's package.
521
+ A plugin whose entry point is named like a built-in command is skipped before
522
+ it's imported, so none of its code runs; give the entry point a different name.
523
+ To use a plugin's version anyway, list the command in the user config file
524
+ at its standard location (`~/.config/deepctl/config.yaml` on Linux, or
525
+ `$XDG_CONFIG_HOME/deepctl/config.yaml` when `XDG_CONFIG_HOME` is set;
526
+ `~/Library/Application Support/deepctl/config.yaml` on macOS;
527
+ `%LOCALAPPDATA%\deepgram\deepctl\config.yaml` on Windows):
513
528
 
514
- ```bash
515
- dg login # Interactive or --api-key
516
- dg login --profile staging --api-key SK # Named profiles
517
- dg profiles --list # List profiles
518
- dg profiles --switch staging # Switch profile
529
+ ```yaml
530
+ plugins:
531
+ allow_override:
532
+ - login
533
+ - debug audio
519
534
  ```
520
535
 
521
- For structured output, use `--output json|yaml|table|csv` after leaf commands
522
- that do not define their own output option, or before any command as a global
523
- flag: `dg --output json <command>`. `dg speak --output FILE` writes audio to
524
- `FILE`, so use `dg --output json speak ...` for Speak's structured output.
525
-
526
- ## Telemetry
527
-
528
- The CLI phones home anonymous error reports to help us catch crashes and regressions before users have to file an issue. It's **on by default** and easy to turn off.
536
+ dg reads this setting only from that file, never from `deepgram.yaml` or a
537
+ `--config` file. Only allow a plugin you trust: it receives everything you
538
+ pass to that command, including API keys.
529
539
 
530
- **What's collected:** Python exceptions, stack traces, the CLI version, and the Python runtime. Request bodies, headers, cookies, API keys, email addresses, IP addresses, and usernames are scrubbed before send. No performance traces, no profiling, no replays — errors only.
540
+ ## Configuration
531
541
 
532
- **Where it goes:** the `dx-cli` Sentry project owned by the Deepgram DX team.
542
+ **Priority:** CLI flags > environment variables > project `deepgram.yaml` > user
543
+ config. A project `deepgram.yaml` is read after the user config, but it can set
544
+ only `output.*` settings and a regional Deepgram `base_url` (see below).
533
545
 
534
- ### Opt out
546
+ The API key is the exception: dg uses `--api-key`, then the key `dg login` saved
547
+ in your system keyring, then `DEEPGRAM_API_KEY`, then an `api_key` in the config
548
+ file (where `dg login` saves it when no keyring is available).
535
549
 
536
- Persistent (recommended) — add this to your `config.yaml`
537
- (`~/.config/deepctl/config.yaml` on Linux,
538
- `~/Library/Application Support/deepctl/config.yaml` on macOS,
539
- `%LOCALAPPDATA%\deepgram\deepctl\config.yaml` on Windows):
550
+ dg sends your API key only to Deepgram API hosts, to hosts you list in
551
+ `api.allowed_hosts` in your own `config.yaml`, or to `DEEPGRAM_BASE_URL` (not in
552
+ an AI agent session). Anything else is refused before a request is made. Hosts
553
+ must use https: plain http works only for localhost, 127.0.0.0/8 and [::1]. For
554
+ a proxy or self-hosted deployment, add its host (and port, to approve only that
555
+ port):
540
556
 
541
557
  ```yaml
542
- telemetry:
543
- enabled: false
558
+ api:
559
+ allowed_hosts:
560
+ - my-proxy.example.com
561
+ - localhost:8080
544
562
  ```
545
563
 
546
- One-shot (CI, scripts, single command):
564
+ A project's `deepgram.yaml` can't set credentials, a project ID, a profile or a
565
+ non-Deepgram `base_url`, and can't approve hosts.
547
566
 
548
567
  ```bash
549
- DEEPCTL_TELEMETRY_DISABLED=1 dg listen recording.wav
568
+ dg login # Interactive or --api-key
569
+ dg login --profile staging --api-key SK # Named profiles
570
+ dg profiles --list # List profiles
571
+ dg profiles --switch staging # Switch profile
550
572
  ```
551
573
 
552
- ### Override the destination
574
+ For structured output, use `--output json|yaml|table|csv` after leaf commands
575
+ that do not define their own output option, or before any command as a global
576
+ flag: `dg --output json <command>`. `dg speak --output FILE` writes audio to
577
+ `FILE`, so use `dg --output json speak ...` for Speak's structured output.
553
578
 
554
- For forks or self-hosted Sentry, point telemetry at your own DSN:
579
+ ## Telemetry
555
580
 
556
- ```bash
557
- export DEEPCTL_TELEMETRY_DSN='https://<key>@<your-sentry>/<project>'
558
- ```
581
+ This version of dg sends no telemetry: no error reports, usage data or other telemetry, whether a command succeeds or fails. dg still contacts the Deepgram API for the commands you run, and pypi.org to check for updates.
559
582
 
560
- `DEEPCTL_TELEMETRY_DISABLED` always wins. The default DSN is baked into the package and only used when the override is unset.
583
+ The `DEEPCTL_TELEMETRY_DISABLED` environment variable and the `telemetry.enabled` setting in `config.yaml` are still accepted, and the new `--no-telemetry` flag is accepted too, but none of them has an effect in this version. `DEEPCTL_TELEMETRY_DSN` is now ignored unless it points at the local machine, which is for tests. The last line of `dg --help` says that dg sends no telemetry.
561
584
 
562
585
  ## Development
563
586
 
@@ -589,7 +612,7 @@ cli/
589
612
  │ ├── deepctl-cmd-keys/ # API keys management command for deepctl
590
613
  │ ├── deepctl-cmd-listen/ # Listen (live speech-to-text) command for deepctl
591
614
  │ ├── deepctl-cmd-login/ # Login command for deepctl
592
- │ ├── deepctl-cmd-mcp/ # MCP proxy command for deepctl — connects to Deepgram's developer API
615
+ │ ├── deepctl-cmd-mcp/ # MCP server for deepctl: Deepgram documentation search over stdio
593
616
  │ ├── deepctl-cmd-members/ # Members management command for deepctl
594
617
  │ ├── deepctl-cmd-models/ # Models command for deepctl
595
618
  │ ├── deepctl-cmd-plugin/ # Plugin management command for deepctl
@@ -604,7 +627,7 @@ cli/
604
627
  │ ├── deepctl-core/ # Core components for deepctl
605
628
  │ ├── deepctl-plugin-example/ # Example plugin for deepctl
606
629
  │ ├── deepctl-shared-utils/ # Shared utilities for deepctl
607
- │ └── deepctl-telemetry/ # Opt-out phone-home telemetry for deepctl
630
+ │ └── deepctl-telemetry/ # Error reporting for deepctl; this version sends no telemetry
608
631
  ├── tests/ # Integration tests
609
632
  └── Makefile # Development tasks
610
633
  ```
@@ -630,7 +653,7 @@ cli/
630
653
  | `deepctl listen` | Listen (live speech-to-text) command for deepctl |
631
654
  | `deepctl login` | Login command for deepctl |
632
655
  | `deepctl logout` | Login command for deepctl |
633
- | `deepctl mcp` | MCP proxy command for deepctl — connects to Deepgram's developer API |
656
+ | `deepctl mcp` | MCP server for deepctl: Deepgram documentation search over stdio |
634
657
  | `deepctl members` | Members management command for deepctl |
635
658
  | `deepctl models` | Models command for deepctl |
636
659
  | `deepctl plugin` | Plugin management command for deepctl |
@@ -665,7 +688,7 @@ cli/
665
688
  | [`deepctl-cmd-keys`](packages/deepctl-cmd-keys) | API keys management command for deepctl |
666
689
  | [`deepctl-cmd-listen`](packages/deepctl-cmd-listen) | Listen (live speech-to-text) command for deepctl |
667
690
  | [`deepctl-cmd-login`](packages/deepctl-cmd-login) | Login command for deepctl |
668
- | [`deepctl-cmd-mcp`](packages/deepctl-cmd-mcp) | MCP proxy command for deepctl — connects to Deepgram's developer API |
691
+ | [`deepctl-cmd-mcp`](packages/deepctl-cmd-mcp) | MCP server for deepctl: Deepgram documentation search over stdio |
669
692
  | [`deepctl-cmd-members`](packages/deepctl-cmd-members) | Members management command for deepctl |
670
693
  | [`deepctl-cmd-models`](packages/deepctl-cmd-models) | Models command for deepctl |
671
694
  | [`deepctl-cmd-plugin`](packages/deepctl-cmd-plugin) | Plugin management command for deepctl |
@@ -680,7 +703,7 @@ cli/
680
703
  | [`deepctl-core`](packages/deepctl-core) | Core components for deepctl |
681
704
  | [`deepctl-plugin-example`](packages/deepctl-plugin-example) | Example plugin for deepctl |
682
705
  | [`deepctl-shared-utils`](packages/deepctl-shared-utils) | Shared utilities for deepctl |
683
- | [`deepctl-telemetry`](packages/deepctl-telemetry) | Opt-out phone-home telemetry for deepctl |
706
+ | [`deepctl-telemetry`](packages/deepctl-telemetry) | Error reporting for deepctl; this version sends no telemetry |
684
707
  <!-- END:packages -->
685
708
 
686
709
  ## Release
@@ -1,34 +1,34 @@
1
1
  click>=8.0.0
2
2
  deepgram-sdk<8,>=7.7.0
3
- deepctl-core>=0.2.19
4
- deepctl-cmd-login>=0.1.20
5
- deepctl-cmd-projects>=0.2.0
3
+ deepctl-core>=0.3.1
4
+ deepctl-cmd-login>=0.2.1
5
+ deepctl-cmd-projects>=0.2.1
6
6
  deepctl-cmd-transcribe>=0.1.12
7
7
  deepctl-cmd-usage>=0.2.1
8
- deepctl-cmd-mcp>=0.1.15
9
- deepctl-cmd-api>=0.0.2
8
+ deepctl-cmd-mcp>=0.1.16
9
+ deepctl-cmd-api>=0.0.3
10
10
  deepctl-cmd-debug>=0.1.13
11
11
  deepctl-cmd-debug-browser>=0.1.12
12
12
  deepctl-cmd-debug-network>=0.1.12
13
- deepctl-cmd-debug-audio>=0.1.14
14
- deepctl-cmd-debug-probe>=0.0.2
15
- deepctl-cmd-debug-toolkit>=0.1.0
16
- deepctl-cmd-ffprobe>=0.0.3
17
- deepctl-cmd-update>=0.2.7
18
- deepctl-cmd-plugin>=0.1.14
19
- deepctl-cmd-skills>=0.0.9
20
- deepctl-cmd-init>=0.0.4
13
+ deepctl-cmd-debug-audio>=0.1.15
14
+ deepctl-cmd-debug-probe>=0.0.3
15
+ deepctl-cmd-debug-toolkit>=0.2.0
16
+ deepctl-cmd-ffprobe>=0.0.4
17
+ deepctl-cmd-update>=0.2.8
18
+ deepctl-cmd-plugin>=0.1.15
19
+ deepctl-cmd-skills>=0.0.10
20
+ deepctl-cmd-init>=0.1.1
21
21
  deepctl-cmd-models>=0.1.1
22
- deepctl-cmd-speak>=0.0.5
22
+ deepctl-cmd-speak>=0.0.6
23
23
  deepctl-cmd-keys>=0.1.0
24
24
  deepctl-cmd-read>=0.1.0
25
- deepctl-cmd-listen>=0.0.14
25
+ deepctl-cmd-listen>=0.0.15
26
26
  deepctl-cmd-requests>=0.1.0
27
27
  deepctl-cmd-billing>=0.1.0
28
28
  deepctl-cmd-members>=0.1.0
29
29
  deepctl-cmd-completion>=0.0.3
30
- deepctl-shared-utils>=0.1.13
31
- deepctl-telemetry>=0.0.7
30
+ deepctl-shared-utils>=0.1.14
31
+ deepctl-telemetry>=0.0.8
32
32
  pydantic>=2.0.0
33
33
  rich>=13.0.0
34
34
  httpx>=0.24.0
File without changes
File without changes