deepctl-core 0.2.8__tar.gz → 0.2.9__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.
- {deepctl_core-0.2.8 → deepctl_core-0.2.9}/PKG-INFO +1 -1
- {deepctl_core-0.2.8 → deepctl_core-0.2.9}/pyproject.toml +1 -1
- {deepctl_core-0.2.8 → deepctl_core-0.2.9}/src/deepctl_core/auth.py +15 -5
- {deepctl_core-0.2.8 → deepctl_core-0.2.9}/src/deepctl_core/plugin_env.py +39 -0
- {deepctl_core-0.2.8 → deepctl_core-0.2.9}/src/deepctl_core/plugin_manager.py +39 -1
- {deepctl_core-0.2.8 → deepctl_core-0.2.9}/src/deepctl_core.egg-info/PKG-INFO +1 -1
- {deepctl_core-0.2.8 → deepctl_core-0.2.9}/README.md +0 -0
- {deepctl_core-0.2.8 → deepctl_core-0.2.9}/setup.cfg +0 -0
- {deepctl_core-0.2.8 → deepctl_core-0.2.9}/src/deepctl_core/__init__.py +0 -0
- {deepctl_core-0.2.8 → deepctl_core-0.2.9}/src/deepctl_core/base_command.py +0 -0
- {deepctl_core-0.2.8 → deepctl_core-0.2.9}/src/deepctl_core/base_group_command.py +0 -0
- {deepctl_core-0.2.8 → deepctl_core-0.2.9}/src/deepctl_core/client.py +0 -0
- {deepctl_core-0.2.8 → deepctl_core-0.2.9}/src/deepctl_core/config.py +0 -0
- {deepctl_core-0.2.8 → deepctl_core-0.2.9}/src/deepctl_core/models.py +0 -0
- {deepctl_core-0.2.8 → deepctl_core-0.2.9}/src/deepctl_core/output.py +0 -0
- {deepctl_core-0.2.8 → deepctl_core-0.2.9}/src/deepctl_core/py.typed +0 -0
- {deepctl_core-0.2.8 → deepctl_core-0.2.9}/src/deepctl_core/skill_generator.py +0 -0
- {deepctl_core-0.2.8 → deepctl_core-0.2.9}/src/deepctl_core/timing.py +0 -0
- {deepctl_core-0.2.8 → deepctl_core-0.2.9}/src/deepctl_core.egg-info/SOURCES.txt +0 -0
- {deepctl_core-0.2.8 → deepctl_core-0.2.9}/src/deepctl_core.egg-info/dependency_links.txt +0 -0
- {deepctl_core-0.2.8 → deepctl_core-0.2.9}/src/deepctl_core.egg-info/requires.txt +0 -0
- {deepctl_core-0.2.8 → deepctl_core-0.2.9}/src/deepctl_core.egg-info/top_level.txt +0 -0
|
@@ -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(
|
|
650
|
+
console.print(
|
|
651
|
+
"[green]✓[/green] Session stored securely in system keyring"
|
|
652
|
+
)
|
|
651
653
|
except Exception as e:
|
|
652
|
-
console.print(
|
|
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(
|
|
668
|
-
|
|
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(
|
|
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(
|
|
@@ -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 .
|
|
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
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|