daita-agents 0.1.0__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.
Potentially problematic release.
This version of daita-agents might be problematic. Click here for more details.
- daita/__init__.py +208 -0
- daita/agents/__init__.py +33 -0
- daita/agents/base.py +722 -0
- daita/agents/substrate.py +895 -0
- daita/cli/__init__.py +145 -0
- daita/cli/__main__.py +7 -0
- daita/cli/ascii_art.py +44 -0
- daita/cli/core/__init__.py +0 -0
- daita/cli/core/create.py +254 -0
- daita/cli/core/deploy.py +473 -0
- daita/cli/core/deployments.py +309 -0
- daita/cli/core/import_detector.py +219 -0
- daita/cli/core/init.py +382 -0
- daita/cli/core/logs.py +239 -0
- daita/cli/core/managed_deploy.py +709 -0
- daita/cli/core/run.py +648 -0
- daita/cli/core/status.py +421 -0
- daita/cli/core/test.py +239 -0
- daita/cli/core/webhooks.py +172 -0
- daita/cli/main.py +588 -0
- daita/cli/utils.py +541 -0
- daita/config/__init__.py +62 -0
- daita/config/base.py +159 -0
- daita/config/settings.py +184 -0
- daita/core/__init__.py +262 -0
- daita/core/decision_tracing.py +701 -0
- daita/core/exceptions.py +480 -0
- daita/core/focus.py +251 -0
- daita/core/interfaces.py +76 -0
- daita/core/plugin_tracing.py +550 -0
- daita/core/relay.py +695 -0
- daita/core/reliability.py +381 -0
- daita/core/scaling.py +444 -0
- daita/core/tools.py +402 -0
- daita/core/tracing.py +770 -0
- daita/core/workflow.py +1084 -0
- daita/display/__init__.py +1 -0
- daita/display/console.py +160 -0
- daita/execution/__init__.py +58 -0
- daita/execution/client.py +856 -0
- daita/execution/exceptions.py +92 -0
- daita/execution/models.py +317 -0
- daita/llm/__init__.py +60 -0
- daita/llm/anthropic.py +166 -0
- daita/llm/base.py +373 -0
- daita/llm/factory.py +101 -0
- daita/llm/gemini.py +152 -0
- daita/llm/grok.py +114 -0
- daita/llm/mock.py +135 -0
- daita/llm/openai.py +109 -0
- daita/plugins/__init__.py +141 -0
- daita/plugins/base.py +37 -0
- daita/plugins/base_db.py +167 -0
- daita/plugins/elasticsearch.py +844 -0
- daita/plugins/mcp.py +481 -0
- daita/plugins/mongodb.py +510 -0
- daita/plugins/mysql.py +351 -0
- daita/plugins/postgresql.py +331 -0
- daita/plugins/redis_messaging.py +500 -0
- daita/plugins/rest.py +529 -0
- daita/plugins/s3.py +761 -0
- daita/plugins/slack.py +729 -0
- daita/utils/__init__.py +18 -0
- daita_agents-0.1.0.dist-info/METADATA +350 -0
- daita_agents-0.1.0.dist-info/RECORD +69 -0
- daita_agents-0.1.0.dist-info/WHEEL +5 -0
- daita_agents-0.1.0.dist-info/entry_points.txt +2 -0
- daita_agents-0.1.0.dist-info/licenses/LICENSE +56 -0
- daita_agents-0.1.0.dist-info/top_level.txt +1 -0
daita/cli/utils.py
ADDED
|
@@ -0,0 +1,541 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Shared utilities for Daita CLI.
|
|
3
|
+
|
|
4
|
+
Common functions used across CLI core modules to avoid duplication.
|
|
5
|
+
"""
|
|
6
|
+
import os
|
|
7
|
+
import json
|
|
8
|
+
import yaml
|
|
9
|
+
import logging
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Dict, Any, Optional
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
# ======= Project Management =======
|
|
16
|
+
|
|
17
|
+
def find_project_root(start_path: Optional[Path] = None) -> Optional[Path]:
|
|
18
|
+
"""
|
|
19
|
+
Find the root directory of a Daita project.
|
|
20
|
+
|
|
21
|
+
Searches upward from the given path (or current directory) looking for .daita folder.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
start_path: Path to start searching from (defaults to current directory)
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
Path to project root, or None if not in a Daita project
|
|
28
|
+
"""
|
|
29
|
+
current = start_path or Path.cwd()
|
|
30
|
+
|
|
31
|
+
for path in [current] + list(current.parents):
|
|
32
|
+
if (path / '.daita').exists():
|
|
33
|
+
return path
|
|
34
|
+
|
|
35
|
+
return None
|
|
36
|
+
|
|
37
|
+
def ensure_project_root() -> Path:
|
|
38
|
+
"""
|
|
39
|
+
Find project root and raise error if not in a project.
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
Path to project root
|
|
43
|
+
|
|
44
|
+
Raises:
|
|
45
|
+
ValueError: If not in a Daita project
|
|
46
|
+
"""
|
|
47
|
+
project_root = find_project_root()
|
|
48
|
+
if not project_root:
|
|
49
|
+
raise ValueError("Not in a Daita project. Run 'daita init' first.")
|
|
50
|
+
return project_root
|
|
51
|
+
|
|
52
|
+
def load_project_config(project_root: Path) -> Optional[Dict[str, Any]]:
|
|
53
|
+
"""
|
|
54
|
+
Load project configuration from daita-project.yaml.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
project_root: Path to project root directory
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
Project configuration dictionary, or None if file doesn't exist
|
|
61
|
+
"""
|
|
62
|
+
config_file = project_root / 'daita-project.yaml'
|
|
63
|
+
|
|
64
|
+
if not config_file.exists():
|
|
65
|
+
return None
|
|
66
|
+
|
|
67
|
+
try:
|
|
68
|
+
with open(config_file, 'r') as f:
|
|
69
|
+
return yaml.safe_load(f)
|
|
70
|
+
except Exception as e:
|
|
71
|
+
logger.warning(f"Failed to load project config: {str(e)}")
|
|
72
|
+
return None
|
|
73
|
+
|
|
74
|
+
def save_project_config(project_root: Path, config: Dict[str, Any]) -> None:
|
|
75
|
+
"""
|
|
76
|
+
Save project configuration to daita-project.yaml.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
project_root: Path to project root directory
|
|
80
|
+
config: Configuration dictionary to save
|
|
81
|
+
"""
|
|
82
|
+
config_file = project_root / 'daita-project.yaml'
|
|
83
|
+
|
|
84
|
+
try:
|
|
85
|
+
with open(config_file, 'w') as f:
|
|
86
|
+
yaml.dump(config, f, default_flow_style=False, sort_keys=False)
|
|
87
|
+
except Exception as e:
|
|
88
|
+
raise ValueError(f"Failed to save project config: {str(e)}")
|
|
89
|
+
|
|
90
|
+
# ======= Deployment Management =======
|
|
91
|
+
|
|
92
|
+
def load_deployments(project_root: Path) -> list:
|
|
93
|
+
"""
|
|
94
|
+
Load deployment history from .daita/deployments.json.
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
project_root: Path to project root directory
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
List of deployment records
|
|
101
|
+
"""
|
|
102
|
+
deployments_file = project_root / '.daita' / 'deployments.json'
|
|
103
|
+
|
|
104
|
+
if not deployments_file.exists():
|
|
105
|
+
return []
|
|
106
|
+
|
|
107
|
+
try:
|
|
108
|
+
with open(deployments_file, 'r') as f:
|
|
109
|
+
return json.load(f)
|
|
110
|
+
except Exception as e:
|
|
111
|
+
logger.warning(f"Failed to load deployments: {str(e)}")
|
|
112
|
+
return []
|
|
113
|
+
|
|
114
|
+
def save_deployments(project_root: Path, deployments: list) -> None:
|
|
115
|
+
"""
|
|
116
|
+
Save deployment history to .daita/deployments.json.
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
project_root: Path to project root directory
|
|
120
|
+
deployments: List of deployment records to save
|
|
121
|
+
"""
|
|
122
|
+
deployments_file = project_root / '.daita' / 'deployments.json'
|
|
123
|
+
|
|
124
|
+
# Ensure .daita directory exists
|
|
125
|
+
deployments_file.parent.mkdir(exist_ok=True)
|
|
126
|
+
|
|
127
|
+
try:
|
|
128
|
+
with open(deployments_file, 'w') as f:
|
|
129
|
+
json.dump(deployments, f, indent=2)
|
|
130
|
+
except Exception as e:
|
|
131
|
+
raise ValueError(f"Failed to save deployments: {str(e)}")
|
|
132
|
+
|
|
133
|
+
# ======= File Operations =======
|
|
134
|
+
|
|
135
|
+
def copy_files(src_dir: Path, dest_dir: Path, pattern: str, verbose: bool = False) -> None:
|
|
136
|
+
"""
|
|
137
|
+
Copy files matching a pattern from source to destination.
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
src_dir: Source directory
|
|
141
|
+
dest_dir: Destination directory
|
|
142
|
+
pattern: File pattern to match (e.g., 'agents/', '*.py')
|
|
143
|
+
verbose: Whether to print detailed output
|
|
144
|
+
"""
|
|
145
|
+
src_path = src_dir / pattern
|
|
146
|
+
|
|
147
|
+
if src_path.is_file():
|
|
148
|
+
# Single file
|
|
149
|
+
dest_file = dest_dir / pattern
|
|
150
|
+
dest_file.parent.mkdir(parents=True, exist_ok=True)
|
|
151
|
+
dest_file.write_bytes(src_path.read_bytes())
|
|
152
|
+
if verbose:
|
|
153
|
+
print(f" Copied: {pattern}")
|
|
154
|
+
|
|
155
|
+
elif src_path.is_dir():
|
|
156
|
+
# Directory - copy all Python files
|
|
157
|
+
for file_path in src_path.rglob('*.py'):
|
|
158
|
+
rel_path = file_path.relative_to(src_dir)
|
|
159
|
+
dest_file = dest_dir / rel_path
|
|
160
|
+
dest_file.parent.mkdir(parents=True, exist_ok=True)
|
|
161
|
+
dest_file.write_bytes(file_path.read_bytes())
|
|
162
|
+
if verbose:
|
|
163
|
+
print(f" Copied: {rel_path}")
|
|
164
|
+
|
|
165
|
+
def list_python_files(directory: Path) -> list:
|
|
166
|
+
"""
|
|
167
|
+
List Python files in a directory (excluding __init__.py).
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
directory: Directory to search
|
|
171
|
+
|
|
172
|
+
Returns:
|
|
173
|
+
List of Python file stems (names without .py extension)
|
|
174
|
+
"""
|
|
175
|
+
if not directory.exists():
|
|
176
|
+
return []
|
|
177
|
+
|
|
178
|
+
files = []
|
|
179
|
+
for file in directory.glob('*.py'):
|
|
180
|
+
if file.name != '__init__.py':
|
|
181
|
+
files.append(file.stem)
|
|
182
|
+
|
|
183
|
+
return sorted(files)
|
|
184
|
+
|
|
185
|
+
# ======= String Utilities =======
|
|
186
|
+
|
|
187
|
+
def to_class_name(snake_case_name: str) -> str:
|
|
188
|
+
"""
|
|
189
|
+
Convert snake_case to PascalCase for class names.
|
|
190
|
+
|
|
191
|
+
Args:
|
|
192
|
+
snake_case_name: Snake case string (e.g., 'my_agent')
|
|
193
|
+
|
|
194
|
+
Returns:
|
|
195
|
+
PascalCase string (e.g., 'MyAgent')
|
|
196
|
+
"""
|
|
197
|
+
return ''.join(word.capitalize() for word in snake_case_name.split('_'))
|
|
198
|
+
|
|
199
|
+
def to_snake_case(name: str) -> str:
|
|
200
|
+
"""
|
|
201
|
+
Convert a name to snake_case.
|
|
202
|
+
|
|
203
|
+
Args:
|
|
204
|
+
name: Input name (any case)
|
|
205
|
+
|
|
206
|
+
Returns:
|
|
207
|
+
Snake case string
|
|
208
|
+
"""
|
|
209
|
+
# Replace spaces and hyphens with underscores
|
|
210
|
+
name = name.replace(' ', '_').replace('-', '_')
|
|
211
|
+
|
|
212
|
+
# Convert to lowercase
|
|
213
|
+
name = name.lower()
|
|
214
|
+
|
|
215
|
+
# Remove any non-alphanumeric characters except underscores
|
|
216
|
+
import re
|
|
217
|
+
name = re.sub(r'[^a-z0-9_]', '', name)
|
|
218
|
+
|
|
219
|
+
# Remove duplicate underscores
|
|
220
|
+
while '__' in name:
|
|
221
|
+
name = name.replace('__', '_')
|
|
222
|
+
|
|
223
|
+
# Remove leading/trailing underscores
|
|
224
|
+
return name.strip('_')
|
|
225
|
+
|
|
226
|
+
def format_result_preview(result: Any, max_length: int = 100) -> str:
|
|
227
|
+
"""
|
|
228
|
+
Format a result for preview display.
|
|
229
|
+
|
|
230
|
+
Args:
|
|
231
|
+
result: Result to format
|
|
232
|
+
max_length: Maximum length of preview string
|
|
233
|
+
|
|
234
|
+
Returns:
|
|
235
|
+
Formatted preview string
|
|
236
|
+
"""
|
|
237
|
+
if isinstance(result, dict):
|
|
238
|
+
# Show status and key count
|
|
239
|
+
status = result.get('status', 'unknown')
|
|
240
|
+
key_count = len(result.keys())
|
|
241
|
+
return f"status={status}, {key_count} keys"
|
|
242
|
+
|
|
243
|
+
elif isinstance(result, list):
|
|
244
|
+
# Show list length and type of first item
|
|
245
|
+
if result:
|
|
246
|
+
first_type = type(result[0]).__name__
|
|
247
|
+
return f"list[{len(result)}] of {first_type}"
|
|
248
|
+
else:
|
|
249
|
+
return "empty list"
|
|
250
|
+
|
|
251
|
+
else:
|
|
252
|
+
# Show truncated string representation
|
|
253
|
+
result_str = str(result)
|
|
254
|
+
if len(result_str) > max_length:
|
|
255
|
+
return result_str[:max_length] + "..."
|
|
256
|
+
return result_str
|
|
257
|
+
|
|
258
|
+
# ======= Environment Utilities =======
|
|
259
|
+
|
|
260
|
+
def has_api_key() -> bool:
|
|
261
|
+
"""
|
|
262
|
+
Check if any API key is configured in environment.
|
|
263
|
+
|
|
264
|
+
Returns:
|
|
265
|
+
True if at least one API key is found
|
|
266
|
+
"""
|
|
267
|
+
api_keys = [
|
|
268
|
+
'OPENAI_API_KEY',
|
|
269
|
+
'ANTHROPIC_API_KEY',
|
|
270
|
+
'GOOGLE_API_KEY',
|
|
271
|
+
'GEMINI_API_KEY',
|
|
272
|
+
'XAI_API_KEY',
|
|
273
|
+
'GROK_API_KEY'
|
|
274
|
+
]
|
|
275
|
+
|
|
276
|
+
return any(os.getenv(key) for key in api_keys)
|
|
277
|
+
|
|
278
|
+
def get_configured_providers() -> list:
|
|
279
|
+
"""
|
|
280
|
+
Get list of LLM providers that have API keys configured.
|
|
281
|
+
|
|
282
|
+
Returns:
|
|
283
|
+
List of provider names
|
|
284
|
+
"""
|
|
285
|
+
providers = []
|
|
286
|
+
|
|
287
|
+
if os.getenv('OPENAI_API_KEY'):
|
|
288
|
+
providers.append('openai')
|
|
289
|
+
|
|
290
|
+
if os.getenv('ANTHROPIC_API_KEY'):
|
|
291
|
+
providers.append('anthropic')
|
|
292
|
+
|
|
293
|
+
if os.getenv('GOOGLE_API_KEY') or os.getenv('GEMINI_API_KEY'):
|
|
294
|
+
providers.append('gemini')
|
|
295
|
+
|
|
296
|
+
if os.getenv('XAI_API_KEY') or os.getenv('GROK_API_KEY'):
|
|
297
|
+
providers.append('grok')
|
|
298
|
+
|
|
299
|
+
return providers
|
|
300
|
+
|
|
301
|
+
# ======= Validation Utilities =======
|
|
302
|
+
|
|
303
|
+
def validate_project_name(name: str) -> bool:
|
|
304
|
+
"""
|
|
305
|
+
Validate project name for safety and compatibility.
|
|
306
|
+
|
|
307
|
+
Args:
|
|
308
|
+
name: Project name to validate
|
|
309
|
+
|
|
310
|
+
Returns:
|
|
311
|
+
True if name is valid
|
|
312
|
+
"""
|
|
313
|
+
if not name:
|
|
314
|
+
return False
|
|
315
|
+
|
|
316
|
+
# Check length
|
|
317
|
+
if len(name) > 50:
|
|
318
|
+
return False
|
|
319
|
+
|
|
320
|
+
# Check for valid characters (alphanumeric, underscore, hyphen)
|
|
321
|
+
import re
|
|
322
|
+
if not re.match(r'^[a-zA-Z0-9_-]+$', name):
|
|
323
|
+
return False
|
|
324
|
+
|
|
325
|
+
# Must start with letter or number
|
|
326
|
+
if not re.match(r'^[a-zA-Z0-9]', name):
|
|
327
|
+
return False
|
|
328
|
+
|
|
329
|
+
return True
|
|
330
|
+
|
|
331
|
+
def validate_component_name(name: str) -> bool:
|
|
332
|
+
"""
|
|
333
|
+
Validate agent/workflow name for Python compatibility.
|
|
334
|
+
|
|
335
|
+
Args:
|
|
336
|
+
name: Component name to validate
|
|
337
|
+
|
|
338
|
+
Returns:
|
|
339
|
+
True if name is valid for Python module
|
|
340
|
+
"""
|
|
341
|
+
if not name:
|
|
342
|
+
return False
|
|
343
|
+
|
|
344
|
+
# Check length
|
|
345
|
+
if len(name) > 30:
|
|
346
|
+
return False
|
|
347
|
+
|
|
348
|
+
# Check for valid Python identifier characters
|
|
349
|
+
import re
|
|
350
|
+
if not re.match(r'^[a-zA-Z0-9_]+$', name):
|
|
351
|
+
return False
|
|
352
|
+
|
|
353
|
+
# Must start with letter or underscore
|
|
354
|
+
if not re.match(r'^[a-zA-Z_]', name):
|
|
355
|
+
return False
|
|
356
|
+
|
|
357
|
+
# Cannot be Python keywords
|
|
358
|
+
import keyword
|
|
359
|
+
if keyword.iskeyword(name):
|
|
360
|
+
return False
|
|
361
|
+
|
|
362
|
+
return True
|
|
363
|
+
|
|
364
|
+
# ======= Error Handling =======
|
|
365
|
+
|
|
366
|
+
class CLIError(Exception):
|
|
367
|
+
"""Base exception for CLI errors."""
|
|
368
|
+
pass
|
|
369
|
+
|
|
370
|
+
class ProjectNotFoundError(CLIError):
|
|
371
|
+
"""Raised when not in a Daita project."""
|
|
372
|
+
pass
|
|
373
|
+
|
|
374
|
+
class ConfigurationError(CLIError):
|
|
375
|
+
"""Raised for configuration-related errors."""
|
|
376
|
+
pass
|
|
377
|
+
|
|
378
|
+
def handle_cli_error(error: Exception, verbose: bool = False) -> None:
|
|
379
|
+
"""
|
|
380
|
+
Handle CLI errors with appropriate user messages.
|
|
381
|
+
|
|
382
|
+
Args:
|
|
383
|
+
error: Exception that occurred
|
|
384
|
+
verbose: Whether to show detailed error information
|
|
385
|
+
"""
|
|
386
|
+
if isinstance(error, ProjectNotFoundError):
|
|
387
|
+
print(" Not in a Daita project directory.")
|
|
388
|
+
print(" Run 'daita init' to create a new project.")
|
|
389
|
+
|
|
390
|
+
elif isinstance(error, ConfigurationError):
|
|
391
|
+
print(f" Configuration error: {str(error)}")
|
|
392
|
+
|
|
393
|
+
elif isinstance(error, CLIError):
|
|
394
|
+
print(f" {str(error)}")
|
|
395
|
+
|
|
396
|
+
else:
|
|
397
|
+
if verbose:
|
|
398
|
+
import traceback
|
|
399
|
+
print(f" Unexpected error: {str(error)}")
|
|
400
|
+
print("\nDetailed error:")
|
|
401
|
+
traceback.print_exc()
|
|
402
|
+
else:
|
|
403
|
+
print(f" Error: {str(error)}")
|
|
404
|
+
print(" Use --verbose for more details.")
|
|
405
|
+
|
|
406
|
+
# ======= Freemium Business Model =======
|
|
407
|
+
|
|
408
|
+
def has_daita_api_key() -> bool:
|
|
409
|
+
"""
|
|
410
|
+
Check if DAITA_API_KEY is configured for cloud operations.
|
|
411
|
+
|
|
412
|
+
Returns:
|
|
413
|
+
True if DAITA_API_KEY is available
|
|
414
|
+
"""
|
|
415
|
+
# First check environment variable
|
|
416
|
+
if os.getenv('DAITA_API_KEY'):
|
|
417
|
+
return True
|
|
418
|
+
|
|
419
|
+
# Try to load from .env file in current directory
|
|
420
|
+
try:
|
|
421
|
+
from dotenv import load_dotenv
|
|
422
|
+
from pathlib import Path
|
|
423
|
+
|
|
424
|
+
# Try loading from current directory
|
|
425
|
+
env_path = Path.cwd() / '.env'
|
|
426
|
+
if env_path.exists():
|
|
427
|
+
load_dotenv(env_path)
|
|
428
|
+
|
|
429
|
+
api_key = os.getenv('DAITA_API_KEY')
|
|
430
|
+
return bool(api_key)
|
|
431
|
+
except ImportError:
|
|
432
|
+
# dotenv is optional, ignore if not installed
|
|
433
|
+
return False
|
|
434
|
+
except Exception as e:
|
|
435
|
+
return False
|
|
436
|
+
|
|
437
|
+
def require_api_key_for_cloud() -> None:
|
|
438
|
+
"""
|
|
439
|
+
Enforce API key requirement for cloud operations with upgrade messaging.
|
|
440
|
+
|
|
441
|
+
Raises:
|
|
442
|
+
SystemExit: If no API key is found, shows upgrade message and exits
|
|
443
|
+
"""
|
|
444
|
+
if not has_daita_api_key():
|
|
445
|
+
show_upgrade_message()
|
|
446
|
+
raise SystemExit(1)
|
|
447
|
+
|
|
448
|
+
def show_upgrade_message() -> None:
|
|
449
|
+
"""
|
|
450
|
+
Display freemium upgrade message for cloud features.
|
|
451
|
+
"""
|
|
452
|
+
print("Ready to deploy to the cloud?")
|
|
453
|
+
print("")
|
|
454
|
+
print(" Get your API key at daita-tech.io")
|
|
455
|
+
print(" Then: export DAITA_API_KEY='your-key-here'")
|
|
456
|
+
print("")
|
|
457
|
+
print(" Get insights, monitoring, and 24/7 hosting")
|
|
458
|
+
|
|
459
|
+
def show_local_vs_cloud_help() -> None:
|
|
460
|
+
"""
|
|
461
|
+
Show help message explaining local vs cloud features.
|
|
462
|
+
"""
|
|
463
|
+
print("")
|
|
464
|
+
print("Daita Command Guide:")
|
|
465
|
+
print("")
|
|
466
|
+
print(" FREE (Local Development):")
|
|
467
|
+
print(" • daita init - Create projects")
|
|
468
|
+
print(" • daita create - Build agents & workflows")
|
|
469
|
+
print(" • daita test - Test locally")
|
|
470
|
+
print(" • daita test --watch - Development mode")
|
|
471
|
+
print("")
|
|
472
|
+
print(" PREMIUM (Cloud Hosting):")
|
|
473
|
+
print(" • daita push - Deploy to cloud")
|
|
474
|
+
print(" • daita status - Monitor deployments")
|
|
475
|
+
print(" • daita logs - View execution logs")
|
|
476
|
+
print(" • daita run - Execute remotely")
|
|
477
|
+
print("")
|
|
478
|
+
print(" Get started: daita-tech.io")
|
|
479
|
+
|
|
480
|
+
def get_freemium_success_message(project_name: str) -> str:
|
|
481
|
+
"""
|
|
482
|
+
Generate success message for project initialization with freemium guidance.
|
|
483
|
+
|
|
484
|
+
Args:
|
|
485
|
+
project_name: Name of the created project
|
|
486
|
+
|
|
487
|
+
Returns:
|
|
488
|
+
Formatted success message with next steps
|
|
489
|
+
"""
|
|
490
|
+
return f"""Project '{project_name}' created successfully!
|
|
491
|
+
|
|
492
|
+
Next steps:
|
|
493
|
+
1. cd {project_name}
|
|
494
|
+
2. daita test # Test locally (always free)
|
|
495
|
+
3. daita create agent # Add more agents (free)
|
|
496
|
+
4. daita test --watch # Development mode (free)
|
|
497
|
+
|
|
498
|
+
Ready for production?
|
|
499
|
+
• daita push staging # Deploy to cloud (requires API key)
|
|
500
|
+
• Get your API key at daita-tech.io
|
|
501
|
+
• Start your free trial with full monitoring & insights!"""
|
|
502
|
+
|
|
503
|
+
# Cloud command definitions for enforcement
|
|
504
|
+
CLOUD_COMMANDS = {
|
|
505
|
+
'push', 'status', 'logs', 'deployments', 'run',
|
|
506
|
+
'executions', 'execution-logs'
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
def is_cloud_command(command: str) -> bool:
|
|
510
|
+
"""
|
|
511
|
+
Check if a command requires cloud/API key access.
|
|
512
|
+
|
|
513
|
+
Args:
|
|
514
|
+
command: Command name to check
|
|
515
|
+
|
|
516
|
+
Returns:
|
|
517
|
+
True if command requires API key
|
|
518
|
+
"""
|
|
519
|
+
return command in CLOUD_COMMANDS
|
|
520
|
+
|
|
521
|
+
# ======= Logging Setup =======
|
|
522
|
+
|
|
523
|
+
def setup_cli_logging(level: str = "INFO") -> None:
|
|
524
|
+
"""
|
|
525
|
+
Setup logging for CLI operations.
|
|
526
|
+
|
|
527
|
+
Args:
|
|
528
|
+
level: Logging level (DEBUG, INFO, WARNING, ERROR)
|
|
529
|
+
"""
|
|
530
|
+
log_level = getattr(logging, level.upper(), logging.INFO)
|
|
531
|
+
|
|
532
|
+
logging.basicConfig(
|
|
533
|
+
level=log_level,
|
|
534
|
+
format="%(levelname)s: %(message)s",
|
|
535
|
+
force=True # Override any existing configuration
|
|
536
|
+
)
|
|
537
|
+
|
|
538
|
+
# Silence verbose third-party loggers
|
|
539
|
+
logging.getLogger("urllib3").setLevel(logging.WARNING)
|
|
540
|
+
logging.getLogger("requests").setLevel(logging.WARNING)
|
|
541
|
+
logging.getLogger("aiohttp").setLevel(logging.WARNING)
|
daita/config/__init__.py
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Configuration system for Daita Agents.
|
|
3
|
+
|
|
4
|
+
Simplified configuration classes focused on essential functionality.
|
|
5
|
+
|
|
6
|
+
Core configuration classes:
|
|
7
|
+
- AgentConfig: Configuration for individual agents
|
|
8
|
+
|
|
9
|
+
- DaitaConfig: Overall framework configuration
|
|
10
|
+
- RetryPolicy: Simple retry behavior configuration
|
|
11
|
+
|
|
12
|
+
Enums and types:
|
|
13
|
+
- AgentType: Types of available agents
|
|
14
|
+
- FocusType: Types of focus selectors
|
|
15
|
+
- RetryStrategy: Retry timing strategies
|
|
16
|
+
|
|
17
|
+
Usage:
|
|
18
|
+
```python
|
|
19
|
+
from daita.config import AgentConfig, RetryPolicy
|
|
20
|
+
|
|
21
|
+
# Simple agent configuration
|
|
22
|
+
config = AgentConfig(name="My Agent")
|
|
23
|
+
|
|
24
|
+
# Agent with retry enabled
|
|
25
|
+
config = AgentConfig(
|
|
26
|
+
name="My Agent",
|
|
27
|
+
enable_retry=True,
|
|
28
|
+
retry_policy=RetryPolicy(max_retries=5)
|
|
29
|
+
)
|
|
30
|
+
```
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
from .base import (
|
|
34
|
+
# Enums
|
|
35
|
+
AgentType,
|
|
36
|
+
FocusType,
|
|
37
|
+
RetryStrategy,
|
|
38
|
+
|
|
39
|
+
# Configuration classes
|
|
40
|
+
FocusConfig,
|
|
41
|
+
RetryPolicy,
|
|
42
|
+
AgentConfig,
|
|
43
|
+
DaitaConfig,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
from .settings import settings
|
|
47
|
+
|
|
48
|
+
__all__ = [
|
|
49
|
+
# Enums
|
|
50
|
+
"AgentType",
|
|
51
|
+
"FocusType",
|
|
52
|
+
"RetryStrategy",
|
|
53
|
+
|
|
54
|
+
# Configuration classes
|
|
55
|
+
"FocusConfig",
|
|
56
|
+
"RetryPolicy",
|
|
57
|
+
"AgentConfig",
|
|
58
|
+
"DaitaConfig",
|
|
59
|
+
|
|
60
|
+
# Runtime settings
|
|
61
|
+
"settings",
|
|
62
|
+
]
|