deepctl-cmd-login 0.1.0__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_cmd_login-0.1.0/PKG-INFO +25 -0
- deepctl_cmd_login-0.1.0/README.md +3 -0
- deepctl_cmd_login-0.1.0/pyproject.toml +44 -0
- deepctl_cmd_login-0.1.0/setup.cfg +4 -0
- deepctl_cmd_login-0.1.0/src/deepctl_cmd_login/__init__.py +6 -0
- deepctl_cmd_login-0.1.0/src/deepctl_cmd_login/command.py +495 -0
- deepctl_cmd_login-0.1.0/src/deepctl_cmd_login/models.py +22 -0
- deepctl_cmd_login-0.1.0/src/deepctl_cmd_login.egg-info/PKG-INFO +25 -0
- deepctl_cmd_login-0.1.0/src/deepctl_cmd_login.egg-info/SOURCES.txt +11 -0
- deepctl_cmd_login-0.1.0/src/deepctl_cmd_login.egg-info/dependency_links.txt +1 -0
- deepctl_cmd_login-0.1.0/src/deepctl_cmd_login.egg-info/entry_points.txt +4 -0
- deepctl_cmd_login-0.1.0/src/deepctl_cmd_login.egg-info/requires.txt +5 -0
- deepctl_cmd_login-0.1.0/src/deepctl_cmd_login.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: deepctl-cmd-login
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Login command for deepctl
|
|
5
|
+
Author-email: Deepgram <devrel@deepgram.com>
|
|
6
|
+
Maintainer-email: Deepgram <devrel@deepgram.com>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
Keywords: deepgram,cli,login,authentication
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Requires-Python: >=3.10
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
Requires-Dist: deepctl-core>=0.1.0
|
|
18
|
+
Requires-Dist: click>=8.0.0
|
|
19
|
+
Requires-Dist: rich>=13.0.0
|
|
20
|
+
Requires-Dist: deepgram-sdk>=3.0.0
|
|
21
|
+
Requires-Dist: pydantic>=2.0.0
|
|
22
|
+
|
|
23
|
+
# deepctl-cmd-login
|
|
24
|
+
|
|
25
|
+
Login command for deepctl. This package is an internal component of deepctl and is not intended for standalone use.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "deepctl-cmd-login"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Login command for deepctl"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
authors = [{ name = "Deepgram", email = "devrel@deepgram.com" }]
|
|
12
|
+
maintainers = [{ name = "Deepgram", email = "devrel@deepgram.com" }]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Development Status :: 3 - Alpha",
|
|
15
|
+
"Intended Audience :: Developers",
|
|
16
|
+
"Programming Language :: Python :: 3",
|
|
17
|
+
"Programming Language :: Python :: 3.10",
|
|
18
|
+
"Programming Language :: Python :: 3.11",
|
|
19
|
+
"Programming Language :: Python :: 3.12",
|
|
20
|
+
]
|
|
21
|
+
keywords = ["deepgram", "cli", "login", "authentication"]
|
|
22
|
+
requires-python = ">=3.10"
|
|
23
|
+
dependencies = [
|
|
24
|
+
"deepctl-core>=0.1.0",
|
|
25
|
+
"click>=8.0.0",
|
|
26
|
+
"rich>=13.0.0",
|
|
27
|
+
"deepgram-sdk>=3.0.0",
|
|
28
|
+
"pydantic>=2.0.0",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
[project.scripts]
|
|
32
|
+
# Internal commands not meant to be called directly
|
|
33
|
+
|
|
34
|
+
[project.entry-points."deepctl.commands"]
|
|
35
|
+
login = "deepctl_cmd_login.command:LoginCommand"
|
|
36
|
+
logout = "deepctl_cmd_login.command:LogoutCommand"
|
|
37
|
+
profiles = "deepctl_cmd_login.command:ProfilesCommand"
|
|
38
|
+
|
|
39
|
+
[tool.setuptools]
|
|
40
|
+
package-dir = { "" = "src" }
|
|
41
|
+
|
|
42
|
+
[tool.setuptools.packages.find]
|
|
43
|
+
where = ["src"]
|
|
44
|
+
include = ["deepctl_cmd_login*"]
|
|
@@ -0,0 +1,495 @@
|
|
|
1
|
+
"""Login command for deepctl."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from deepctl_core import (
|
|
6
|
+
AuthenticationError,
|
|
7
|
+
AuthManager,
|
|
8
|
+
BaseCommand,
|
|
9
|
+
Config,
|
|
10
|
+
DeepgramClient,
|
|
11
|
+
ProfileInfo,
|
|
12
|
+
ProfilesResult,
|
|
13
|
+
)
|
|
14
|
+
from rich.console import Console
|
|
15
|
+
|
|
16
|
+
from .models import LoginResult, LogoutResult
|
|
17
|
+
|
|
18
|
+
console = Console()
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class LoginCommand(BaseCommand):
|
|
22
|
+
"""Login command for authenticating with Deepgram."""
|
|
23
|
+
|
|
24
|
+
name = "login"
|
|
25
|
+
help = "Log in to Deepgram with browser-based authentication or API key"
|
|
26
|
+
short_help = "Log in to Deepgram"
|
|
27
|
+
|
|
28
|
+
# Login doesn't require existing auth
|
|
29
|
+
requires_auth = False
|
|
30
|
+
requires_project = False
|
|
31
|
+
ci_friendly = True
|
|
32
|
+
|
|
33
|
+
def get_arguments(self) -> list[dict[str, Any]]:
|
|
34
|
+
"""Get command arguments and options."""
|
|
35
|
+
return [
|
|
36
|
+
{
|
|
37
|
+
"names": ["--api-key", "-k"],
|
|
38
|
+
"help": "Configure the CLI with your Deepgram API key",
|
|
39
|
+
"type": str,
|
|
40
|
+
"required": False,
|
|
41
|
+
"is_option": True,
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"names": ["--project-id", "-p"],
|
|
45
|
+
"help": "Configure the CLI with your Deepgram project ID",
|
|
46
|
+
"type": str,
|
|
47
|
+
"required": False,
|
|
48
|
+
"is_option": True,
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"names": ["--force-write", "-f"],
|
|
52
|
+
"help": (
|
|
53
|
+
"Don't prompt for confirmation when providing "
|
|
54
|
+
"credentials"
|
|
55
|
+
),
|
|
56
|
+
"is_flag": True,
|
|
57
|
+
"is_option": True,
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"names": ["--profile"],
|
|
61
|
+
"help": "Profile name to use for storing credentials",
|
|
62
|
+
"type": str,
|
|
63
|
+
"required": False,
|
|
64
|
+
"is_option": True,
|
|
65
|
+
},
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
def handle(
|
|
69
|
+
self,
|
|
70
|
+
config: Config,
|
|
71
|
+
auth_manager: AuthManager,
|
|
72
|
+
client: DeepgramClient,
|
|
73
|
+
**kwargs: Any,
|
|
74
|
+
) -> Any:
|
|
75
|
+
"""Handle login command."""
|
|
76
|
+
api_key = kwargs.get("api_key")
|
|
77
|
+
project_id = kwargs.get("project_id")
|
|
78
|
+
force_write = kwargs.get("force_write", False)
|
|
79
|
+
profile = kwargs.get("profile")
|
|
80
|
+
|
|
81
|
+
# Set profile if provided
|
|
82
|
+
if profile:
|
|
83
|
+
config.profile = profile
|
|
84
|
+
|
|
85
|
+
# Check if user is already logged in
|
|
86
|
+
if auth_manager.is_authenticated() and not force_write:
|
|
87
|
+
console.print(
|
|
88
|
+
f"[yellow]Already logged in to profile:[/yellow] "
|
|
89
|
+
f"{config.profile or 'default'}"
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
if not self.confirm("Do you want to login again?", default=False):
|
|
93
|
+
return LoginResult(
|
|
94
|
+
status="cancelled",
|
|
95
|
+
message="Login cancelled by user",
|
|
96
|
+
profile=config.profile or "default",
|
|
97
|
+
api_key_masked=None,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
# Determine authentication method
|
|
101
|
+
if api_key:
|
|
102
|
+
return self._cli_auth(
|
|
103
|
+
config, auth_manager, str(api_key), project_id, force_write
|
|
104
|
+
)
|
|
105
|
+
else:
|
|
106
|
+
return self._web_auth(config, auth_manager, force_write)
|
|
107
|
+
|
|
108
|
+
def _cli_auth(
|
|
109
|
+
self,
|
|
110
|
+
config: Config,
|
|
111
|
+
auth_manager: AuthManager,
|
|
112
|
+
api_key: str,
|
|
113
|
+
project_id: str | None,
|
|
114
|
+
force_write: bool,
|
|
115
|
+
) -> LoginResult:
|
|
116
|
+
"""Handle CLI authentication with API key."""
|
|
117
|
+
console.print("[blue]Configuring CLI with API key...[/blue]")
|
|
118
|
+
|
|
119
|
+
# Validate API key format
|
|
120
|
+
if not api_key.startswith(("sk-", "pk-")):
|
|
121
|
+
console.print(
|
|
122
|
+
"[yellow]Warning:[/yellow] API key format doesn't match "
|
|
123
|
+
"expected pattern"
|
|
124
|
+
)
|
|
125
|
+
if not force_write and not self.confirm(
|
|
126
|
+
"Continue anyway?", default=False
|
|
127
|
+
):
|
|
128
|
+
return LoginResult(
|
|
129
|
+
status="cancelled",
|
|
130
|
+
message="Login cancelled by user",
|
|
131
|
+
profile=config.profile or "default",
|
|
132
|
+
api_key_masked=None,
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
# Validate project ID is provided with API key
|
|
136
|
+
if not project_id:
|
|
137
|
+
console.print("[yellow]Warning:[/yellow] Project ID not provided")
|
|
138
|
+
console.print(
|
|
139
|
+
"You can set it later with: "
|
|
140
|
+
"deepctl login --project-id <project_id>"
|
|
141
|
+
)
|
|
142
|
+
console.print("Or use environment variable: DEEPGRAM_PROJECT_ID")
|
|
143
|
+
|
|
144
|
+
# Check if config file exists and prompt for overwrite
|
|
145
|
+
if not force_write:
|
|
146
|
+
if config.config_path.exists():
|
|
147
|
+
console.print(
|
|
148
|
+
f"[yellow]Configuration file already exists:[/yellow] "
|
|
149
|
+
f"{config.config_path}"
|
|
150
|
+
)
|
|
151
|
+
if not self.confirm(
|
|
152
|
+
"Overwrite existing configuration?", default=False
|
|
153
|
+
):
|
|
154
|
+
return LoginResult(
|
|
155
|
+
status="cancelled",
|
|
156
|
+
message="Login cancelled by user",
|
|
157
|
+
profile=config.profile or "default",
|
|
158
|
+
api_key_masked=None,
|
|
159
|
+
)
|
|
160
|
+
else:
|
|
161
|
+
if not self.confirm(
|
|
162
|
+
"Do you want to write these credentials to config?",
|
|
163
|
+
default=True,
|
|
164
|
+
):
|
|
165
|
+
return LoginResult(
|
|
166
|
+
status="cancelled",
|
|
167
|
+
message="Login cancelled by user",
|
|
168
|
+
profile=config.profile or "default",
|
|
169
|
+
api_key_masked=None,
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
try:
|
|
173
|
+
# Store credentials (verification happens inside
|
|
174
|
+
# login_with_api_key)
|
|
175
|
+
auth_manager.login_with_api_key(
|
|
176
|
+
api_key, project_id or "", force_write
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
profile_name = config.profile or "default"
|
|
180
|
+
return LoginResult(
|
|
181
|
+
status="success",
|
|
182
|
+
message="Successfully logged in with API key",
|
|
183
|
+
profile=profile_name,
|
|
184
|
+
api_key_masked=f"****{api_key[-4:]}",
|
|
185
|
+
project_id=project_id,
|
|
186
|
+
config_path=str(config.config_path),
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
except AuthenticationError as e:
|
|
190
|
+
console.print(f"[red]Authentication failed:[/red] {e}")
|
|
191
|
+
return LoginResult(
|
|
192
|
+
status="error",
|
|
193
|
+
message=str(e),
|
|
194
|
+
profile=config.profile or "default",
|
|
195
|
+
api_key_masked=None,
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
except Exception as e:
|
|
199
|
+
console.print(f"[red]Error during CLI authentication:[/red] {e}")
|
|
200
|
+
return LoginResult(
|
|
201
|
+
status="error",
|
|
202
|
+
message=str(e),
|
|
203
|
+
profile=config.profile or "default",
|
|
204
|
+
api_key_masked=None,
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
def _web_auth(
|
|
208
|
+
self, config: Config, auth_manager: AuthManager, force_write: bool
|
|
209
|
+
) -> LoginResult:
|
|
210
|
+
"""Handle web authentication with device flow."""
|
|
211
|
+
console.print("[blue]Starting web authentication...[/blue]")
|
|
212
|
+
|
|
213
|
+
# Check if config file exists and prompt for overwrite
|
|
214
|
+
if not force_write and config.config_path.exists():
|
|
215
|
+
console.print(
|
|
216
|
+
f"[yellow]Configuration file already exists:[/yellow] "
|
|
217
|
+
f"{config.config_path}"
|
|
218
|
+
)
|
|
219
|
+
if not self.confirm(
|
|
220
|
+
"Overwrite existing configuration?", default=False
|
|
221
|
+
):
|
|
222
|
+
return LoginResult(
|
|
223
|
+
status="cancelled",
|
|
224
|
+
message="Login cancelled by user",
|
|
225
|
+
profile=config.profile or "default",
|
|
226
|
+
api_key_masked=None,
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
try:
|
|
230
|
+
# Start device flow
|
|
231
|
+
auth_manager.login_with_device_flow()
|
|
232
|
+
|
|
233
|
+
profile_name = config.profile or "default"
|
|
234
|
+
|
|
235
|
+
# Retrieve the stored credentials
|
|
236
|
+
api_key = auth_manager.get_api_key()
|
|
237
|
+
project_id = auth_manager.get_project_id()
|
|
238
|
+
|
|
239
|
+
# Mask the API key for display
|
|
240
|
+
api_key_masked = None
|
|
241
|
+
if api_key:
|
|
242
|
+
api_key_masked = f"****{api_key[-4:]}"
|
|
243
|
+
|
|
244
|
+
return LoginResult(
|
|
245
|
+
status="success",
|
|
246
|
+
message="Successfully logged in via web authentication",
|
|
247
|
+
profile=profile_name,
|
|
248
|
+
api_key_masked=api_key_masked,
|
|
249
|
+
project_id=project_id,
|
|
250
|
+
config_path=str(config.config_path),
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
except AuthenticationError as e:
|
|
254
|
+
console.print(f"[red]Authentication failed:[/red] {e}")
|
|
255
|
+
return LoginResult(
|
|
256
|
+
status="error",
|
|
257
|
+
message=str(e),
|
|
258
|
+
profile=config.profile or "default",
|
|
259
|
+
api_key_masked=None,
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
except KeyboardInterrupt:
|
|
263
|
+
console.print(
|
|
264
|
+
"\n[yellow]Authentication cancelled by user[/yellow]"
|
|
265
|
+
)
|
|
266
|
+
return LoginResult(
|
|
267
|
+
status="cancelled",
|
|
268
|
+
message="Login cancelled by user",
|
|
269
|
+
profile=config.profile or "default",
|
|
270
|
+
api_key_masked=None,
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
except Exception as e:
|
|
274
|
+
console.print(f"[red]Error during web authentication:[/red] {e}")
|
|
275
|
+
return LoginResult(
|
|
276
|
+
status="error",
|
|
277
|
+
message=str(e),
|
|
278
|
+
profile=config.profile or "default",
|
|
279
|
+
api_key_masked=None,
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
class LogoutCommand(BaseCommand):
|
|
284
|
+
"""Logout command for clearing authentication."""
|
|
285
|
+
|
|
286
|
+
name = "logout"
|
|
287
|
+
help = "Log out and clear stored credentials"
|
|
288
|
+
short_help = "Log out of Deepgram"
|
|
289
|
+
|
|
290
|
+
# Logout doesn't require existing auth
|
|
291
|
+
requires_auth = False
|
|
292
|
+
requires_project = False
|
|
293
|
+
ci_friendly = True
|
|
294
|
+
|
|
295
|
+
def get_arguments(self) -> list[dict[str, Any]]:
|
|
296
|
+
"""Get command arguments and options."""
|
|
297
|
+
return [
|
|
298
|
+
{
|
|
299
|
+
"names": ["--profile"],
|
|
300
|
+
"help": "Profile to logout from (default: current profile)",
|
|
301
|
+
"type": str,
|
|
302
|
+
"required": False,
|
|
303
|
+
"is_option": True,
|
|
304
|
+
},
|
|
305
|
+
{
|
|
306
|
+
"names": ["--all"],
|
|
307
|
+
"help": "Logout from all profiles",
|
|
308
|
+
"is_flag": True,
|
|
309
|
+
"is_option": True,
|
|
310
|
+
},
|
|
311
|
+
]
|
|
312
|
+
|
|
313
|
+
def handle(
|
|
314
|
+
self,
|
|
315
|
+
config: Config,
|
|
316
|
+
auth_manager: AuthManager,
|
|
317
|
+
client: DeepgramClient,
|
|
318
|
+
**kwargs: Any,
|
|
319
|
+
) -> Any:
|
|
320
|
+
"""Handle logout command."""
|
|
321
|
+
profile = kwargs.get("profile")
|
|
322
|
+
logout_all = kwargs.get("all", False)
|
|
323
|
+
|
|
324
|
+
try:
|
|
325
|
+
if logout_all:
|
|
326
|
+
# Logout from all profiles
|
|
327
|
+
profiles = config.list_profiles()
|
|
328
|
+
for profile_name in profiles:
|
|
329
|
+
config.profile = profile_name
|
|
330
|
+
auth_manager_for_profile = AuthManager(config)
|
|
331
|
+
auth_manager_for_profile.logout()
|
|
332
|
+
|
|
333
|
+
console.print(
|
|
334
|
+
f"[green]✓[/green] Successfully logged out from all "
|
|
335
|
+
f"profiles ({len(profiles)} profiles)"
|
|
336
|
+
)
|
|
337
|
+
return LogoutResult(
|
|
338
|
+
status="success",
|
|
339
|
+
message="Logged out from all profiles",
|
|
340
|
+
profiles_count=len(profiles),
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
else:
|
|
344
|
+
# Logout from specific profile
|
|
345
|
+
if profile:
|
|
346
|
+
config.profile = profile
|
|
347
|
+
|
|
348
|
+
if not auth_manager.is_authenticated():
|
|
349
|
+
console.print("[yellow]Not currently logged in[/yellow]")
|
|
350
|
+
return LogoutResult(
|
|
351
|
+
status="info", message="Not currently logged in"
|
|
352
|
+
)
|
|
353
|
+
|
|
354
|
+
auth_manager.logout()
|
|
355
|
+
|
|
356
|
+
profile_name = config.profile or "default"
|
|
357
|
+
return LogoutResult(
|
|
358
|
+
status="success",
|
|
359
|
+
message=(
|
|
360
|
+
f"Successfully logged out from profile: {profile_name}"
|
|
361
|
+
),
|
|
362
|
+
profile=profile_name,
|
|
363
|
+
)
|
|
364
|
+
|
|
365
|
+
except Exception as e:
|
|
366
|
+
console.print(f"[red]Error during logout:[/red] {e}")
|
|
367
|
+
return LogoutResult(status="error", message=str(e))
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
class ProfilesCommand(BaseCommand):
|
|
371
|
+
"""Command to manage authentication profiles."""
|
|
372
|
+
|
|
373
|
+
name = "profiles"
|
|
374
|
+
help = "Manage authentication profiles"
|
|
375
|
+
short_help = "Manage profiles"
|
|
376
|
+
|
|
377
|
+
# Profiles command doesn't require auth
|
|
378
|
+
requires_auth = False
|
|
379
|
+
requires_project = False
|
|
380
|
+
ci_friendly = True
|
|
381
|
+
|
|
382
|
+
def get_arguments(self) -> list[dict[str, Any]]:
|
|
383
|
+
"""Get command arguments and options."""
|
|
384
|
+
return [
|
|
385
|
+
{
|
|
386
|
+
"names": ["--list", "-l"],
|
|
387
|
+
"help": "List all profiles",
|
|
388
|
+
"is_flag": True,
|
|
389
|
+
"is_option": True,
|
|
390
|
+
},
|
|
391
|
+
{
|
|
392
|
+
"names": ["--current"],
|
|
393
|
+
"help": "Show current profile",
|
|
394
|
+
"is_flag": True,
|
|
395
|
+
"is_option": True,
|
|
396
|
+
},
|
|
397
|
+
{
|
|
398
|
+
"names": ["--switch"],
|
|
399
|
+
"help": "Switch to a different profile",
|
|
400
|
+
"type": str,
|
|
401
|
+
"required": False,
|
|
402
|
+
"is_option": True,
|
|
403
|
+
},
|
|
404
|
+
]
|
|
405
|
+
|
|
406
|
+
def handle(
|
|
407
|
+
self,
|
|
408
|
+
config: Config,
|
|
409
|
+
auth_manager: AuthManager,
|
|
410
|
+
client: DeepgramClient,
|
|
411
|
+
**kwargs: Any,
|
|
412
|
+
) -> Any:
|
|
413
|
+
"""Handle profiles command."""
|
|
414
|
+
list_profiles = kwargs.get("list", False)
|
|
415
|
+
show_current = kwargs.get("current", False)
|
|
416
|
+
switch_profile = kwargs.get("switch")
|
|
417
|
+
|
|
418
|
+
if list_profiles:
|
|
419
|
+
profiles_result = auth_manager.list_profiles()
|
|
420
|
+
profiles = profiles_result.profiles
|
|
421
|
+
if not profiles:
|
|
422
|
+
console.print("[yellow]No profiles found[/yellow]")
|
|
423
|
+
return ProfilesResult(
|
|
424
|
+
status="info", message="No profiles found", profiles={}
|
|
425
|
+
)
|
|
426
|
+
|
|
427
|
+
console.print("[blue]Available profiles:[/blue]")
|
|
428
|
+
for name, info in profiles.items():
|
|
429
|
+
current_marker = (
|
|
430
|
+
" (current)"
|
|
431
|
+
if name == (config.profile or "default")
|
|
432
|
+
else ""
|
|
433
|
+
)
|
|
434
|
+
console.print(f" • {name}{current_marker}")
|
|
435
|
+
console.print(f" API Key: {info.api_key or 'Not set'}")
|
|
436
|
+
console.print(
|
|
437
|
+
f" Project ID: {info.project_id or 'Not set'}"
|
|
438
|
+
)
|
|
439
|
+
console.print(f" Base URL: {info.base_url}")
|
|
440
|
+
console.print()
|
|
441
|
+
return profiles_result
|
|
442
|
+
|
|
443
|
+
elif show_current:
|
|
444
|
+
current_profile = config.profile or "default"
|
|
445
|
+
profile_info = auth_manager.list_profiles().profiles.get(
|
|
446
|
+
current_profile,
|
|
447
|
+
ProfileInfo(api_key=None, project_id=None, base_url=""),
|
|
448
|
+
)
|
|
449
|
+
|
|
450
|
+
console.print(f"[blue]Current profile:[/blue] {current_profile}")
|
|
451
|
+
console.print(
|
|
452
|
+
f"[dim]API Key:[/dim] {profile_info.api_key or 'Not set'}"
|
|
453
|
+
)
|
|
454
|
+
console.print(
|
|
455
|
+
f"[dim]Project ID:[/dim] "
|
|
456
|
+
f"{profile_info.project_id or 'Not set'}"
|
|
457
|
+
)
|
|
458
|
+
console.print(
|
|
459
|
+
f"[dim]Base URL:[/dim] {profile_info.base_url or 'Not set'}"
|
|
460
|
+
)
|
|
461
|
+
|
|
462
|
+
return ProfilesResult(
|
|
463
|
+
status="success",
|
|
464
|
+
current_profile=current_profile,
|
|
465
|
+
profiles={current_profile: profile_info},
|
|
466
|
+
)
|
|
467
|
+
|
|
468
|
+
elif switch_profile:
|
|
469
|
+
profile_names = config.list_profiles()
|
|
470
|
+
|
|
471
|
+
if switch_profile not in profile_names:
|
|
472
|
+
console.print(
|
|
473
|
+
f"[red]Profile '{switch_profile}' not found[/red]"
|
|
474
|
+
)
|
|
475
|
+
return ProfilesResult(
|
|
476
|
+
status="error",
|
|
477
|
+
message=f"Profile '{switch_profile}' not found",
|
|
478
|
+
)
|
|
479
|
+
|
|
480
|
+
# Update default profile in config
|
|
481
|
+
config._config.default_profile = switch_profile
|
|
482
|
+
config.save()
|
|
483
|
+
|
|
484
|
+
console.print(
|
|
485
|
+
f"[green]✓[/green] Switched to profile: {switch_profile}"
|
|
486
|
+
)
|
|
487
|
+
return ProfilesResult(
|
|
488
|
+
status="success",
|
|
489
|
+
message=f"Switched to profile: {switch_profile}",
|
|
490
|
+
current_profile=switch_profile,
|
|
491
|
+
)
|
|
492
|
+
|
|
493
|
+
else:
|
|
494
|
+
# Default behavior - show current profile
|
|
495
|
+
return self.handle(config, auth_manager, client, current=True)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""Models for login command."""
|
|
2
|
+
|
|
3
|
+
from deepctl_core import BaseResult
|
|
4
|
+
from pydantic import Field
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class LoginResult(BaseResult):
|
|
8
|
+
"""Return structure for `deepctl login` command."""
|
|
9
|
+
|
|
10
|
+
profile: str
|
|
11
|
+
api_key_masked: str | None = Field(
|
|
12
|
+
None, description="Obfuscated key for display – e.g. ****abcd"
|
|
13
|
+
)
|
|
14
|
+
project_id: str | None = None
|
|
15
|
+
config_path: str | None = None
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class LogoutResult(BaseResult):
|
|
19
|
+
"""Return structure for logout command."""
|
|
20
|
+
|
|
21
|
+
profile: str | None = None
|
|
22
|
+
profiles_count: int | None = None # when --all is used
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: deepctl-cmd-login
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Login command for deepctl
|
|
5
|
+
Author-email: Deepgram <devrel@deepgram.com>
|
|
6
|
+
Maintainer-email: Deepgram <devrel@deepgram.com>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
Keywords: deepgram,cli,login,authentication
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Requires-Python: >=3.10
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
Requires-Dist: deepctl-core>=0.1.0
|
|
18
|
+
Requires-Dist: click>=8.0.0
|
|
19
|
+
Requires-Dist: rich>=13.0.0
|
|
20
|
+
Requires-Dist: deepgram-sdk>=3.0.0
|
|
21
|
+
Requires-Dist: pydantic>=2.0.0
|
|
22
|
+
|
|
23
|
+
# deepctl-cmd-login
|
|
24
|
+
|
|
25
|
+
Login command for deepctl. This package is an internal component of deepctl and is not intended for standalone use.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
src/deepctl_cmd_login/__init__.py
|
|
4
|
+
src/deepctl_cmd_login/command.py
|
|
5
|
+
src/deepctl_cmd_login/models.py
|
|
6
|
+
src/deepctl_cmd_login.egg-info/PKG-INFO
|
|
7
|
+
src/deepctl_cmd_login.egg-info/SOURCES.txt
|
|
8
|
+
src/deepctl_cmd_login.egg-info/dependency_links.txt
|
|
9
|
+
src/deepctl_cmd_login.egg-info/entry_points.txt
|
|
10
|
+
src/deepctl_cmd_login.egg-info/requires.txt
|
|
11
|
+
src/deepctl_cmd_login.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
deepctl_cmd_login
|