llamactl 0.3.0a13__py3-none-any.whl → 0.3.0a15__py3-none-any.whl
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.
- llama_deploy/cli/__init__.py +2 -1
- llama_deploy/cli/app.py +4 -7
- llama_deploy/cli/client.py +18 -32
- llama_deploy/cli/commands/auth.py +230 -235
- llama_deploy/cli/commands/deployment.py +24 -36
- llama_deploy/cli/commands/env.py +206 -0
- llama_deploy/cli/config/_config.py +385 -0
- llama_deploy/cli/config/auth_service.py +68 -0
- llama_deploy/cli/config/env_service.py +64 -0
- llama_deploy/cli/config/schema.py +31 -0
- llama_deploy/cli/interactive_prompts/utils.py +0 -39
- llama_deploy/cli/options.py +0 -9
- {llamactl-0.3.0a13.dist-info → llamactl-0.3.0a15.dist-info}/METADATA +3 -3
- {llamactl-0.3.0a13.dist-info → llamactl-0.3.0a15.dist-info}/RECORD +16 -13
- llama_deploy/cli/config.py +0 -241
- llama_deploy/cli/textual/api_key_profile_form.py +0 -563
- {llamactl-0.3.0a13.dist-info → llamactl-0.3.0a15.dist-info}/WHEEL +0 -0
- {llamactl-0.3.0a13.dist-info → llamactl-0.3.0a15.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
from dataclasses import replace
|
|
2
|
+
|
|
3
|
+
from llama_deploy.cli.config.schema import Environment
|
|
4
|
+
|
|
5
|
+
from ._config import ConfigManager, config_manager
|
|
6
|
+
from .auth_service import AuthService
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class EnvService:
|
|
10
|
+
def __init__(self, config_manager: ConfigManager):
|
|
11
|
+
self.config_manager = config_manager
|
|
12
|
+
|
|
13
|
+
def list_environments(self) -> list[Environment]:
|
|
14
|
+
return self.config_manager.list_environments()
|
|
15
|
+
|
|
16
|
+
def get_current_environment(self) -> Environment:
|
|
17
|
+
return self.config_manager.get_current_environment()
|
|
18
|
+
|
|
19
|
+
def switch_environment(self, api_url: str) -> Environment:
|
|
20
|
+
env = self.config_manager.get_environment(api_url)
|
|
21
|
+
if not env:
|
|
22
|
+
raise ValueError(
|
|
23
|
+
f"Environment '{api_url}' not found. Add it with 'llamactl auth env add <API_URL>'"
|
|
24
|
+
)
|
|
25
|
+
self.config_manager.set_settings_current_environment(api_url)
|
|
26
|
+
self.config_manager.set_settings_current_profile(None)
|
|
27
|
+
return env
|
|
28
|
+
|
|
29
|
+
def create_or_update_environment(self, env: Environment) -> None:
|
|
30
|
+
self.config_manager.create_or_update_environment(
|
|
31
|
+
env.api_url, env.requires_auth, env.min_llamactl_version
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
def delete_environment(self, api_url: str) -> bool:
|
|
35
|
+
return self.config_manager.delete_environment(api_url)
|
|
36
|
+
|
|
37
|
+
def current_auth_service(self) -> AuthService:
|
|
38
|
+
return AuthService(self.config_manager, self.get_current_environment())
|
|
39
|
+
|
|
40
|
+
def auto_update_env(self, env: Environment) -> Environment:
|
|
41
|
+
svc = AuthService(self.config_manager, env)
|
|
42
|
+
version = svc.fetch_server_version()
|
|
43
|
+
update = replace(env)
|
|
44
|
+
update.requires_auth = version.requires_auth
|
|
45
|
+
update.min_llamactl_version = version.min_llamactl_version
|
|
46
|
+
if update != env:
|
|
47
|
+
self.config_manager.create_or_update_environment(
|
|
48
|
+
update.api_url, update.requires_auth, update.min_llamactl_version
|
|
49
|
+
)
|
|
50
|
+
return update
|
|
51
|
+
|
|
52
|
+
def probe_environment(self, api_url: str) -> Environment:
|
|
53
|
+
clean = api_url.rstrip("/")
|
|
54
|
+
base_env = Environment(
|
|
55
|
+
api_url=clean, requires_auth=False, min_llamactl_version=None
|
|
56
|
+
)
|
|
57
|
+
svc = AuthService(self.config_manager, base_env)
|
|
58
|
+
version = svc.fetch_server_version()
|
|
59
|
+
base_env.requires_auth = version.requires_auth
|
|
60
|
+
base_env.min_llamactl_version = version.min_llamactl_version
|
|
61
|
+
return base_env
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
service = EnvService(config_manager)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
@dataclass
|
|
5
|
+
class Auth:
|
|
6
|
+
"""Auth Profile configuration"""
|
|
7
|
+
|
|
8
|
+
name: str
|
|
9
|
+
api_url: str
|
|
10
|
+
project_id: str
|
|
11
|
+
api_key: str | None = None
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass
|
|
15
|
+
class Environment:
|
|
16
|
+
"""Environment configuration stored in SQLite.
|
|
17
|
+
|
|
18
|
+
Note: `api_url`, `requires_auth`, and `min_llamactl_version` are persisted
|
|
19
|
+
in the environments table.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
api_url: str
|
|
23
|
+
requires_auth: bool
|
|
24
|
+
min_llamactl_version: str | None = None
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
DEFAULT_ENVIRONMENT = Environment(
|
|
28
|
+
api_url="https://api.cloud.llamaindex.ai",
|
|
29
|
+
requires_auth=True,
|
|
30
|
+
min_llamactl_version=None,
|
|
31
|
+
)
|
|
@@ -1,52 +1,13 @@
|
|
|
1
1
|
"""Shared utilities for CLI operations"""
|
|
2
2
|
|
|
3
3
|
import questionary
|
|
4
|
-
from rich import print as rprint
|
|
5
4
|
from rich.console import Console
|
|
6
5
|
|
|
7
|
-
from ..config import config_manager
|
|
8
6
|
from .session_utils import is_interactive_session
|
|
9
7
|
|
|
10
8
|
console = Console()
|
|
11
9
|
|
|
12
10
|
|
|
13
|
-
def select_profile(profile_name: str | None = None) -> str | None:
|
|
14
|
-
"""
|
|
15
|
-
Select a profile interactively if name not provided.
|
|
16
|
-
Returns the selected profile name or None if cancelled.
|
|
17
|
-
|
|
18
|
-
In non-interactive sessions, returns None if profile_name is not provided.
|
|
19
|
-
"""
|
|
20
|
-
if profile_name:
|
|
21
|
-
return profile_name
|
|
22
|
-
|
|
23
|
-
# Don't attempt interactive selection in non-interactive sessions
|
|
24
|
-
if not is_interactive_session():
|
|
25
|
-
return None
|
|
26
|
-
|
|
27
|
-
try:
|
|
28
|
-
profiles = config_manager.list_profiles()
|
|
29
|
-
|
|
30
|
-
if not profiles:
|
|
31
|
-
rprint("[yellow]No profiles found[/yellow]")
|
|
32
|
-
return None
|
|
33
|
-
|
|
34
|
-
choices = []
|
|
35
|
-
current_name = config_manager.get_current_profile_name()
|
|
36
|
-
|
|
37
|
-
for profile in profiles:
|
|
38
|
-
title = f"{profile.name} ({profile.api_url})"
|
|
39
|
-
if profile.name == current_name:
|
|
40
|
-
title += " [current]"
|
|
41
|
-
choices.append(questionary.Choice(title=title, value=profile.name))
|
|
42
|
-
|
|
43
|
-
return questionary.select("Select profile:", choices=choices).ask()
|
|
44
|
-
|
|
45
|
-
except Exception as e:
|
|
46
|
-
rprint(f"[red]Error loading profiles: {e}[/red]")
|
|
47
|
-
return None
|
|
48
|
-
|
|
49
|
-
|
|
50
11
|
def confirm_action(message: str, default: bool = False) -> bool:
|
|
51
12
|
"""
|
|
52
13
|
Ask for confirmation with a consistent interface.
|
llama_deploy/cli/options.py
CHANGED
|
@@ -2,7 +2,6 @@ import logging
|
|
|
2
2
|
from typing import Callable, ParamSpec, TypeVar
|
|
3
3
|
|
|
4
4
|
import click
|
|
5
|
-
from llama_deploy.cli.config import config_manager
|
|
6
5
|
from llama_deploy.cli.interactive_prompts.session_utils import is_interactive_session
|
|
7
6
|
|
|
8
7
|
from .debug import setup_file_logging
|
|
@@ -32,14 +31,6 @@ def global_options(f: Callable[P, R]) -> Callable[P, R]:
|
|
|
32
31
|
)(f)
|
|
33
32
|
|
|
34
33
|
|
|
35
|
-
def control_plane_url_callback(
|
|
36
|
-
ctx: click.Context, param: click.Parameter, value: str
|
|
37
|
-
) -> str:
|
|
38
|
-
if value:
|
|
39
|
-
config_manager.set_default_control_plane_url(value)
|
|
40
|
-
return value
|
|
41
|
-
|
|
42
|
-
|
|
43
34
|
def interactive_option(f: Callable[P, R]) -> Callable[P, R]:
|
|
44
35
|
"""Add an interactive option to the command"""
|
|
45
36
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: llamactl
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.0a15
|
|
4
4
|
Summary: A command-line interface for managing LlamaDeploy projects and deployments
|
|
5
5
|
Author: Adrian Lyjak
|
|
6
6
|
Author-email: Adrian Lyjak <adrianlyjak@gmail.com>
|
|
7
7
|
License: MIT
|
|
8
|
-
Requires-Dist: llama-deploy-core[client]>=0.3.
|
|
9
|
-
Requires-Dist: llama-deploy-appserver>=0.3.
|
|
8
|
+
Requires-Dist: llama-deploy-core[client]>=0.3.0a15,<0.4.0
|
|
9
|
+
Requires-Dist: llama-deploy-appserver>=0.3.0a15,<0.4.0
|
|
10
10
|
Requires-Dist: httpx>=0.24.0
|
|
11
11
|
Requires-Dist: rich>=13.0.0
|
|
12
12
|
Requires-Dist: questionary>=2.0.0
|
|
@@ -1,20 +1,23 @@
|
|
|
1
|
-
llama_deploy/cli/__init__.py,sha256=
|
|
2
|
-
llama_deploy/cli/app.py,sha256=
|
|
3
|
-
llama_deploy/cli/client.py,sha256=
|
|
1
|
+
llama_deploy/cli/__init__.py,sha256=df028686233c4d5a3e244bb50c1c7b84cf2399ae03abe45eb4d01e53caa1be38,476
|
|
2
|
+
llama_deploy/cli/app.py,sha256=9170e4f506c482522bd745eb1cdb700a198cfcfd7204c168c94e5ee2b6b43ffa,2199
|
|
3
|
+
llama_deploy/cli/client.py,sha256=f0f72c90cddfbc9198e154883f3b8f05fb47dbe7ec1f5755106dbb8009d2bb54,1459
|
|
4
4
|
llama_deploy/cli/commands/aliased_group.py,sha256=bc41007c97b7b93981217dbd4d4591df2b6c9412a2d9ed045b0ec5655ed285f2,1066
|
|
5
|
-
llama_deploy/cli/commands/auth.py,sha256=
|
|
6
|
-
llama_deploy/cli/commands/deployment.py,sha256=
|
|
5
|
+
llama_deploy/cli/commands/auth.py,sha256=de584d11c1acf5a4e7aee8c8f30184335053ed206d38dbc8509b2d1d0677a092,12640
|
|
6
|
+
llama_deploy/cli/commands/deployment.py,sha256=c99feb73a887063cad86e2cc555f21ebac6d47576749d79c551f26b8567af638,9879
|
|
7
|
+
llama_deploy/cli/commands/env.py,sha256=e0b96b9f4e7921b4370ad5f8bc0a2bfb19b705e73004f72c37c9bed28a208e0d,6702
|
|
7
8
|
llama_deploy/cli/commands/init.py,sha256=51b2de1e35ff34bc15c9dfec72fbad08aaf528c334df168896d36458a4e9401c,6307
|
|
8
9
|
llama_deploy/cli/commands/serve.py,sha256=4d47850397ba172944df56a934a51bedb52403cbd3f9b000b1ced90a31c75049,2721
|
|
9
|
-
llama_deploy/cli/config.py,sha256=
|
|
10
|
+
llama_deploy/cli/config/_config.py,sha256=31376c3f3ecadaf52545e4aeb4a660dde2c22d7e6c33b232ec93eebeb926e3fb,14953
|
|
11
|
+
llama_deploy/cli/config/auth_service.py,sha256=38a2de9bbcf5780675130d90e4f9116da190fd31a33e1c178a7f3847b943c229,2667
|
|
12
|
+
llama_deploy/cli/config/env_service.py,sha256=6e50ffe2e33dcff4193c87b3df8daac71542d406314a7bcaf48187715a53780e,2445
|
|
13
|
+
llama_deploy/cli/config/schema.py,sha256=086b6161b238c2037068a2b510f5d4bbda917494df764818ff9692e9735a8953,608
|
|
10
14
|
llama_deploy/cli/debug.py,sha256=e85a72d473bbe1645eb31772f7349bde703d45704166f767385895c440afc762,496
|
|
11
15
|
llama_deploy/cli/env.py,sha256=6ebc24579815b3787829c81fd5bb9f31698a06e62c0128a788559f962b33a7af,1016
|
|
12
16
|
llama_deploy/cli/interactive_prompts/session_utils.py,sha256=b996f2eddf70d6c49636c4797d246d212fce0950fe7e9a3f59cf6a1bf7ae26f5,1142
|
|
13
|
-
llama_deploy/cli/interactive_prompts/utils.py,sha256=
|
|
14
|
-
llama_deploy/cli/options.py,sha256=
|
|
17
|
+
llama_deploy/cli/interactive_prompts/utils.py,sha256=594cc2a242cc3405d66d0e26a60647496cc5fcb4ce7d0500a4cfec4888c9a0fa,516
|
|
18
|
+
llama_deploy/cli/options.py,sha256=62ee7286c3305ddb4b597783d19e854284d79bf9384800045f15b934dc245c1d,1298
|
|
15
19
|
llama_deploy/cli/platform_client.py,sha256=69de23dc79a8f5922afc9e3bac1b633a531340ebbefeb7838e3a88419faa754c,1451
|
|
16
20
|
llama_deploy/cli/py.typed,sha256=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855,0
|
|
17
|
-
llama_deploy/cli/textual/api_key_profile_form.py,sha256=cf059426d758136318c3b6f340e2741e79ab1ec613b8741ef3d2a402214ea2c4,22253
|
|
18
21
|
llama_deploy/cli/textual/deployment_form.py,sha256=1cf186b765d10a1bdd7394f22ddd7598d75dba8c9a8a7a8be74e6151da2f31dc,20941
|
|
19
22
|
llama_deploy/cli/textual/deployment_help.py,sha256=d43e9ff29db71a842cf8b491545763d581ede3132b8af518c73af85a40950046,2464
|
|
20
23
|
llama_deploy/cli/textual/deployment_monitor.py,sha256=7bcf3f0213401c2432fdb5a9d9acf468a4afe83b2d86d7f2852319768e6f2534,17231
|
|
@@ -23,7 +26,7 @@ llama_deploy/cli/textual/github_callback_server.py,sha256=dc74c510f8a98ef6ffaab0
|
|
|
23
26
|
llama_deploy/cli/textual/llama_loader.py,sha256=33cb32a46dd40bcf889c553e44f2672c410e26bd1d4b17aa6cca6d0a5d59c2c4,1468
|
|
24
27
|
llama_deploy/cli/textual/secrets_form.py,sha256=a43fbd81aad034d0d60906bfd917c107f9ace414648b0f63ac0b29eeba4050db,7061
|
|
25
28
|
llama_deploy/cli/textual/styles.tcss,sha256=b1a54dc5fb0e0aa12cbf48807e9e6a94b9926838b8058dae1336a134f02e92b0,3327
|
|
26
|
-
llamactl-0.3.
|
|
27
|
-
llamactl-0.3.
|
|
28
|
-
llamactl-0.3.
|
|
29
|
-
llamactl-0.3.
|
|
29
|
+
llamactl-0.3.0a15.dist-info/WHEEL,sha256=66530aef82d5020ef5af27ae0123c71abb9261377c5bc519376c671346b12918,79
|
|
30
|
+
llamactl-0.3.0a15.dist-info/entry_points.txt,sha256=b67e1eb64305058751a651a80f2d2268b5f7046732268421e796f64d4697f83c,52
|
|
31
|
+
llamactl-0.3.0a15.dist-info/METADATA,sha256=4d142f83a647194c1f5ed5d393d918de8dfc05286d82ea317c4480dd66ee4e8a,3177
|
|
32
|
+
llamactl-0.3.0a15.dist-info/RECORD,,
|
llama_deploy/cli/config.py
DELETED
|
@@ -1,241 +0,0 @@
|
|
|
1
|
-
"""Configuration and profile management for llamactl"""
|
|
2
|
-
|
|
3
|
-
import os
|
|
4
|
-
import sqlite3
|
|
5
|
-
from dataclasses import dataclass
|
|
6
|
-
from pathlib import Path
|
|
7
|
-
from typing import List
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
@dataclass
|
|
11
|
-
class Profile:
|
|
12
|
-
"""Profile configuration"""
|
|
13
|
-
|
|
14
|
-
name: str
|
|
15
|
-
api_url: str
|
|
16
|
-
project_id: str
|
|
17
|
-
api_key_auth_token: str | None = None
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
class ConfigManager:
|
|
21
|
-
"""Manages profiles and configuration using SQLite"""
|
|
22
|
-
|
|
23
|
-
def __init__(self):
|
|
24
|
-
self.config_dir = self._get_config_dir()
|
|
25
|
-
self.db_path = self.config_dir / "profiles.db"
|
|
26
|
-
self._ensure_config_dir()
|
|
27
|
-
self._init_database()
|
|
28
|
-
self.default_control_plane_url = "https://api.llamacloud.com"
|
|
29
|
-
|
|
30
|
-
def _get_config_dir(self) -> Path:
|
|
31
|
-
"""Get the configuration directory path based on OS.
|
|
32
|
-
|
|
33
|
-
Honors LLAMACTL_CONFIG_DIR when set. This helps tests isolate state.
|
|
34
|
-
"""
|
|
35
|
-
override = os.environ.get("LLAMACTL_CONFIG_DIR")
|
|
36
|
-
if override:
|
|
37
|
-
return Path(override).expanduser()
|
|
38
|
-
if os.name == "nt": # Windows
|
|
39
|
-
config_dir = Path(os.environ.get("APPDATA", "~")) / "llamactl"
|
|
40
|
-
else: # Unix-like (Linux, macOS)
|
|
41
|
-
config_dir = Path.home() / ".config" / "llamactl"
|
|
42
|
-
return config_dir.expanduser()
|
|
43
|
-
|
|
44
|
-
def _ensure_config_dir(self):
|
|
45
|
-
"""Create configuration directory if it doesn't exist"""
|
|
46
|
-
self.config_dir.mkdir(parents=True, exist_ok=True)
|
|
47
|
-
|
|
48
|
-
def _init_database(self):
|
|
49
|
-
"""Initialize SQLite database with required tables"""
|
|
50
|
-
with sqlite3.connect(self.db_path) as conn:
|
|
51
|
-
# Check if we need to migrate from old schema
|
|
52
|
-
cursor = conn.execute("PRAGMA table_info(profiles)")
|
|
53
|
-
columns = [row[1] for row in cursor.fetchall()]
|
|
54
|
-
|
|
55
|
-
# Migration: handle old active_project_id -> project_id and make it required
|
|
56
|
-
if "active_project_id" in columns and "project_id" not in columns:
|
|
57
|
-
# Delete any profiles that have no active_project_id since project_id is now required
|
|
58
|
-
conn.execute(
|
|
59
|
-
"DELETE FROM profiles WHERE active_project_id IS NULL OR active_project_id = ''"
|
|
60
|
-
)
|
|
61
|
-
|
|
62
|
-
# Rename active_project_id to project_id
|
|
63
|
-
# Note: SQLite doesn't allow changing column constraints easily, but we enforce
|
|
64
|
-
# the NOT NULL constraint in our application code and new table creation
|
|
65
|
-
conn.execute(
|
|
66
|
-
"ALTER TABLE profiles RENAME COLUMN active_project_id TO project_id"
|
|
67
|
-
)
|
|
68
|
-
|
|
69
|
-
# Add api_key_auth_token column if not already present
|
|
70
|
-
mig_cursor = conn.execute("PRAGMA table_info(profiles)")
|
|
71
|
-
mig_columns = [row[1] for row in mig_cursor.fetchall()]
|
|
72
|
-
if "api_key_auth_token" not in mig_columns:
|
|
73
|
-
conn.execute(
|
|
74
|
-
"ALTER TABLE profiles ADD COLUMN api_key_auth_token TEXT"
|
|
75
|
-
)
|
|
76
|
-
|
|
77
|
-
# Create tables with new schema (this will only create if they don't exist)
|
|
78
|
-
conn.execute("""
|
|
79
|
-
CREATE TABLE IF NOT EXISTS profiles (
|
|
80
|
-
name TEXT PRIMARY KEY,
|
|
81
|
-
api_url TEXT NOT NULL,
|
|
82
|
-
project_id TEXT NOT NULL,
|
|
83
|
-
api_key_auth_token TEXT
|
|
84
|
-
)
|
|
85
|
-
""")
|
|
86
|
-
|
|
87
|
-
conn.execute("""
|
|
88
|
-
CREATE TABLE IF NOT EXISTS settings (
|
|
89
|
-
key TEXT PRIMARY KEY,
|
|
90
|
-
value TEXT NOT NULL
|
|
91
|
-
)
|
|
92
|
-
""")
|
|
93
|
-
|
|
94
|
-
conn.commit()
|
|
95
|
-
|
|
96
|
-
def create_profile(
|
|
97
|
-
self,
|
|
98
|
-
name: str,
|
|
99
|
-
api_url: str,
|
|
100
|
-
project_id: str,
|
|
101
|
-
api_key_auth_token: str | None = None,
|
|
102
|
-
) -> Profile:
|
|
103
|
-
"""Create a new profile"""
|
|
104
|
-
if not project_id.strip():
|
|
105
|
-
raise ValueError("Project ID is required")
|
|
106
|
-
profile = Profile(
|
|
107
|
-
name=name,
|
|
108
|
-
api_url=api_url,
|
|
109
|
-
project_id=project_id,
|
|
110
|
-
api_key_auth_token=api_key_auth_token,
|
|
111
|
-
)
|
|
112
|
-
|
|
113
|
-
with sqlite3.connect(self.db_path) as conn:
|
|
114
|
-
try:
|
|
115
|
-
conn.execute(
|
|
116
|
-
"INSERT INTO profiles (name, api_url, project_id, api_key_auth_token) VALUES (?, ?, ?, ?)",
|
|
117
|
-
(
|
|
118
|
-
profile.name,
|
|
119
|
-
profile.api_url,
|
|
120
|
-
profile.project_id,
|
|
121
|
-
profile.api_key_auth_token,
|
|
122
|
-
),
|
|
123
|
-
)
|
|
124
|
-
conn.commit()
|
|
125
|
-
except sqlite3.IntegrityError:
|
|
126
|
-
raise ValueError(f"Profile '{name}' already exists")
|
|
127
|
-
|
|
128
|
-
return profile
|
|
129
|
-
|
|
130
|
-
def get_profile(self, name: str) -> Profile | None:
|
|
131
|
-
"""Get a profile by name"""
|
|
132
|
-
with sqlite3.connect(self.db_path) as conn:
|
|
133
|
-
cursor = conn.execute(
|
|
134
|
-
"SELECT name, api_url, project_id, api_key_auth_token FROM profiles WHERE name = ?",
|
|
135
|
-
(name,),
|
|
136
|
-
)
|
|
137
|
-
row = cursor.fetchone()
|
|
138
|
-
if row:
|
|
139
|
-
return Profile(
|
|
140
|
-
name=row[0],
|
|
141
|
-
api_url=row[1],
|
|
142
|
-
project_id=row[2],
|
|
143
|
-
api_key_auth_token=row[3],
|
|
144
|
-
)
|
|
145
|
-
return None
|
|
146
|
-
|
|
147
|
-
def list_profiles(self) -> List[Profile]:
|
|
148
|
-
"""List all profiles"""
|
|
149
|
-
profiles = []
|
|
150
|
-
with sqlite3.connect(self.db_path) as conn:
|
|
151
|
-
cursor = conn.execute(
|
|
152
|
-
"SELECT name, api_url, project_id, api_key_auth_token FROM profiles ORDER BY name"
|
|
153
|
-
)
|
|
154
|
-
for row in cursor.fetchall():
|
|
155
|
-
profiles.append(
|
|
156
|
-
Profile(
|
|
157
|
-
name=row[0],
|
|
158
|
-
api_url=row[1],
|
|
159
|
-
project_id=row[2],
|
|
160
|
-
api_key_auth_token=row[3],
|
|
161
|
-
)
|
|
162
|
-
)
|
|
163
|
-
return profiles
|
|
164
|
-
|
|
165
|
-
def delete_profile(self, name: str) -> bool:
|
|
166
|
-
"""Delete a profile by name. Returns True if deleted, False if not found."""
|
|
167
|
-
with sqlite3.connect(self.db_path) as conn:
|
|
168
|
-
cursor = conn.execute("DELETE FROM profiles WHERE name = ?", (name,))
|
|
169
|
-
conn.commit()
|
|
170
|
-
|
|
171
|
-
# If this was the active profile, clear it
|
|
172
|
-
if self.get_current_profile_name() == name:
|
|
173
|
-
self.set_current_profile(None)
|
|
174
|
-
|
|
175
|
-
return cursor.rowcount > 0
|
|
176
|
-
|
|
177
|
-
def set_current_profile(self, name: str | None):
|
|
178
|
-
"""Set the current active profile"""
|
|
179
|
-
with sqlite3.connect(self.db_path) as conn:
|
|
180
|
-
if name is None:
|
|
181
|
-
conn.execute("DELETE FROM settings WHERE key = 'current_profile'")
|
|
182
|
-
else:
|
|
183
|
-
conn.execute(
|
|
184
|
-
"INSERT OR REPLACE INTO settings (key, value) VALUES ('current_profile', ?)",
|
|
185
|
-
(name,),
|
|
186
|
-
)
|
|
187
|
-
conn.commit()
|
|
188
|
-
|
|
189
|
-
def get_current_profile_name(self) -> str | None:
|
|
190
|
-
"""Get the name of the current active profile"""
|
|
191
|
-
with sqlite3.connect(self.db_path) as conn:
|
|
192
|
-
cursor = conn.execute(
|
|
193
|
-
"SELECT value FROM settings WHERE key = 'current_profile'"
|
|
194
|
-
)
|
|
195
|
-
row = cursor.fetchone()
|
|
196
|
-
return row[0] if row else None
|
|
197
|
-
|
|
198
|
-
def get_current_profile(self) -> Profile | None:
|
|
199
|
-
"""Get the current active profile"""
|
|
200
|
-
current_name = self.get_current_profile_name()
|
|
201
|
-
if current_name:
|
|
202
|
-
return self.get_profile(current_name)
|
|
203
|
-
profiles = self.list_profiles()
|
|
204
|
-
if len(profiles) == 1:
|
|
205
|
-
return profiles[0]
|
|
206
|
-
return None
|
|
207
|
-
|
|
208
|
-
def set_project(self, profile_name: str, project_id: str) -> bool:
|
|
209
|
-
"""Set the project for a profile. Returns True if profile exists."""
|
|
210
|
-
with sqlite3.connect(self.db_path) as conn:
|
|
211
|
-
cursor = conn.execute(
|
|
212
|
-
"UPDATE profiles SET project_id = ? WHERE name = ?",
|
|
213
|
-
(project_id, profile_name),
|
|
214
|
-
)
|
|
215
|
-
conn.commit()
|
|
216
|
-
return cursor.rowcount > 0
|
|
217
|
-
|
|
218
|
-
def set_default_control_plane_url(self, url: str) -> None:
|
|
219
|
-
"""Set the default control plane URL for the current session"""
|
|
220
|
-
self.default_control_plane_url = url
|
|
221
|
-
|
|
222
|
-
def get_project(self, profile_name: str) -> str | None:
|
|
223
|
-
"""Get the project for a profile"""
|
|
224
|
-
profile = self.get_profile(profile_name)
|
|
225
|
-
return profile.project_id if profile else None
|
|
226
|
-
|
|
227
|
-
def set_api_key_auth_token(
|
|
228
|
-
self, profile_name: str, api_key_auth_token: str | None
|
|
229
|
-
) -> bool:
|
|
230
|
-
"""Set the API key auth token for a profile. Returns True if profile exists."""
|
|
231
|
-
with sqlite3.connect(self.db_path) as conn:
|
|
232
|
-
cursor = conn.execute(
|
|
233
|
-
"UPDATE profiles SET api_key_auth_token = ? WHERE name = ?",
|
|
234
|
-
(api_key_auth_token, profile_name),
|
|
235
|
-
)
|
|
236
|
-
conn.commit()
|
|
237
|
-
return cursor.rowcount > 0
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
# Global config manager instance
|
|
241
|
-
config_manager = ConfigManager()
|