deepctl-core 0.2.8__tar.gz → 0.2.10__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.8 → deepctl_core-0.2.10}/PKG-INFO +1 -1
  2. {deepctl_core-0.2.8 → deepctl_core-0.2.10}/pyproject.toml +1 -1
  3. {deepctl_core-0.2.8 → deepctl_core-0.2.10}/src/deepctl_core/auth.py +15 -5
  4. {deepctl_core-0.2.8 → deepctl_core-0.2.10}/src/deepctl_core/config.py +7 -0
  5. {deepctl_core-0.2.8 → deepctl_core-0.2.10}/src/deepctl_core/plugin_env.py +39 -0
  6. {deepctl_core-0.2.8 → deepctl_core-0.2.10}/src/deepctl_core/plugin_manager.py +39 -1
  7. {deepctl_core-0.2.8 → deepctl_core-0.2.10}/src/deepctl_core.egg-info/PKG-INFO +1 -1
  8. {deepctl_core-0.2.8 → deepctl_core-0.2.10}/README.md +0 -0
  9. {deepctl_core-0.2.8 → deepctl_core-0.2.10}/setup.cfg +0 -0
  10. {deepctl_core-0.2.8 → deepctl_core-0.2.10}/src/deepctl_core/__init__.py +0 -0
  11. {deepctl_core-0.2.8 → deepctl_core-0.2.10}/src/deepctl_core/base_command.py +0 -0
  12. {deepctl_core-0.2.8 → deepctl_core-0.2.10}/src/deepctl_core/base_group_command.py +0 -0
  13. {deepctl_core-0.2.8 → deepctl_core-0.2.10}/src/deepctl_core/client.py +0 -0
  14. {deepctl_core-0.2.8 → deepctl_core-0.2.10}/src/deepctl_core/models.py +0 -0
  15. {deepctl_core-0.2.8 → deepctl_core-0.2.10}/src/deepctl_core/output.py +0 -0
  16. {deepctl_core-0.2.8 → deepctl_core-0.2.10}/src/deepctl_core/py.typed +0 -0
  17. {deepctl_core-0.2.8 → deepctl_core-0.2.10}/src/deepctl_core/skill_generator.py +0 -0
  18. {deepctl_core-0.2.8 → deepctl_core-0.2.10}/src/deepctl_core/timing.py +0 -0
  19. {deepctl_core-0.2.8 → deepctl_core-0.2.10}/src/deepctl_core.egg-info/SOURCES.txt +0 -0
  20. {deepctl_core-0.2.8 → deepctl_core-0.2.10}/src/deepctl_core.egg-info/dependency_links.txt +0 -0
  21. {deepctl_core-0.2.8 → deepctl_core-0.2.10}/src/deepctl_core.egg-info/requires.txt +0 -0
  22. {deepctl_core-0.2.8 → deepctl_core-0.2.10}/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.8
3
+ Version: 0.2.10
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.8" # x-release-please-version
7
+ version = "0.2.10" # x-release-please-version
8
8
  description = "Core components for deepctl"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -647,9 +647,13 @@ class AuthManager:
647
647
  keyring.set_password(
648
648
  KEYRING_SERVICE, f"refresh-token.{profile_name}", refresh_token
649
649
  )
650
- console.print("[green]✓[/green] Session stored securely in system keyring")
650
+ console.print(
651
+ "[green]✓[/green] Session stored securely in system keyring"
652
+ )
651
653
  except Exception as e:
652
- console.print(f"[yellow]Warning:[/yellow] Could not store in keyring: {e}")
654
+ console.print(
655
+ f"[yellow]Warning:[/yellow] Could not store in keyring: {e}"
656
+ )
653
657
 
654
658
  # Store expiry timestamps and project ID in config (non-sensitive).
655
659
  profile = self.config.get_profile(profile_name)
@@ -664,11 +668,17 @@ class AuthManager:
664
668
  keyring_available = False
665
669
 
666
670
  try:
667
- keyring.set_password(KEYRING_SERVICE, f"api-key.{profile_name}", api_key)
668
- console.print("[green]✓[/green] API key stored securely in system keyring")
671
+ keyring.set_password(
672
+ KEYRING_SERVICE, f"api-key.{profile_name}", api_key
673
+ )
674
+ console.print(
675
+ "[green]✓[/green] API key stored securely in system keyring"
676
+ )
669
677
  keyring_available = True
670
678
  except Exception as e:
671
- console.print(f"[yellow]Warning:[/yellow] Could not store in keyring: {e}")
679
+ console.print(
680
+ f"[yellow]Warning:[/yellow] Could not store in keyring: {e}"
681
+ )
672
682
  console.print("API key will be stored in config file instead")
673
683
 
674
684
  self.config.create_profile(
@@ -54,6 +54,12 @@ class ToolsConfig(BaseModel):
54
54
  ffprobe_path: str | None = None
55
55
 
56
56
 
57
+ class TelemetryConfig(BaseModel):
58
+ """Phone-home telemetry configuration. Opt-out — defaults to on."""
59
+
60
+ enabled: bool = True
61
+
62
+
57
63
  class DeepgramConfig(BaseModel):
58
64
  """Main configuration model."""
59
65
 
@@ -64,6 +70,7 @@ class DeepgramConfig(BaseModel):
64
70
  plugins: PluginConfig = Field(default_factory=PluginConfig)
65
71
  update: UpdateConfig = Field(default_factory=UpdateConfig)
66
72
  tools: ToolsConfig = Field(default_factory=ToolsConfig)
73
+ telemetry: TelemetryConfig = Field(default_factory=TelemetryConfig)
67
74
 
68
75
 
69
76
  class Config:
@@ -96,6 +96,45 @@ def get_venv_python() -> str | None:
96
96
  return str(python) if python.exists() else None
97
97
 
98
98
 
99
+ def get_venv_python_version() -> tuple[int, int] | None:
100
+ """Read the (major, minor) Python version that built the plugin venv.
101
+
102
+ Reads ``pyvenv.cfg`` from :data:`PLUGIN_VENV`. Both ``version`` (stdlib
103
+ ``python -m venv``) and ``version_info`` (uv-created venvs) are accepted.
104
+
105
+ Returns ``None`` when the venv doesn't exist, the cfg file is missing,
106
+ or the version line can't be parsed — callers should treat ``None`` as
107
+ "unknown" and skip ABI-mismatch checks rather than failing loudly.
108
+ """
109
+ if not PLUGIN_VENV.exists():
110
+ return None
111
+
112
+ cfg = PLUGIN_VENV / "pyvenv.cfg"
113
+ if not cfg.exists():
114
+ return None
115
+
116
+ try:
117
+ text = cfg.read_text()
118
+ except OSError:
119
+ return None
120
+
121
+ for line in text.splitlines():
122
+ key, sep, value = line.partition("=")
123
+ if not sep:
124
+ continue
125
+ if key.strip() not in ("version", "version_info"):
126
+ continue
127
+ parts = value.strip().split(".")
128
+ if len(parts) < 2:
129
+ continue
130
+ try:
131
+ return int(parts[0]), int(parts[1])
132
+ except ValueError:
133
+ continue
134
+
135
+ return None
136
+
137
+
99
138
  def get_venv_site_packages() -> Path | None:
100
139
  """Return the ``site-packages`` directory inside the plugin venv.
101
140
 
@@ -12,7 +12,13 @@ from rich.console import Console
12
12
  from .base_command import BaseCommand
13
13
  from .base_group_command import BaseGroupCommand
14
14
  from .models import ErrorResult, PluginInfo
15
- from .plugin_env import PLUGIN_VENV, get_plugin_state, get_venv_site_packages
15
+ from .output import print_warning
16
+ from .plugin_env import (
17
+ PLUGIN_VENV,
18
+ get_plugin_state,
19
+ get_venv_python_version,
20
+ get_venv_site_packages,
21
+ )
16
22
  from .timing import TimingContext
17
23
 
18
24
  console = Console()
@@ -139,6 +145,8 @@ class PluginManager:
139
145
  if site_packages is None:
140
146
  return
141
147
 
148
+ self._warn_if_plugin_venv_python_mismatch()
149
+
142
150
  site_packages_str = str(site_packages)
143
151
 
144
152
  # Append to sys.path so modules inside the plugin venv can be imported.
@@ -186,6 +194,36 @@ class PluginManager:
186
194
  except Exception as e:
187
195
  console.print(f"[red]Error loading plugins from plugin venv:[/red] {e}")
188
196
 
197
+ def _warn_if_plugin_venv_python_mismatch(self) -> None:
198
+ """Surface a one-line warning when the plugin venv's Python differs from ours.
199
+
200
+ Triggered when the underlying interpreter is bumped (e.g. ``brew upgrade
201
+ python@3.13`` rebuilding deepctl against a newer Python) without the user
202
+ recreating ``~/.deepctl/plugins/venv/``. Pure-Python plugins keep loading
203
+ via :data:`sys.path` bridging; only C-extension plugins fail to load,
204
+ and they fail with a confusing low-level ImportError. This warning gives
205
+ users a clear remediation before they hit that error.
206
+
207
+ Silent when the venv version can't be determined (no ``pyvenv.cfg``,
208
+ unparseable cfg, etc.) — there's no useful guidance to give.
209
+ """
210
+ venv_version = get_venv_python_version()
211
+ if venv_version is None:
212
+ return
213
+
214
+ running = (sys.version_info.major, sys.version_info.minor)
215
+ if venv_version == running:
216
+ return
217
+
218
+ venv_str = f"{venv_version[0]}.{venv_version[1]}"
219
+ running_str = f"{running[0]}.{running[1]}"
220
+ print_warning(
221
+ f"Plugin environment was built with Python {venv_str} but you're "
222
+ f"running Python {running_str}. C-extension plugins may fail to "
223
+ f"load. To rebuild:\n"
224
+ f" rm -rf {PLUGIN_VENV} && dg plugin install <your-plugin>"
225
+ )
226
+
189
227
  def _create_click_command(self, command_instance: Any) -> click.Command:
190
228
  """Create a Click command from a BaseCommand instance.
191
229
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: deepctl-core
3
- Version: 0.2.8
3
+ Version: 0.2.10
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