videosdkagent-cli 0.0.1__py2.py3-none-any.whl → 0.0.4__py2.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.
@@ -13,25 +13,89 @@ CACHE_DIR = Path.home() / ".videosdk"
13
13
  CACHE_DIR.mkdir(exist_ok=True)
14
14
  CACHE_FILE = CACHE_DIR / "template_cache.json"
15
15
  TIMESTAMP_FILE = CACHE_DIR / "template_cache_timestamp.txt"
16
- CDN_BASE_URL = os.environ.get("CDN_URL","https://probargaining-lynda-contradictiously.ngrok-free.dev/cdn")
17
- API_BASE_URL = os.environ.get("API_BASE_URL","http://localhost:8000")
16
+ CDN_BASE_URL = "https://cdn.videosdk.live"
17
+ API_BASE_URL = "https://api.videosdk.live"
18
+ DASHBOARD_URL = "https://stage.app.videosdk.live"
18
19
 
19
20
 
20
21
  PROVIDER_IMPORTS = {
21
- "DeepgramSTT": ("videosdk.plugins.deepgram", "DeepgramSTT", "STT", "deepgram", "DEEPGRAM_API_KEY"),
22
- "GoogleSTT": ("videosdk.plugins.google", "GoogleSTT", "STT", "google-cloud-speech","GOOGLE_API_KEY"),
23
- "OpenAISTT": ("videosdk.plugins.openai", "OpenAISTT", "STT", "openai", "OPENAI_API_KEY"),
24
-
25
- "OpenAILLM": ("videosdk.plugins.openai", "OpenAILLM", "LLM", "openai", "OPENAI_API_KEY"),
26
- "GoogleLLM": ("videosdk.plugins.google", "GoogleLLM", "LLM", "google-cloud-language", "GOOGLE_API_KEY"),
27
-
28
- "ElevenLabsTTS": ("videosdk.plugins.elevenlabs", "ElevenLabsTTS", "TTS", "elevenlabs", "ELEVENLABS_API_KEY"),
29
- "OpenAITTS": ("videosdk.plugins.openai", "OpenAITTS", "TTS", "openai", "OPENAI_API_KEY"),
30
- "GoogleTTS": ("videosdk.plugins.google", "GoogleTTS", "TTS", "google-cloud-texttospeech", "GOOGLE_API_KEY"),
31
- "DeepgramTTS": ("videosdk.plugins.deepgram", "DeepgramTTS", "TTS", "deepgram", "DEEPGRAM_API_KEY"),
32
-
33
- "OpenAIRealtime": ("videosdk.plugins.openai", "OpenAIRealtime", "Realtime", "openai", "OPENAI_API_KEY"),
34
- "GeminiRealtime": ("videosdk.plugins.google", "GeminiRealtime", "Realtime", "google-cloud-realtime", "GOOGLE_API_KEY"),
22
+ "DeepgramSTT": (
23
+ "videosdk.plugins.deepgram",
24
+ "DeepgramSTT",
25
+ "STT",
26
+ "deepgram",
27
+ "DEEPGRAM_API_KEY",
28
+ ),
29
+ "GoogleSTT": (
30
+ "videosdk.plugins.google",
31
+ "GoogleSTT",
32
+ "STT",
33
+ "google-cloud-speech",
34
+ "GOOGLE_API_KEY",
35
+ ),
36
+ "OpenAISTT": (
37
+ "videosdk.plugins.openai",
38
+ "OpenAISTT",
39
+ "STT",
40
+ "openai",
41
+ "OPENAI_API_KEY",
42
+ ),
43
+ "OpenAILLM": (
44
+ "videosdk.plugins.openai",
45
+ "OpenAILLM",
46
+ "LLM",
47
+ "openai",
48
+ "OPENAI_API_KEY",
49
+ ),
50
+ "GoogleLLM": (
51
+ "videosdk.plugins.google",
52
+ "GoogleLLM",
53
+ "LLM",
54
+ "google-cloud-language",
55
+ "GOOGLE_API_KEY",
56
+ ),
57
+ "ElevenLabsTTS": (
58
+ "videosdk.plugins.elevenlabs",
59
+ "ElevenLabsTTS",
60
+ "TTS",
61
+ "elevenlabs",
62
+ "ELEVENLABS_API_KEY",
63
+ ),
64
+ "OpenAITTS": (
65
+ "videosdk.plugins.openai",
66
+ "OpenAITTS",
67
+ "TTS",
68
+ "openai",
69
+ "OPENAI_API_KEY",
70
+ ),
71
+ "GoogleTTS": (
72
+ "videosdk.plugins.google",
73
+ "GoogleTTS",
74
+ "TTS",
75
+ "google-cloud-texttospeech",
76
+ "GOOGLE_API_KEY",
77
+ ),
78
+ "DeepgramTTS": (
79
+ "videosdk.plugins.deepgram",
80
+ "DeepgramTTS",
81
+ "TTS",
82
+ "deepgram",
83
+ "DEEPGRAM_API_KEY",
84
+ ),
85
+ "OpenAIRealtime": (
86
+ "videosdk.plugins.openai",
87
+ "OpenAIRealtime",
88
+ "Realtime",
89
+ "openai",
90
+ "OPENAI_API_KEY",
91
+ ),
92
+ "GeminiRealtime": (
93
+ "videosdk.plugins.google",
94
+ "GeminiRealtime",
95
+ "Realtime",
96
+ "google-cloud-realtime",
97
+ "GOOGLE_API_KEY",
98
+ ),
35
99
  }
36
100
 
37
101
  STATIC_REQUIREMENTS = [
@@ -78,13 +142,16 @@ async def fetch_template_cache(force_refresh=False):
78
142
  return
79
143
  data = await resp.json()
80
144
  templateResponse = data.get("templates", {})
81
- CACHE_FILE.write_text(json.dumps(templateResponse, indent=4), encoding="utf-8")
145
+ CACHE_FILE.write_text(
146
+ json.dumps(templateResponse, indent=4), encoding="utf-8"
147
+ )
82
148
  TIMESTAMP_FILE.write_text(datetime.now().isoformat(), encoding="utf-8")
83
149
  console.print("[green]Template metadata cache updated.[/green]")
84
150
 
85
151
  except Exception as e:
86
152
  console.print(f"[red]Error fetching template metadata: {e}[/red]")
87
153
 
154
+
88
155
  async def select_provider_arrow(prompt_message: str, options: list[str]) -> str:
89
156
  """Arrow-based selection for providers."""
90
157
  choice = await inquirer.select(
@@ -121,7 +188,9 @@ def generate_realtime_providers(providers: dict):
121
188
  return imports, provider_modules
122
189
 
123
190
 
124
- def write_template_file(app_dir: Path, filename: str, imports_str: str, template_code: str):
191
+ def write_template_file(
192
+ app_dir: Path, filename: str, imports_str: str, template_code: str
193
+ ):
125
194
  file_path = app_dir / filename
126
195
  file_path.write_text(f"{imports_str}\n\n{template_code}", encoding="utf-8")
127
196
 
@@ -134,12 +203,12 @@ def write_config(
134
203
  providers: dict,
135
204
  env_path: str = None,
136
205
  auto_configured: bool = True,
137
- requirements_installed: bool = False
206
+ requirements_installed: bool = False,
138
207
  ):
139
208
  """Write project config.json with both template ID and file name."""
140
209
  config_data = {
141
- "template_id": template_id,
142
- "template_file": file_name,
210
+ "template_id": template_id,
211
+ "template_file": file_name,
143
212
  "description": description,
144
213
  "providers": providers,
145
214
  "auto_configured": auto_configured,
@@ -149,16 +218,20 @@ def write_config(
149
218
  config_data["venv_path"] = env_path
150
219
 
151
220
  (app_dir / "config.json").write_text(
152
- json.dumps(config_data, indent=4),
153
- encoding="utf-8"
221
+ json.dumps(config_data, indent=4), encoding="utf-8"
154
222
  )
155
223
 
224
+
156
225
  def write_requirements(app_dir: Path, requirements: list):
157
- (app_dir / "requirements.txt").write_text("\n".join(sorted(set(requirements))), encoding="utf-8")
226
+ (app_dir / "requirements.txt").write_text(
227
+ "\n".join(sorted(set(requirements))), encoding="utf-8"
228
+ )
229
+
158
230
 
159
231
  def write_env_file(app_dir: Path, selected_providers: dict, api_keys: dict):
160
232
  """Create a .env file with API keys and auth token."""
161
233
  from collections import OrderedDict
234
+
162
235
  auth_token = get_config_value("VIDEOSDK_AUTH_TOKEN") or ""
163
236
 
164
237
  env_vars = OrderedDict()
@@ -166,14 +239,14 @@ def write_env_file(app_dir: Path, selected_providers: dict, api_keys: dict):
166
239
 
167
240
  for provider in selected_providers.values():
168
241
  env_var_name = PROVIDER_IMPORTS.get(
169
- provider,
170
- (None, None, None, None, f"{provider.upper()}_API_KEY")
242
+ provider, (None, None, None, None, f"{provider.upper()}_API_KEY")
171
243
  )[-1]
172
244
  env_vars[env_var_name] = api_keys.get(provider, "")
173
245
 
174
246
  env_content = "\n".join(f"{key}={value}" for key, value in env_vars.items())
175
247
  (app_dir / ".env").write_text(env_content, encoding="utf-8")
176
248
 
249
+
177
250
  async def prompt_provider_api_keys(providers: dict, api_keys: dict = None) -> dict:
178
251
  """Ask user for API keys for selected providers. Skip if already in api_keys."""
179
252
  if api_keys is None:
@@ -185,18 +258,19 @@ async def prompt_provider_api_keys(providers: dict, api_keys: dict = None) -> di
185
258
 
186
259
  key = await inquirer.text(
187
260
  message=f"Enter API key for {provider}:",
188
- validate=lambda val: len(val.strip()) > 0 or "API key cannot be empty"
261
+ validate=lambda val: len(val.strip()) > 0 or "API key cannot be empty",
189
262
  ).execute_async()
190
263
 
191
264
  api_keys[provider] = key
192
265
 
193
266
  return api_keys
194
267
 
268
+
195
269
  async def prompt_existing_env():
196
270
  """Ask user whether to use an existing Python environment."""
197
271
  use_existing = await select_provider_arrow(
198
272
  "Do you want to use an existing Python environment (venv/conda)?",
199
- ["No, create new venv", "Yes, I have an environment"]
273
+ ["No, create new venv", "Yes, I have an environment"],
200
274
  )
201
275
  if use_existing == "Yes, I have an environment":
202
276
  path = input("Enter the path to your environment: ").strip()
@@ -26,19 +26,27 @@ async def run_with_progress(
26
26
  try:
27
27
  while True:
28
28
  elapsed = asyncio.get_event_loop().time() - start_time
29
- percent = min((elapsed / duration) * 95, 95)
30
29
 
30
+ # Check if task is done - complete immediately if so
31
+ if api_task.done():
32
+ result = await api_task
33
+ progress.update(task_id, completed=100)
34
+ return result
35
+
36
+ # Update progress based on elapsed time (up to 95% until task completes)
37
+ # Duration is the maximum expected time
38
+ percent = min((elapsed / duration) * 95, 95)
31
39
  progress.update(task_id, completed=percent)
32
40
 
33
- if api_task.done() and elapsed >= duration:
34
- break
41
+ # If we've exceeded the maximum duration, break and wait for task
42
+ if elapsed >= duration:
43
+ # Task might still be running, wait for it
44
+ result = await api_task
45
+ progress.update(task_id, completed=100)
46
+ return result
35
47
 
36
48
  await asyncio.sleep(0.1)
37
49
 
38
- result = await api_task
39
- progress.update(task_id, completed=100)
40
- return result
41
-
42
50
  except asyncio.CancelledError:
43
51
  raise
44
52
 
@@ -0,0 +1,144 @@
1
+ """
2
+ VideoSDK CLI Theme and Styling
3
+
4
+ Brand Colors:
5
+ - Primary: #D1BCFE (light purple/lavender)
6
+ - Secondary: #37265E (dark purple)
7
+ """
8
+
9
+ from rich.console import Console
10
+ from rich.theme import Theme
11
+ from rich.panel import Panel
12
+ from rich.text import Text
13
+ from rich.style import Style
14
+
15
+ # VideoSDK Brand Colors
16
+ PRIMARY = "#D1BCFE"
17
+ SECONDARY = "#37265E"
18
+ SUCCESS = "#50FA7B"
19
+ ERROR = "#FF5555"
20
+ WARNING = "#FFB86C"
21
+ INFO = "#8BE9FD"
22
+ MUTED = "#6272A4"
23
+
24
+ # Custom theme for Rich
25
+ VIDEOSDK_THEME = Theme(
26
+ {
27
+ "primary": PRIMARY,
28
+ "secondary": SECONDARY,
29
+ "success": SUCCESS,
30
+ "error": ERROR,
31
+ "warning": WARNING,
32
+ "info": INFO,
33
+ "muted": MUTED,
34
+ "header": f"bold {PRIMARY}",
35
+ "highlight": f"bold {PRIMARY}",
36
+ "command": f"bold {INFO}",
37
+ "path": f"italic {MUTED}",
38
+ }
39
+ )
40
+
41
+ # Create themed console
42
+ console = Console(theme=VIDEOSDK_THEME)
43
+
44
+
45
+ # ASCII Art Banner - Simple and clean
46
+ BANNER = f"""
47
+ [{PRIMARY}]╭────────────────────────────────────────────────────────────────────╮[/{PRIMARY}]
48
+ [{PRIMARY}]│[/{PRIMARY}] [{PRIMARY}]│[/{PRIMARY}]
49
+ [{PRIMARY}]│[/{PRIMARY}] [bold {PRIMARY}]██╗ ██╗██╗██████╗ ███████╗ ██████╗ ███████╗██████╗ ██╗ ██╗[/bold {PRIMARY}] [{PRIMARY}]│[/{PRIMARY}]
50
+ [{PRIMARY}]│[/{PRIMARY}] [bold {PRIMARY}]██║ ██║██║██╔══██╗██╔════╝██╔═══██╗██╔════╝██╔══██╗██║ ██╔╝[/bold {PRIMARY}] [{PRIMARY}]│[/{PRIMARY}]
51
+ [{PRIMARY}]│[/{PRIMARY}] [bold {PRIMARY}]██║ ██║██║██║ ██║█████╗ ██║ ██║███████╗██║ ██║█████╔╝[/bold {PRIMARY}] [{PRIMARY}]│[/{PRIMARY}]
52
+ [{PRIMARY}]│[/{PRIMARY}] [bold {PRIMARY}]╚██╗ ██╔╝██║██║ ██║██╔══╝ ██║ ██║╚════██║██║ ██║██╔═██╗[/bold {PRIMARY}] [{PRIMARY}]│[/{PRIMARY}]
53
+ [{PRIMARY}]│[/{PRIMARY}] [bold {PRIMARY}] ╚████╔╝ ██║██████╔╝███████╗╚██████╔╝███████║██████╔╝██║ ██╗[/bold {PRIMARY}] [{PRIMARY}]│[/{PRIMARY}]
54
+ [{PRIMARY}]│[/{PRIMARY}] [bold {PRIMARY}] ╚═══╝ ╚═╝╚═════╝ ╚══════╝ ╚═════╝ ╚══════╝╚═════╝ ╚═╝ ╚═╝[/bold {PRIMARY}] [{PRIMARY}]│[/{PRIMARY}]
55
+ [{PRIMARY}]│[/{PRIMARY}] [{PRIMARY}]│[/{PRIMARY}]
56
+ [{PRIMARY}]│[/{PRIMARY}] [{MUTED}]Your Complete Platform for Real-Time Communication[/{MUTED}] [{PRIMARY}]│[/{PRIMARY}]
57
+ [{PRIMARY}]│[/{PRIMARY}] [{PRIMARY}]│[/{PRIMARY}]
58
+ [{PRIMARY}]╰────────────────────────────────────────────────────────────────────╯[/{PRIMARY}]
59
+ """
60
+
61
+ MINI_BANNER = """[bold #D1BCFE]◆ VideoSDK CLI[/bold #D1BCFE] [muted]v0.2.0[/muted]"""
62
+
63
+
64
+ def print_banner(mini: bool = True):
65
+ """Print the VideoSDK banner."""
66
+ if mini:
67
+ console.print(MINI_BANNER)
68
+ console.print()
69
+ else:
70
+ console.print(BANNER)
71
+
72
+
73
+ def print_header(text: str):
74
+ """Print a styled header."""
75
+ console.print()
76
+ console.print(f"[bold {PRIMARY}]◆[/bold {PRIMARY}] [bold white]{text}[/bold white]")
77
+ console.print()
78
+
79
+
80
+ def print_success(text: str):
81
+ """Print a success message."""
82
+ console.print(f"[{SUCCESS}]✓[/{SUCCESS}] {text}")
83
+
84
+
85
+ def print_error(text: str):
86
+ """Print an error message."""
87
+ console.print(f"[{ERROR}]✗[/{ERROR}] [bold {ERROR}]{text}[/bold {ERROR}]")
88
+
89
+
90
+ def print_warning(text: str):
91
+ """Print a warning message."""
92
+ console.print(f"[{WARNING}]![/{WARNING}] {text}")
93
+
94
+
95
+ def print_info(text: str):
96
+ """Print an info message."""
97
+ console.print(f"[{INFO}]ℹ[/{INFO}] {text}")
98
+
99
+
100
+ def print_step(text: str, step: int = None, total: int = None):
101
+ """Print a step in a process."""
102
+ if step and total:
103
+ console.print(
104
+ f"[{PRIMARY}]▸[/{PRIMARY}] [{MUTED}][{step}/{total}][/{MUTED}] {text}"
105
+ )
106
+ else:
107
+ console.print(f"[{PRIMARY}]▸[/{PRIMARY}] {text}")
108
+
109
+
110
+ def print_key_value(key: str, value: str):
111
+ """Print a key-value pair."""
112
+ console.print(f" [{MUTED}]{key}:[/{MUTED}] [{PRIMARY}]{value}[/{PRIMARY}]")
113
+
114
+
115
+ def print_divider():
116
+ """Print a divider line."""
117
+ console.print(f"[{MUTED}]{'─' * 60}[/{MUTED}]")
118
+
119
+
120
+ def create_styled_table(title: str = None):
121
+ """Create a styled table with VideoSDK theme."""
122
+ from rich.table import Table
123
+
124
+ table = Table(
125
+ title=f"[bold {PRIMARY}]{title}[/bold {PRIMARY}]" if title else None,
126
+ show_header=True,
127
+ header_style=f"bold {PRIMARY}",
128
+ border_style=MUTED,
129
+ title_style=f"bold {PRIMARY}",
130
+ show_lines=True,
131
+ row_styles=[f"on {SECONDARY}20", ""], # Alternating row colors
132
+ )
133
+ return table
134
+
135
+
136
+ def styled_panel(content: str, title: str = None, subtitle: str = None):
137
+ """Create a styled panel."""
138
+ return Panel(
139
+ content,
140
+ title=f"[bold {PRIMARY}]{title}[/bold {PRIMARY}]" if title else None,
141
+ subtitle=f"[{MUTED}]{subtitle}[/{MUTED}]" if subtitle else None,
142
+ border_style=PRIMARY,
143
+ padding=(1, 2),
144
+ )
@@ -1,6 +1,6 @@
1
1
  import yaml
2
2
  from pathlib import Path
3
- from dataclasses import dataclass, asdict
3
+ from dataclasses import dataclass, asdict, field
4
4
  from typing import Optional
5
5
 
6
6
 
@@ -9,54 +9,203 @@ class AgentConfig:
9
9
  templateId: Optional[str] = None
10
10
  id: Optional[str] = None
11
11
  name: Optional[str] = None
12
- image: Optional[str] = None
12
+ description: Optional[str] = None
13
+ image: Optional[str] = None # Legacy v1.0 support
13
14
 
14
15
  @classmethod
15
16
  def from_dict(cls, data: dict):
16
17
  # Filter keys to only those present in the dataclass
17
- return cls(**{
18
- k: v for k, v in data.items()
19
- if k in cls.__annotations__
20
- })
18
+ return cls(**{k: v for k, v in data.items() if k in cls.__annotations__})
19
+
20
+
21
+ @dataclass
22
+ class BuildLogsConfig:
23
+ id: Optional[str] = None # buildId from presigned URL response
24
+ enabled: Optional[bool] = None
25
+
26
+ @classmethod
27
+ def from_dict(cls, data: dict):
28
+ if data is None:
29
+ return cls()
30
+ return cls(id=data.get("id"), enabled=data.get("enabled"))
31
+
32
+
33
+ @dataclass
34
+ class BuildConfig:
35
+ file: Optional[str] = None
36
+ image: Optional[str] = None
37
+ logs: Optional[BuildLogsConfig] = None
38
+
39
+ @classmethod
40
+ def from_dict(cls, data: dict):
41
+ if data is None:
42
+ return cls()
43
+ logs_data = data.get("logs")
44
+ return cls(
45
+ file=data.get("file"),
46
+ image=data.get("image"),
47
+ logs=BuildLogsConfig.from_dict(logs_data) if logs_data else None,
48
+ )
49
+
50
+
51
+ @dataclass
52
+ class ReplicasConfig:
53
+ min: Optional[int] = None
54
+ max: Optional[int] = None
55
+
56
+ @classmethod
57
+ def from_dict(cls, data: dict):
58
+ if data is None:
59
+ return cls()
60
+ return cls(**{k: v for k, v in data.items() if k in cls.__annotations__})
61
+
62
+
63
+ @dataclass
64
+ class DeployConfig:
65
+ id: Optional[str] = None # deploymentId from API
66
+ replicas: Optional[ReplicasConfig] = None
67
+ profile: Optional[str] = None
68
+ region: Optional[str] = None
69
+
70
+ @classmethod
71
+ def from_dict(cls, data: dict):
72
+ if data is None:
73
+ return cls()
74
+ replicas_data = data.get("replicas")
75
+ return cls(
76
+ id=data.get("id"),
77
+ replicas=ReplicasConfig.from_dict(replicas_data) if replicas_data else None,
78
+ profile=data.get("profile"),
79
+ region=data.get("region"),
80
+ )
81
+
82
+
83
+ @dataclass
84
+ class SecretsConfig:
85
+ env: Optional[str] = None
86
+ image_pull: Optional[str] = None # maps from 'image-pull' in YAML
87
+
88
+ @classmethod
89
+ def from_dict(cls, data: dict):
90
+ if data is None:
91
+ return cls()
92
+ return cls(env=data.get("env"), image_pull=data.get("image-pull"))
93
+
21
94
 
22
95
  @dataclass
23
96
  class VideosdkYamlConfig:
24
- agent: AgentConfig
97
+ version: Optional[str] = None
98
+ agent: Optional[AgentConfig] = None
99
+ build: Optional[BuildConfig] = None
100
+ deploy: Optional[DeployConfig] = None
101
+ secrets: Optional[SecretsConfig] = None
25
102
 
26
103
  @classmethod
27
104
  def from_dict(cls, data: dict):
28
105
  agent_data = data.get("agent", {})
106
+ build_data = data.get("build")
107
+ deploy_data = data.get("deploy")
108
+ secrets_data = data.get("secrets")
109
+
29
110
  # If agent_data is None (key exists but empty), default to empty dict
30
111
  if agent_data is None:
31
112
  agent_data = {}
32
- return cls(agent=AgentConfig.from_dict(agent_data))
33
113
 
34
- def update_agent_config(app_dir: Path, agent_config: AgentConfig):
114
+ return cls(
115
+ version=data.get("version"),
116
+ agent=AgentConfig.from_dict(agent_data),
117
+ build=BuildConfig.from_dict(build_data) if build_data else None,
118
+ deploy=DeployConfig.from_dict(deploy_data) if deploy_data else None,
119
+ secrets=SecretsConfig.from_dict(secrets_data) if secrets_data else None,
120
+ )
121
+
122
+
123
+ def update_agent_config(
124
+ app_dir: Path,
125
+ agent_config: AgentConfig = None,
126
+ deploy_config: DeployConfig = None,
127
+ build_config: BuildConfig = None,
128
+ secrets_config: SecretsConfig = None,
129
+ ):
130
+ """
131
+ Update the videosdk.yaml with agent, deploy, build, and/or secrets configuration.
132
+
133
+ Args:
134
+ app_dir: Directory where videosdk.yaml is located
135
+ agent_config: AgentConfig dataclass with agent fields to update
136
+ deploy_config: DeployConfig dataclass with deploy fields to update
137
+ build_config: BuildConfig dataclass with build fields to update
138
+ secrets_config: SecretsConfig dataclass with secrets fields to update
139
+ """
35
140
  videosdk_dir = app_dir
36
141
  videosdk_dir.mkdir(parents=True, exist_ok=True)
37
-
142
+
38
143
  config_file = videosdk_dir / "videosdk.yaml"
39
-
144
+
40
145
  config_data = {}
41
-
146
+
42
147
  if config_file.exists():
43
148
  try:
44
149
  with open(config_file, "r") as f:
45
150
  config_data = yaml.safe_load(f) or {}
46
151
  except yaml.YAMLError:
47
152
  pass
48
-
153
+
49
154
  # Load into dataclass structure
50
155
  yaml_config = VideosdkYamlConfig.from_dict(config_data)
51
-
52
- # Update agent fields dynamically from the passed AgentConfig object
53
- # We iterate over fields in the passed config and update if they are not None
54
-
55
- updates = asdict(agent_config)
56
- for key, value in updates.items():
57
- if value is not None and hasattr(yaml_config.agent, key):
58
- setattr(yaml_config.agent, key, value)
59
-
156
+
157
+ # Update agent fields if provided
158
+ if agent_config:
159
+ if yaml_config.agent is None:
160
+ yaml_config.agent = AgentConfig()
161
+ updates = asdict(agent_config)
162
+ for key, value in updates.items():
163
+ if value is not None and hasattr(yaml_config.agent, key):
164
+ setattr(yaml_config.agent, key, value)
165
+
166
+ # Update deploy fields if provided
167
+ if deploy_config:
168
+ if yaml_config.deploy is None:
169
+ yaml_config.deploy = DeployConfig()
170
+ updates = asdict(deploy_config)
171
+ for key, value in updates.items():
172
+ if value is not None and hasattr(yaml_config.deploy, key):
173
+ # Handle nested ReplicasConfig
174
+ if key == "replicas" and isinstance(value, dict):
175
+ if yaml_config.deploy.replicas is None:
176
+ yaml_config.deploy.replicas = ReplicasConfig()
177
+ for rkey, rvalue in value.items():
178
+ if rvalue is not None:
179
+ setattr(yaml_config.deploy.replicas, rkey, rvalue)
180
+ else:
181
+ setattr(yaml_config.deploy, key, value)
182
+
183
+ # Update build fields if provided
184
+ if build_config:
185
+ if yaml_config.build is None:
186
+ yaml_config.build = BuildConfig()
187
+ updates = asdict(build_config)
188
+ for key, value in updates.items():
189
+ if value is not None and hasattr(yaml_config.build, key):
190
+ # Handle nested BuildLogsConfig
191
+ if key == "logs" and isinstance(value, dict):
192
+ if yaml_config.build.logs is None:
193
+ yaml_config.build.logs = BuildLogsConfig()
194
+ for lkey, lvalue in value.items():
195
+ if lvalue is not None:
196
+ setattr(yaml_config.build.logs, lkey, lvalue)
197
+ else:
198
+ setattr(yaml_config.build, key, value)
199
+
200
+ # Update secrets fields if provided
201
+ if secrets_config:
202
+ if yaml_config.secrets is None:
203
+ yaml_config.secrets = SecretsConfig()
204
+ updates = asdict(secrets_config)
205
+ for key, value in updates.items():
206
+ if value is not None and hasattr(yaml_config.secrets, key):
207
+ setattr(yaml_config.secrets, key, value)
208
+
60
209
  # Convert back to dict and clean None values
61
210
  raw_output = asdict(yaml_config)
62
211
 
@@ -66,10 +215,11 @@ def update_agent_config(app_dir: Path, agent_config: AgentConfig):
66
215
  with open(config_file, "w") as f:
67
216
  yaml.dump(output_data, f, default_flow_style=False, sort_keys=False)
68
217
 
218
+
69
219
  # Helper to remove None values recursively
70
220
  def clean_nones(value):
71
221
  if isinstance(value, dict):
72
222
  return {k: clean_nones(v) for k, v in value.items() if v is not None}
73
223
  if isinstance(value, list):
74
224
  return [clean_nones(v) for v in value if v is not None]
75
- return value
225
+ return value
@@ -1,8 +1,8 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: videosdkagent-cli
3
- Version: 0.0.1
3
+ Version: 0.0.4
4
4
  Summary: VideoSDK CLI tool
5
- Author-email: Krishna <krishna@videosdk.live>
5
+ Author-email: VIDEOSDK <sdk@videosdk.live>
6
6
  Requires-Dist: aiohttp>=3.9.0
7
7
  Requires-Dist: click>=8.1.0
8
8
  Requires-Dist: docker-image-py>=0.1.13
@@ -0,0 +1,28 @@
1
+ videosdk_cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ videosdk_cli/auth.py,sha256=A3tMffpoNoYFHkPFbl-84pjHXqLFLMMJ5oH0S_ruWTc,3495
3
+ videosdk_cli/build.py,sha256=pUjKXhK-GCOvIDPBCYlyPM5SkBzIyW7Mditog8VmXC8,46545
4
+ videosdk_cli/main.py,sha256=MS7os7BuOGOhFMcENx6LL5BXP16bGgCu5_FvdAIj3Hk,3217
5
+ videosdk_cli/projects.py,sha256=e2xHqxTFGpGuGU4eVVfrMGnXX8PULJbREy7CTDHHr9s,4359
6
+ videosdk_cli/run_agent.py,sha256=3Rlki35Q3cTnJnYCbFiCE9EoHfLnQCq79skH-JCA-oA,3205
7
+ videosdk_cli/templates.py,sha256=CLGVGSR_HafIUIyqd1A7jNzMSXcszW5mxyb45eRkcPE,7627
8
+ videosdk_cli/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
+ videosdk_cli/utils/analytics.py,sha256=4PLCURNI5xiULC3aOjdPRfmAP0JvwYDhwRSb_w84ynI,1975
10
+ videosdk_cli/utils/api_client.py,sha256=ljBre_vaJtmrpwNoovJf6kcPSjLgNcLK_L4U2CNUDKI,3726
11
+ videosdk_cli/utils/auth_api_client.py,sha256=5ins-e3B1QQlpkpQBWoBGGseVexchvXuO0BWSWzbSQA,1361
12
+ videosdk_cli/utils/config_manager.py,sha256=yygGOp-CaU1KINClLZC7nFKI8Ob7vzuL-7cBP20dNEU,1893
13
+ videosdk_cli/utils/exceptions.py,sha256=1Tjxxmw8Ts8DOQEZqnJBmNAFgJ0CpcHkAQ95RLQs4iw,133
14
+ videosdk_cli/utils/project_config.py,sha256=X-jro6nUDkZ1ZTdJ324JfqE8_0MCZMyGqGUDhM_e0Rs,3141
15
+ videosdk_cli/utils/template_helper.py,sha256=r2O7mLe5TC3Sl-fJBduhifJz_Xb25bhKHS8pMTq6CR8,8729
16
+ videosdk_cli/utils/template_options.py,sha256=BFNCmn6lLUCJ-eZ9TkhVbRtMNTP89EVm8MHhZQv61Q0,1872
17
+ videosdk_cli/utils/videosdk_yaml_helper.py,sha256=IIb8HAF8cR4Mths4VHJG9bVy-2tnyGUUfVy8HiZjrLE,7426
18
+ videosdk_cli/utils/apis/deployment_client.py,sha256=MIay-XUql5FUr4NmyqAHfB14z11Hu3FJSiFpWCu9V5U,10689
19
+ videosdk_cli/utils/apis/error.py,sha256=UgJI1a0ZU1ZYKAxz01pOKhFYP1XrIsz8JQVVv7XwgDc,248
20
+ videosdk_cli/utils/apis/videosdk_auth_api_client.py,sha256=w_Gpx_kpjzEtMa3zcL4Wb45NcC65ztpqPrmnRlAjxoQ,1863
21
+ videosdk_cli/utils/manager/agent_manager.py,sha256=7SujOT5j7gbTNQ9KOmXfjqUS8-JBp9FYGbqPcnRTnxU,17021
22
+ videosdk_cli/utils/ui/kv_blocks.py,sha256=_KJZ0_q7AbaIDIh9kx6VvHuSQ3AN5fYo-vsMjYIBbFM,288
23
+ videosdk_cli/utils/ui/progress_runner.py,sha256=CHMQgU6ABKbD6AysMF1EVb3iBrnLqEC8OJPtCjuGYcA,1891
24
+ videosdk_cli/utils/ui/theme.py,sha256=LJ4gEmOwSD5kXH-tesPJBGHlieVoJe1uxRwjtDnqkFY,5857
25
+ videosdkagent_cli-0.0.4.dist-info/METADATA,sha256=D9Fu8h3JujydKCTstSJrMFdKZVrKYbnod-58OuENl0I,446
26
+ videosdkagent_cli-0.0.4.dist-info/WHEEL,sha256=aha0VrrYvgDJ3Xxl3db_g_MDIW-ZexDdrc_m-Hk8YY4,105
27
+ videosdkagent_cli-0.0.4.dist-info/entry_points.txt,sha256=7qv3V6B64WCwEshsIsTUc3fYcg6iNbnVJJOc6WE4U1E,51
28
+ videosdkagent_cli-0.0.4.dist-info/RECORD,,