deepctl-core 0.2.17__tar.gz → 0.2.18__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 (22) hide show
  1. {deepctl_core-0.2.17 → deepctl_core-0.2.18}/PKG-INFO +1 -1
  2. {deepctl_core-0.2.17 → deepctl_core-0.2.18}/pyproject.toml +1 -1
  3. {deepctl_core-0.2.17 → deepctl_core-0.2.18}/src/deepctl_core/auth.py +5 -2
  4. {deepctl_core-0.2.17 → deepctl_core-0.2.18}/src/deepctl_core/base_command.py +54 -12
  5. {deepctl_core-0.2.17 → deepctl_core-0.2.18}/src/deepctl_core/base_group_command.py +5 -2
  6. {deepctl_core-0.2.17 → deepctl_core-0.2.18}/src/deepctl_core/client.py +6 -2
  7. {deepctl_core-0.2.17 → deepctl_core-0.2.18}/src/deepctl_core/timing.py +5 -2
  8. {deepctl_core-0.2.17 → deepctl_core-0.2.18}/src/deepctl_core.egg-info/PKG-INFO +1 -1
  9. {deepctl_core-0.2.17 → deepctl_core-0.2.18}/README.md +0 -0
  10. {deepctl_core-0.2.17 → deepctl_core-0.2.18}/setup.cfg +0 -0
  11. {deepctl_core-0.2.17 → deepctl_core-0.2.18}/src/deepctl_core/__init__.py +0 -0
  12. {deepctl_core-0.2.17 → deepctl_core-0.2.18}/src/deepctl_core/config.py +0 -0
  13. {deepctl_core-0.2.17 → deepctl_core-0.2.18}/src/deepctl_core/models.py +0 -0
  14. {deepctl_core-0.2.17 → deepctl_core-0.2.18}/src/deepctl_core/output.py +0 -0
  15. {deepctl_core-0.2.17 → deepctl_core-0.2.18}/src/deepctl_core/plugin_env.py +0 -0
  16. {deepctl_core-0.2.17 → deepctl_core-0.2.18}/src/deepctl_core/plugin_manager.py +0 -0
  17. {deepctl_core-0.2.17 → deepctl_core-0.2.18}/src/deepctl_core/py.typed +0 -0
  18. {deepctl_core-0.2.17 → deepctl_core-0.2.18}/src/deepctl_core/skill_generator.py +0 -0
  19. {deepctl_core-0.2.17 → deepctl_core-0.2.18}/src/deepctl_core.egg-info/SOURCES.txt +0 -0
  20. {deepctl_core-0.2.17 → deepctl_core-0.2.18}/src/deepctl_core.egg-info/dependency_links.txt +0 -0
  21. {deepctl_core-0.2.17 → deepctl_core-0.2.18}/src/deepctl_core.egg-info/requires.txt +0 -0
  22. {deepctl_core-0.2.17 → deepctl_core-0.2.18}/src/deepctl_core.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: deepctl-core
3
- Version: 0.2.17
3
+ Version: 0.2.18
4
4
  Summary: Core components for deepctl
5
5
  Author-email: Deepgram <devrel@deepgram.com>
6
6
  Maintainer-email: Deepgram <devrel@deepgram.com>
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "deepctl-core"
7
- version = "0.2.17" # x-release-please-version
7
+ version = "0.2.18" # x-release-please-version
8
8
  description = "Core components for deepctl"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -9,14 +9,17 @@ from urllib.parse import urlencode
9
9
  import httpx
10
10
  import keyring
11
11
  from pydantic import BaseModel
12
- from rich.console import Console
13
12
  from rich.progress import Progress, SpinnerColumn, TextColumn
14
13
 
15
14
  from .client import _split_base_url
16
15
  from .config import Config
17
16
  from .models import ProfileInfo, ProfilesResult
17
+ from .output import stderr_console
18
18
 
19
- console = Console()
19
+ # Diagnostics only: everything printed here is status or error chrome, so
20
+ # it goes to stderr and can never corrupt the machine-readable payload a
21
+ # command writes to stdout. See get_status_console() in output.py.
22
+ console = stderr_console
20
23
 
21
24
  # Auth provider base URL (dx-id OIDC provider)
22
25
  AUTH_BASE_URL = os.getenv("DEEPGRAM_CLI_BASE_URL", "https://id.dx.deepgram.com")
@@ -4,15 +4,20 @@ from abc import ABC, abstractmethod
4
4
  from typing import Any, ClassVar
5
5
 
6
6
  import click
7
- from rich.console import Console
8
7
 
9
8
  from .auth import AuthManager
10
9
  from .client import DeepgramClient
11
10
  from .config import Config
12
- from .output import _agentic, print_error, print_info, print_warning, stderr_console
11
+ from .models import ErrorResult
12
+ from .output import _agentic, print_error, stderr_console
13
+ from .output import console as stdout_console
13
14
  from .timing import TimingContext
14
15
 
15
- console = Console()
16
+ # The PAYLOAD console -- stdout, deliberately. Tables, JSON and raw text
17
+ # written by _output_* are the machine-readable result and belong there.
18
+ # Shared instance rather than a bare Console() so it honours the agentic
19
+ # no-color/highlight settings; diagnostics use stderr_console instead.
20
+ console = stdout_console
16
21
 
17
22
 
18
23
  class BaseCommand(ABC):
@@ -99,16 +104,52 @@ class BaseCommand(ABC):
99
104
  "explicit flags",
100
105
  "environment variables",
101
106
  ]:
102
- print_info(f"Using credentials from {source}")
107
+ # Diagnostics, not the result. print_info /
108
+ # print_warning still write to stdout outside
109
+ # agentic mode, so with an explicit `-o json`
110
+ # these three lines landed in front of the
111
+ # payload and broke `| jq` (#104, success
112
+ # path). Route them straight to stderr; the
113
+ # prefixes mirror output.py so the rendering
114
+ # is unchanged in both modes.
115
+ info = "INFO:" if _agentic else "[blue]ℹ[/blue]"
116
+ stderr_console.print(
117
+ f"{info} Using credentials from {source}"
118
+ )
103
119
  if project_id:
104
- print_info(f"Affecting project: {project_id}")
120
+ stderr_console.print(
121
+ f"{info} Affecting project: {project_id}"
122
+ )
105
123
  else:
106
- print_warning("No project ID specified")
107
-
108
- except Exception:
109
- # guard() already printed helpful error messages;
110
- # exit without duplicating them.
111
- raise SystemExit(1)
124
+ warn = "WARN:" if _agentic else "[yellow]⚠[/yellow]"
125
+ stderr_console.print(
126
+ f"{warn} No project ID specified"
127
+ )
128
+
129
+ except Exception as auth_error:
130
+ # guard() already wrote the human-readable diagnosis to
131
+ # stderr, so don't duplicate it -- but stdout must still
132
+ # carry a parseable payload in a machine-readable
133
+ # format. A CI step doing `dg -o json ... > out.json`
134
+ # and parsing the result should get a structured error,
135
+ # not an empty file. output_result is a no-op in
136
+ # default mode, so this adds nothing for humans.
137
+ self._tag_telemetry_status("error")
138
+ try:
139
+ self.output_result(
140
+ ErrorResult(error=str(auth_error)), config
141
+ )
142
+ except OSError:
143
+ # Downstream stream closed. BrokenPipeError is an
144
+ # OSError, so one clause covers both.
145
+ pass
146
+ except ValueError as exc:
147
+ # Rich raises ValueError("I/O operation on closed
148
+ # file") when the stream went away mid-write; any
149
+ # other ValueError is a real bug.
150
+ if "closed file" not in str(exc):
151
+ raise
152
+ raise SystemExit(1) from auth_error
112
153
 
113
154
  # Check project ID if required
114
155
  if self.requires_project:
@@ -139,10 +180,11 @@ class BaseCommand(ABC):
139
180
  with TimingContext("output_processing"):
140
181
  try:
141
182
  self.output_result(result, config)
142
- except (BrokenPipeError, OSError):
183
+ except OSError:
143
184
  # Downstream stream closed (e.g. an MCP host disconnected
144
185
  # stdio after `dg mcp` finished). Nothing useful to log
145
186
  # here because the logger writes to the same closed stream.
187
+ # BrokenPipeError is an OSError, so one clause covers both.
146
188
  pass
147
189
  except ValueError as exc:
148
190
  if "closed file" not in str(exc):
@@ -3,14 +3,17 @@
3
3
  from typing import Any
4
4
 
5
5
  import click
6
- from rich.console import Console
7
6
 
8
7
  from .auth import AuthManager
9
8
  from .base_command import BaseCommand
10
9
  from .client import DeepgramClient
11
10
  from .config import Config
11
+ from .output import stderr_console
12
12
 
13
- console = Console()
13
+ # Diagnostics only: everything printed here is status or error chrome, so
14
+ # it goes to stderr and can never corrupt the machine-readable payload a
15
+ # command writes to stdout. See get_status_console() in output.py.
16
+ console = stderr_console
14
17
 
15
18
 
16
19
  class BaseGroupCommand(BaseCommand):
@@ -9,7 +9,8 @@ from typing import TYPE_CHECKING, Any, cast
9
9
  from deepgram import DeepgramClient as DGClient
10
10
  from deepgram import DeepgramClientEnvironment
11
11
  from deepgram.core.api_error import ApiError
12
- from rich.console import Console
12
+
13
+ from .output import stderr_console
13
14
 
14
15
  if TYPE_CHECKING:
15
16
  from collections.abc import Iterator
@@ -18,7 +19,10 @@ if TYPE_CHECKING:
18
19
  from .auth import AuthManager
19
20
  from .config import Config
20
21
 
21
- console = Console()
22
+ # Diagnostics only: everything printed here is status or error chrome, so
23
+ # it goes to stderr and can never corrupt the machine-readable payload a
24
+ # command writes to stdout. See get_status_console() in output.py.
25
+ console = stderr_console
22
26
 
23
27
 
24
28
  def _split_base_url(base_url: str) -> tuple[str, str, str]:
@@ -7,9 +7,12 @@ from dataclasses import dataclass, field
7
7
  from threading import local
8
8
  from typing import Any
9
9
 
10
- from rich.console import Console
10
+ from .output import stderr_console
11
11
 
12
- console = Console()
12
+ # Diagnostics only: everything printed here is status or error chrome, so
13
+ # it goes to stderr and can never corrupt the machine-readable payload a
14
+ # command writes to stdout. See get_status_console() in output.py.
15
+ console = stderr_console
13
16
 
14
17
  # Thread-local storage for timing data
15
18
  _timing_data = local()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: deepctl-core
3
- Version: 0.2.17
3
+ Version: 0.2.18
4
4
  Summary: Core components for deepctl
5
5
  Author-email: Deepgram <devrel@deepgram.com>
6
6
  Maintainer-email: Deepgram <devrel@deepgram.com>
File without changes
File without changes