daita-agents 0.2.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.
- daita/__init__.py +216 -0
- daita/agents/__init__.py +33 -0
- daita/agents/base.py +743 -0
- daita/agents/substrate.py +1141 -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 +481 -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 +779 -0
- daita/core/reliability.py +381 -0
- daita/core/scaling.py +459 -0
- daita/core/tools.py +554 -0
- daita/core/tracing.py +770 -0
- daita/core/workflow.py +1144 -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 +291 -0
- daita/llm/base.py +530 -0
- daita/llm/factory.py +101 -0
- daita/llm/gemini.py +355 -0
- daita/llm/grok.py +219 -0
- daita/llm/mock.py +172 -0
- daita/llm/openai.py +220 -0
- daita/plugins/__init__.py +141 -0
- daita/plugins/base.py +37 -0
- daita/plugins/base_db.py +167 -0
- daita/plugins/elasticsearch.py +849 -0
- daita/plugins/mcp.py +481 -0
- daita/plugins/mongodb.py +520 -0
- daita/plugins/mysql.py +362 -0
- daita/plugins/postgresql.py +342 -0
- daita/plugins/redis_messaging.py +500 -0
- daita/plugins/rest.py +537 -0
- daita/plugins/s3.py +770 -0
- daita/plugins/slack.py +729 -0
- daita/utils/__init__.py +18 -0
- daita_agents-0.2.0.dist-info/METADATA +409 -0
- daita_agents-0.2.0.dist-info/RECORD +69 -0
- daita_agents-0.2.0.dist-info/WHEEL +5 -0
- daita_agents-0.2.0.dist-info/entry_points.txt +2 -0
- daita_agents-0.2.0.dist-info/licenses/LICENSE +56 -0
- daita_agents-0.2.0.dist-info/top_level.txt +1 -0
daita/cli/main.py
ADDED
|
@@ -0,0 +1,588 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Daita CLI - Command Line Interface for Daita Agents.
|
|
3
|
+
|
|
4
|
+
Simple, git-like CLI for building and deploying AI agents.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
daita init [project-name] # Initialize new project
|
|
8
|
+
daita create agent <name> # Create agent
|
|
9
|
+
daita create workflow <name> # Create workflow
|
|
10
|
+
daita test [target] # Test agents/workflows
|
|
11
|
+
daita push # Deploy to production
|
|
12
|
+
daita status # Show project status
|
|
13
|
+
daita logs # View deployment logs
|
|
14
|
+
"""
|
|
15
|
+
import click
|
|
16
|
+
import asyncio
|
|
17
|
+
import logging
|
|
18
|
+
import sys
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
|
|
21
|
+
# Load environment variables from .env file
|
|
22
|
+
try:
|
|
23
|
+
from dotenv import load_dotenv
|
|
24
|
+
load_dotenv()
|
|
25
|
+
except ImportError:
|
|
26
|
+
# dotenv is optional, ignore if not installed
|
|
27
|
+
pass
|
|
28
|
+
|
|
29
|
+
# Check for required CLI dependencies
|
|
30
|
+
def check_cli_dependencies():
|
|
31
|
+
"""Check if CLI dependencies are installed."""
|
|
32
|
+
missing = []
|
|
33
|
+
|
|
34
|
+
try:
|
|
35
|
+
import yaml
|
|
36
|
+
except ImportError:
|
|
37
|
+
missing.append("PyYAML")
|
|
38
|
+
|
|
39
|
+
try:
|
|
40
|
+
import watchdog
|
|
41
|
+
except ImportError:
|
|
42
|
+
missing.append("watchdog")
|
|
43
|
+
|
|
44
|
+
if missing:
|
|
45
|
+
click.echo(" Missing CLI dependencies. Install with:", err=True)
|
|
46
|
+
click.echo(f" pip install {' '.join(missing)}", err=True)
|
|
47
|
+
click.echo(" Or install all CLI dependencies with:", err=True)
|
|
48
|
+
click.echo(" pip install daita-agents[cli]", err=True)
|
|
49
|
+
sys.exit(1)
|
|
50
|
+
|
|
51
|
+
# Import core functions only after dependency check
|
|
52
|
+
def _import_core_functions():
|
|
53
|
+
"""Import core CLI functions after dependency check."""
|
|
54
|
+
try:
|
|
55
|
+
from .core.init import initialize_project
|
|
56
|
+
from .core.create import create_from_template
|
|
57
|
+
from .core.test import run_tests
|
|
58
|
+
from .core.deploy import deploy_to_environment
|
|
59
|
+
from .core.status import show_project_status
|
|
60
|
+
from .core.logs import show_deployment_logs
|
|
61
|
+
from .core.deployments import (
|
|
62
|
+
list_deployments, download_deployment, show_deployment_details,
|
|
63
|
+
rollback_deployment, delete_deployment
|
|
64
|
+
)
|
|
65
|
+
from .core.run import (
|
|
66
|
+
run_remote_execution, list_remote_executions, get_execution_logs
|
|
67
|
+
)
|
|
68
|
+
return (
|
|
69
|
+
initialize_project,
|
|
70
|
+
create_from_template,
|
|
71
|
+
run_tests,
|
|
72
|
+
deploy_to_environment,
|
|
73
|
+
show_project_status,
|
|
74
|
+
show_deployment_logs,
|
|
75
|
+
list_deployments,
|
|
76
|
+
download_deployment,
|
|
77
|
+
show_deployment_details,
|
|
78
|
+
rollback_deployment,
|
|
79
|
+
delete_deployment,
|
|
80
|
+
run_remote_execution,
|
|
81
|
+
list_remote_executions,
|
|
82
|
+
get_execution_logs
|
|
83
|
+
)
|
|
84
|
+
except ImportError as e:
|
|
85
|
+
click.echo(f" Error importing CLI modules: {str(e)}", err=True)
|
|
86
|
+
sys.exit(1)
|
|
87
|
+
|
|
88
|
+
# CLI version
|
|
89
|
+
__version__ = "0.1.1"
|
|
90
|
+
|
|
91
|
+
def _check_first_time_usage():
|
|
92
|
+
"""Check if this is the user's first time using the CLI and show welcome banner."""
|
|
93
|
+
import os
|
|
94
|
+
from pathlib import Path
|
|
95
|
+
|
|
96
|
+
# Create a marker file in user's home directory
|
|
97
|
+
marker_file = Path.home() / '.daita_cli_first_run'
|
|
98
|
+
|
|
99
|
+
# If marker doesn't exist, this is first run
|
|
100
|
+
if not marker_file.exists():
|
|
101
|
+
try:
|
|
102
|
+
# Show welcome banner
|
|
103
|
+
from .ascii_art import display_welcome_banner
|
|
104
|
+
display_welcome_banner()
|
|
105
|
+
click.echo(" Welcome to Daita! ")
|
|
106
|
+
click.echo(" Get started with: daita init my_project")
|
|
107
|
+
click.echo(" For help: daita --help")
|
|
108
|
+
click.echo("")
|
|
109
|
+
|
|
110
|
+
# Create marker file to remember this isn't first run anymore
|
|
111
|
+
marker_file.touch()
|
|
112
|
+
except Exception:
|
|
113
|
+
# If anything goes wrong, silently continue
|
|
114
|
+
pass
|
|
115
|
+
|
|
116
|
+
@click.group()
|
|
117
|
+
@click.version_option(version=__version__, prog_name="daita")
|
|
118
|
+
@click.option('--verbose', '-v', is_flag=True, help='Enable verbose output')
|
|
119
|
+
@click.option('--quiet', '-q', is_flag=True, help='Suppress non-error output')
|
|
120
|
+
@click.pass_context
|
|
121
|
+
def cli(ctx, verbose, quiet):
|
|
122
|
+
"""
|
|
123
|
+
Daita CLI - AI Agent Framework Command Line Interface.
|
|
124
|
+
|
|
125
|
+
Build, test, and deploy AI agents with ease.
|
|
126
|
+
"""
|
|
127
|
+
# Check CLI dependencies
|
|
128
|
+
check_cli_dependencies()
|
|
129
|
+
|
|
130
|
+
# Import core functions after dependency check
|
|
131
|
+
ctx.ensure_object(dict)
|
|
132
|
+
ctx.obj['core_functions'] = _import_core_functions()
|
|
133
|
+
|
|
134
|
+
# Setup logging
|
|
135
|
+
if verbose:
|
|
136
|
+
log_level = logging.DEBUG
|
|
137
|
+
elif quiet:
|
|
138
|
+
log_level = logging.ERROR
|
|
139
|
+
else:
|
|
140
|
+
log_level = logging.INFO
|
|
141
|
+
|
|
142
|
+
logging.basicConfig(
|
|
143
|
+
level=log_level,
|
|
144
|
+
format="%(levelname)s: %(message)s"
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
# Store options in context
|
|
148
|
+
ctx.obj['verbose'] = verbose
|
|
149
|
+
ctx.obj['quiet'] = quiet
|
|
150
|
+
|
|
151
|
+
# ======= Core Commands =======
|
|
152
|
+
|
|
153
|
+
@cli.command()
|
|
154
|
+
@click.argument('project_name', required=False)
|
|
155
|
+
@click.option('--type', 'project_type', default='basic',
|
|
156
|
+
type=click.Choice(['basic', 'analysis', 'pipeline']),
|
|
157
|
+
help='Type of project to create')
|
|
158
|
+
@click.option('--force', is_flag=True, help='Overwrite existing project')
|
|
159
|
+
@click.pass_context
|
|
160
|
+
def init(ctx, project_name, project_type, force):
|
|
161
|
+
"""Initialize a new Daita project."""
|
|
162
|
+
try:
|
|
163
|
+
initialize_project = ctx.obj['core_functions'][0]
|
|
164
|
+
asyncio.run(initialize_project(
|
|
165
|
+
project_name=project_name,
|
|
166
|
+
project_type=project_type,
|
|
167
|
+
force=force,
|
|
168
|
+
verbose=ctx.obj.get('verbose', False)
|
|
169
|
+
))
|
|
170
|
+
except KeyboardInterrupt:
|
|
171
|
+
click.echo("\n Operation cancelled.", err=True)
|
|
172
|
+
sys.exit(1)
|
|
173
|
+
except Exception as e:
|
|
174
|
+
if ctx.obj.get('verbose'):
|
|
175
|
+
logging.exception("Init command failed")
|
|
176
|
+
click.echo(f" Error: {str(e)}", err=True)
|
|
177
|
+
sys.exit(1)
|
|
178
|
+
|
|
179
|
+
@cli.group()
|
|
180
|
+
def create():
|
|
181
|
+
"""Create agents, workflows, and other components."""
|
|
182
|
+
pass
|
|
183
|
+
|
|
184
|
+
@create.command()
|
|
185
|
+
@click.argument('name')
|
|
186
|
+
@click.pass_context
|
|
187
|
+
def agent(ctx, name):
|
|
188
|
+
"""Create a new agent."""
|
|
189
|
+
try:
|
|
190
|
+
create_from_template = ctx.obj['core_functions'][1]
|
|
191
|
+
create_from_template(
|
|
192
|
+
template='agent',
|
|
193
|
+
name=name,
|
|
194
|
+
verbose=ctx.obj.get('verbose', False)
|
|
195
|
+
)
|
|
196
|
+
except Exception as e:
|
|
197
|
+
click.echo(f" Error: {str(e)}", err=True)
|
|
198
|
+
sys.exit(1)
|
|
199
|
+
|
|
200
|
+
@create.command()
|
|
201
|
+
@click.argument('name')
|
|
202
|
+
@click.pass_context
|
|
203
|
+
def workflow(ctx, name):
|
|
204
|
+
"""Create a new workflow."""
|
|
205
|
+
try:
|
|
206
|
+
create_from_template = ctx.obj['core_functions'][1]
|
|
207
|
+
create_from_template(
|
|
208
|
+
template='workflow',
|
|
209
|
+
name=name,
|
|
210
|
+
verbose=ctx.obj.get('verbose', False)
|
|
211
|
+
)
|
|
212
|
+
except Exception as e:
|
|
213
|
+
click.echo(f" Error: {str(e)}", err=True)
|
|
214
|
+
sys.exit(1)
|
|
215
|
+
|
|
216
|
+
@cli.command()
|
|
217
|
+
@click.argument('target', required=False)
|
|
218
|
+
@click.option('--data', help='Test data file to use')
|
|
219
|
+
@click.option('--watch', is_flag=True, help='Watch for changes and re-run tests')
|
|
220
|
+
@click.pass_context
|
|
221
|
+
def test(ctx, target, data, watch):
|
|
222
|
+
"""Test agents and workflows."""
|
|
223
|
+
try:
|
|
224
|
+
run_tests = ctx.obj['core_functions'][2]
|
|
225
|
+
asyncio.run(run_tests(
|
|
226
|
+
target=target,
|
|
227
|
+
data_file=data,
|
|
228
|
+
watch=watch,
|
|
229
|
+
verbose=ctx.obj.get('verbose', False)
|
|
230
|
+
))
|
|
231
|
+
except KeyboardInterrupt:
|
|
232
|
+
click.echo("\n Tests cancelled.", err=True)
|
|
233
|
+
sys.exit(1)
|
|
234
|
+
except Exception as e:
|
|
235
|
+
if ctx.obj.get('verbose'):
|
|
236
|
+
logging.exception("Test command failed")
|
|
237
|
+
click.echo(f" Error: {str(e)}", err=True)
|
|
238
|
+
sys.exit(1)
|
|
239
|
+
|
|
240
|
+
@cli.command()
|
|
241
|
+
@click.option('--force', is_flag=True, help='Force deployment without confirmation')
|
|
242
|
+
@click.option('--dry-run', is_flag=True, help='Show what would be deployed')
|
|
243
|
+
@click.pass_context
|
|
244
|
+
def push(ctx, force, dry_run):
|
|
245
|
+
"""Deploy to production (like git push)."""
|
|
246
|
+
environment = 'production'
|
|
247
|
+
try:
|
|
248
|
+
deploy_to_environment = ctx.obj['core_functions'][3]
|
|
249
|
+
asyncio.run(deploy_to_environment(
|
|
250
|
+
environment=environment,
|
|
251
|
+
force=force,
|
|
252
|
+
dry_run=dry_run,
|
|
253
|
+
verbose=ctx.obj.get('verbose', False)
|
|
254
|
+
))
|
|
255
|
+
except KeyboardInterrupt:
|
|
256
|
+
click.echo("\n Deployment cancelled.", err=True)
|
|
257
|
+
sys.exit(1)
|
|
258
|
+
except Exception as e:
|
|
259
|
+
if ctx.obj.get('verbose'):
|
|
260
|
+
logging.exception("Deploy command failed")
|
|
261
|
+
click.echo(f" Error: {str(e)}", err=True)
|
|
262
|
+
sys.exit(1)
|
|
263
|
+
|
|
264
|
+
@cli.command()
|
|
265
|
+
@click.pass_context
|
|
266
|
+
def status(ctx):
|
|
267
|
+
"""Show project and deployment status (like git status)."""
|
|
268
|
+
try:
|
|
269
|
+
show_project_status = ctx.obj['core_functions'][4]
|
|
270
|
+
asyncio.run(show_project_status(
|
|
271
|
+
environment='production', # Only production is supported
|
|
272
|
+
verbose=ctx.obj.get('verbose', False)
|
|
273
|
+
))
|
|
274
|
+
except Exception as e:
|
|
275
|
+
if ctx.obj.get('verbose'):
|
|
276
|
+
logging.exception("Status command failed")
|
|
277
|
+
click.echo(f" Error: {str(e)}", err=True)
|
|
278
|
+
sys.exit(1)
|
|
279
|
+
|
|
280
|
+
@cli.command()
|
|
281
|
+
@click.option('--follow', '-f', is_flag=True, help='Follow log output')
|
|
282
|
+
@click.option('--lines', '-n', default=10, help='Number of lines to show')
|
|
283
|
+
@click.pass_context
|
|
284
|
+
def logs(ctx, follow, lines):
|
|
285
|
+
"""View deployment logs (like git log)."""
|
|
286
|
+
try:
|
|
287
|
+
show_deployment_logs = ctx.obj['core_functions'][5]
|
|
288
|
+
asyncio.run(show_deployment_logs(
|
|
289
|
+
environment='production', # Only production is supported
|
|
290
|
+
limit=lines,
|
|
291
|
+
follow=follow,
|
|
292
|
+
verbose=ctx.obj.get('verbose', False)
|
|
293
|
+
))
|
|
294
|
+
except KeyboardInterrupt:
|
|
295
|
+
if follow:
|
|
296
|
+
click.echo("\n Stopped following logs.", err=True)
|
|
297
|
+
sys.exit(1)
|
|
298
|
+
except Exception as e:
|
|
299
|
+
if ctx.obj.get('verbose'):
|
|
300
|
+
logging.exception("Logs command failed")
|
|
301
|
+
click.echo(f" Error: {str(e)}", err=True)
|
|
302
|
+
sys.exit(1)
|
|
303
|
+
|
|
304
|
+
# ======= Deployment Management Commands =======
|
|
305
|
+
|
|
306
|
+
@cli.group()
|
|
307
|
+
def deployments():
|
|
308
|
+
"""Manage deployments."""
|
|
309
|
+
pass
|
|
310
|
+
|
|
311
|
+
@deployments.command('list')
|
|
312
|
+
@click.argument('project_name', required=False)
|
|
313
|
+
@click.option('--limit', default=10, help='Number of deployments to show')
|
|
314
|
+
@click.pass_context
|
|
315
|
+
def list_cmd(ctx, project_name, limit):
|
|
316
|
+
"""List deployment history."""
|
|
317
|
+
try:
|
|
318
|
+
list_deployments_func = ctx.obj['core_functions'][6]
|
|
319
|
+
asyncio.run(list_deployments_func(project_name, 'production', limit))
|
|
320
|
+
except Exception as e:
|
|
321
|
+
if ctx.obj.get('verbose'):
|
|
322
|
+
logging.exception("List deployments failed")
|
|
323
|
+
click.echo(f" Error: {str(e)}", err=True)
|
|
324
|
+
sys.exit(1)
|
|
325
|
+
|
|
326
|
+
@deployments.command()
|
|
327
|
+
@click.argument('deployment_id')
|
|
328
|
+
@click.option('--output', '-o', help='Output file path')
|
|
329
|
+
@click.pass_context
|
|
330
|
+
def download(ctx, deployment_id, output):
|
|
331
|
+
"""Download a deployment package."""
|
|
332
|
+
try:
|
|
333
|
+
download_deployment_func = ctx.obj['core_functions'][7]
|
|
334
|
+
asyncio.run(download_deployment_func(deployment_id, output))
|
|
335
|
+
except Exception as e:
|
|
336
|
+
if ctx.obj.get('verbose'):
|
|
337
|
+
logging.exception("Download deployment failed")
|
|
338
|
+
click.echo(f" Error: {str(e)}", err=True)
|
|
339
|
+
sys.exit(1)
|
|
340
|
+
|
|
341
|
+
@deployments.command()
|
|
342
|
+
@click.argument('deployment_id')
|
|
343
|
+
@click.pass_context
|
|
344
|
+
def show(ctx, deployment_id):
|
|
345
|
+
"""Show detailed deployment information."""
|
|
346
|
+
try:
|
|
347
|
+
show_deployment_details_func = ctx.obj['core_functions'][8]
|
|
348
|
+
asyncio.run(show_deployment_details_func(deployment_id))
|
|
349
|
+
except Exception as e:
|
|
350
|
+
if ctx.obj.get('verbose'):
|
|
351
|
+
logging.exception("Show deployment failed")
|
|
352
|
+
click.echo(f" Error: {str(e)}", err=True)
|
|
353
|
+
sys.exit(1)
|
|
354
|
+
|
|
355
|
+
@deployments.command()
|
|
356
|
+
@click.argument('deployment_id')
|
|
357
|
+
@click.pass_context
|
|
358
|
+
def rollback(ctx, deployment_id):
|
|
359
|
+
"""Rollback to a previous deployment."""
|
|
360
|
+
try:
|
|
361
|
+
rollback_deployment_func = ctx.obj['core_functions'][9]
|
|
362
|
+
asyncio.run(rollback_deployment_func(deployment_id, 'production'))
|
|
363
|
+
except Exception as e:
|
|
364
|
+
if ctx.obj.get('verbose'):
|
|
365
|
+
logging.exception("Rollback deployment failed")
|
|
366
|
+
click.echo(f" Error: {str(e)}", err=True)
|
|
367
|
+
sys.exit(1)
|
|
368
|
+
|
|
369
|
+
@deployments.command()
|
|
370
|
+
@click.argument('deployment_id')
|
|
371
|
+
@click.option('--force', is_flag=True, help='Skip confirmation')
|
|
372
|
+
@click.pass_context
|
|
373
|
+
def delete(ctx, deployment_id, force):
|
|
374
|
+
"""Delete a deployment and its Lambda functions."""
|
|
375
|
+
try:
|
|
376
|
+
delete_deployment_func = ctx.obj['core_functions'][10]
|
|
377
|
+
asyncio.run(delete_deployment_func(deployment_id, force))
|
|
378
|
+
except Exception as e:
|
|
379
|
+
if ctx.obj.get('verbose'):
|
|
380
|
+
logging.exception("Delete deployment failed")
|
|
381
|
+
click.echo(f" Error: {str(e)}", err=True)
|
|
382
|
+
sys.exit(1)
|
|
383
|
+
|
|
384
|
+
# ======= Utility Commands =======
|
|
385
|
+
|
|
386
|
+
@cli.command()
|
|
387
|
+
def version():
|
|
388
|
+
"""Show version information."""
|
|
389
|
+
click.echo(f"Daita CLI v{__version__}")
|
|
390
|
+
click.echo("AI Agent Framework")
|
|
391
|
+
|
|
392
|
+
@cli.command()
|
|
393
|
+
def docs():
|
|
394
|
+
"""Open documentation in browser."""
|
|
395
|
+
import webbrowser
|
|
396
|
+
webbrowser.open("https://docs.daita-tech.io")
|
|
397
|
+
click.echo(" Opening documentation in browser...")
|
|
398
|
+
|
|
399
|
+
# ======= Main Entry Point =======
|
|
400
|
+
|
|
401
|
+
def main():
|
|
402
|
+
"""Main CLI entry point."""
|
|
403
|
+
try:
|
|
404
|
+
# Import freemium utilities
|
|
405
|
+
from .utils import is_cloud_command, require_api_key_for_cloud, show_local_vs_cloud_help
|
|
406
|
+
|
|
407
|
+
# Check current command (skip global flags like --verbose, --quiet, -v, -q)
|
|
408
|
+
current_cmd = None
|
|
409
|
+
for arg in sys.argv[1:]:
|
|
410
|
+
if not arg.startswith('-'):
|
|
411
|
+
current_cmd = arg
|
|
412
|
+
break
|
|
413
|
+
|
|
414
|
+
# Handle help command with freemium info
|
|
415
|
+
if current_cmd in ['--help', '-h', 'help']:
|
|
416
|
+
# Show ASCII art before help
|
|
417
|
+
from .ascii_art import display_compact_banner
|
|
418
|
+
display_compact_banner()
|
|
419
|
+
# Let click handle help, but add freemium info
|
|
420
|
+
try:
|
|
421
|
+
cli()
|
|
422
|
+
except SystemExit:
|
|
423
|
+
show_local_vs_cloud_help()
|
|
424
|
+
raise
|
|
425
|
+
else:
|
|
426
|
+
# Check for first-time usage only if not showing help
|
|
427
|
+
_check_first_time_usage()
|
|
428
|
+
|
|
429
|
+
# Enforce API key for cloud commands (but not for help)
|
|
430
|
+
if current_cmd and is_cloud_command(current_cmd) and '--help' not in sys.argv:
|
|
431
|
+
require_api_key_for_cloud()
|
|
432
|
+
|
|
433
|
+
# Check if we're in a project for commands that need it
|
|
434
|
+
project_commands = ['create', 'test', 'push', 'status', 'logs', 'deployments', 'run', 'executions', 'execution-logs']
|
|
435
|
+
|
|
436
|
+
if current_cmd in project_commands:
|
|
437
|
+
# Check if we're in a Daita project
|
|
438
|
+
current = Path.cwd()
|
|
439
|
+
in_project = False
|
|
440
|
+
for path in [current] + list(current.parents):
|
|
441
|
+
if (path / '.daita').exists():
|
|
442
|
+
in_project = True
|
|
443
|
+
break
|
|
444
|
+
|
|
445
|
+
if not in_project:
|
|
446
|
+
click.echo(" Not in a Daita project directory.", err=True)
|
|
447
|
+
click.echo(" Run 'daita init' to create a new project.")
|
|
448
|
+
sys.exit(1)
|
|
449
|
+
|
|
450
|
+
# Run the CLI
|
|
451
|
+
cli()
|
|
452
|
+
|
|
453
|
+
except Exception as e:
|
|
454
|
+
click.echo(f" Unexpected error: {str(e)}", err=True)
|
|
455
|
+
sys.exit(1)
|
|
456
|
+
|
|
457
|
+
# ===== REMOTE EXECUTION COMMANDS =====
|
|
458
|
+
|
|
459
|
+
@cli.command()
|
|
460
|
+
@click.argument('target_name')
|
|
461
|
+
@click.option('--type', 'target_type', default='agent',
|
|
462
|
+
type=click.Choice(['agent', 'workflow']),
|
|
463
|
+
help='Type of target to execute')
|
|
464
|
+
@click.option('--data', 'data_file',
|
|
465
|
+
help='JSON file containing input data')
|
|
466
|
+
@click.option('--data-json',
|
|
467
|
+
help='JSON string containing input data')
|
|
468
|
+
@click.option('--task', default='process',
|
|
469
|
+
help='Task to execute (for agents only)')
|
|
470
|
+
@click.option('--follow', '-f', is_flag=True,
|
|
471
|
+
help='Follow execution progress in real-time')
|
|
472
|
+
@click.option('--timeout', default=300, type=int,
|
|
473
|
+
help='Execution timeout in seconds')
|
|
474
|
+
@click.pass_context
|
|
475
|
+
def run(ctx, target_name, target_type, data_file, data_json, task, follow, timeout):
|
|
476
|
+
"""Execute an agent or workflow remotely in the cloud."""
|
|
477
|
+
environment = 'production' # Only production is supported
|
|
478
|
+
try:
|
|
479
|
+
run_remote_execution_func = ctx.obj['core_functions'][11]
|
|
480
|
+
success = asyncio.run(run_remote_execution_func(
|
|
481
|
+
target_name=target_name,
|
|
482
|
+
target_type=target_type,
|
|
483
|
+
environment=environment,
|
|
484
|
+
data_file=data_file,
|
|
485
|
+
data_json=data_json,
|
|
486
|
+
task=task,
|
|
487
|
+
follow=follow,
|
|
488
|
+
timeout=timeout,
|
|
489
|
+
verbose=ctx.obj.get('verbose', False)
|
|
490
|
+
))
|
|
491
|
+
if not success:
|
|
492
|
+
sys.exit(1)
|
|
493
|
+
except KeyboardInterrupt:
|
|
494
|
+
click.echo("\n Operation cancelled.", err=True)
|
|
495
|
+
sys.exit(1)
|
|
496
|
+
except Exception as e:
|
|
497
|
+
if ctx.obj.get('verbose'):
|
|
498
|
+
logging.exception("Run command failed")
|
|
499
|
+
click.echo(f" Error: {str(e)}", err=True)
|
|
500
|
+
sys.exit(1)
|
|
501
|
+
|
|
502
|
+
@cli.command('executions')
|
|
503
|
+
@click.option('--limit', default=10, type=int,
|
|
504
|
+
help='Number of executions to show')
|
|
505
|
+
@click.option('--status',
|
|
506
|
+
type=click.Choice(['queued', 'running', 'completed', 'failed', 'cancelled']),
|
|
507
|
+
help='Filter by execution status')
|
|
508
|
+
@click.option('--type', 'target_type',
|
|
509
|
+
type=click.Choice(['agent', 'workflow']),
|
|
510
|
+
help='Filter by target type')
|
|
511
|
+
@click.pass_context
|
|
512
|
+
def list_executions(ctx, limit, status, target_type):
|
|
513
|
+
"""List recent remote executions."""
|
|
514
|
+
try:
|
|
515
|
+
list_remote_executions_func = ctx.obj['core_functions'][12]
|
|
516
|
+
success = asyncio.run(list_remote_executions_func(
|
|
517
|
+
limit=limit,
|
|
518
|
+
status=status,
|
|
519
|
+
target_type=target_type,
|
|
520
|
+
environment='production', # Only production is supported
|
|
521
|
+
verbose=ctx.obj.get('verbose', False)
|
|
522
|
+
))
|
|
523
|
+
if not success:
|
|
524
|
+
sys.exit(1)
|
|
525
|
+
except Exception as e:
|
|
526
|
+
if ctx.obj.get('verbose'):
|
|
527
|
+
logging.exception("List executions command failed")
|
|
528
|
+
click.echo(f" Error: {str(e)}", err=True)
|
|
529
|
+
sys.exit(1)
|
|
530
|
+
|
|
531
|
+
@cli.command('execution-logs')
|
|
532
|
+
@click.argument('execution_id')
|
|
533
|
+
@click.option('--follow', '-f', is_flag=True,
|
|
534
|
+
help='Follow execution progress')
|
|
535
|
+
@click.pass_context
|
|
536
|
+
def execution_logs(ctx, execution_id, follow):
|
|
537
|
+
"""Get logs and status for a specific execution."""
|
|
538
|
+
try:
|
|
539
|
+
get_execution_logs_func = ctx.obj['core_functions'][13]
|
|
540
|
+
success = asyncio.run(get_execution_logs_func(
|
|
541
|
+
execution_id=execution_id,
|
|
542
|
+
follow=follow,
|
|
543
|
+
verbose=ctx.obj.get('verbose', False)
|
|
544
|
+
))
|
|
545
|
+
if not success:
|
|
546
|
+
sys.exit(1)
|
|
547
|
+
except KeyboardInterrupt:
|
|
548
|
+
if follow:
|
|
549
|
+
click.echo("\n Stopped following execution.", err=True)
|
|
550
|
+
sys.exit(1)
|
|
551
|
+
except Exception as e:
|
|
552
|
+
if ctx.obj.get('verbose'):
|
|
553
|
+
logging.exception("Execution logs command failed")
|
|
554
|
+
click.echo(f" Error: {str(e)}", err=True)
|
|
555
|
+
sys.exit(1)
|
|
556
|
+
|
|
557
|
+
# ======= Webhook Commands =======
|
|
558
|
+
|
|
559
|
+
@cli.group()
|
|
560
|
+
@click.pass_context
|
|
561
|
+
def webhook(ctx):
|
|
562
|
+
"""Webhook management commands."""
|
|
563
|
+
pass
|
|
564
|
+
|
|
565
|
+
@webhook.command('list')
|
|
566
|
+
@click.option('--api-key-only', is_flag=True,
|
|
567
|
+
help='Show only webhooks created with current API key')
|
|
568
|
+
@click.pass_context
|
|
569
|
+
def webhook_list(ctx, api_key_only):
|
|
570
|
+
"""List all webhook URLs for your organization."""
|
|
571
|
+
try:
|
|
572
|
+
# Import the webhook listing function
|
|
573
|
+
from .core.webhooks import list_webhooks
|
|
574
|
+
|
|
575
|
+
success = asyncio.run(list_webhooks(
|
|
576
|
+
api_key_only=api_key_only,
|
|
577
|
+
verbose=ctx.obj.get('verbose', False)
|
|
578
|
+
))
|
|
579
|
+
if not success:
|
|
580
|
+
sys.exit(1)
|
|
581
|
+
except Exception as e:
|
|
582
|
+
if ctx.obj.get('verbose'):
|
|
583
|
+
logging.exception("Webhook list command failed")
|
|
584
|
+
click.echo(f" Error: {str(e)}", err=True)
|
|
585
|
+
sys.exit(1)
|
|
586
|
+
|
|
587
|
+
if __name__ == "__main__":
|
|
588
|
+
main()
|