kailash 0.6.6__py3-none-any.whl → 0.7.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.
- kailash/__init__.py +35 -5
- kailash/adapters/__init__.py +5 -0
- kailash/adapters/mcp_platform_adapter.py +273 -0
- kailash/channels/__init__.py +21 -0
- kailash/channels/api_channel.py +409 -0
- kailash/channels/base.py +271 -0
- kailash/channels/cli_channel.py +661 -0
- kailash/channels/event_router.py +496 -0
- kailash/channels/mcp_channel.py +648 -0
- kailash/channels/session.py +423 -0
- kailash/mcp_server/discovery.py +1 -1
- kailash/middleware/mcp/enhanced_server.py +22 -16
- kailash/nexus/__init__.py +21 -0
- kailash/nexus/factory.py +413 -0
- kailash/nexus/gateway.py +545 -0
- kailash/nodes/__init__.py +2 -0
- kailash/nodes/ai/iterative_llm_agent.py +988 -17
- kailash/nodes/ai/llm_agent.py +29 -9
- kailash/nodes/api/__init__.py +2 -2
- kailash/nodes/api/monitoring.py +1 -1
- kailash/nodes/base_async.py +54 -14
- kailash/nodes/code/async_python.py +1 -1
- kailash/nodes/data/bulk_operations.py +939 -0
- kailash/nodes/data/query_builder.py +373 -0
- kailash/nodes/data/query_cache.py +512 -0
- kailash/nodes/monitoring/__init__.py +10 -0
- kailash/nodes/monitoring/deadlock_detector.py +964 -0
- kailash/nodes/monitoring/performance_anomaly.py +1078 -0
- kailash/nodes/monitoring/race_condition_detector.py +1151 -0
- kailash/nodes/monitoring/transaction_metrics.py +790 -0
- kailash/nodes/monitoring/transaction_monitor.py +931 -0
- kailash/nodes/system/__init__.py +17 -0
- kailash/nodes/system/command_parser.py +820 -0
- kailash/nodes/transaction/__init__.py +48 -0
- kailash/nodes/transaction/distributed_transaction_manager.py +983 -0
- kailash/nodes/transaction/saga_coordinator.py +652 -0
- kailash/nodes/transaction/saga_state_storage.py +411 -0
- kailash/nodes/transaction/saga_step.py +467 -0
- kailash/nodes/transaction/transaction_context.py +756 -0
- kailash/nodes/transaction/two_phase_commit.py +978 -0
- kailash/nodes/transform/processors.py +17 -1
- kailash/nodes/validation/__init__.py +21 -0
- kailash/nodes/validation/test_executor.py +532 -0
- kailash/nodes/validation/validation_nodes.py +447 -0
- kailash/resources/factory.py +1 -1
- kailash/runtime/async_local.py +84 -21
- kailash/runtime/local.py +21 -2
- kailash/runtime/parameter_injector.py +187 -31
- kailash/security.py +16 -1
- kailash/servers/__init__.py +32 -0
- kailash/servers/durable_workflow_server.py +430 -0
- kailash/servers/enterprise_workflow_server.py +466 -0
- kailash/servers/gateway.py +183 -0
- kailash/servers/workflow_server.py +290 -0
- kailash/utils/data_validation.py +192 -0
- kailash/workflow/builder.py +291 -12
- kailash/workflow/validation.py +144 -8
- {kailash-0.6.6.dist-info → kailash-0.7.0.dist-info}/METADATA +1 -1
- {kailash-0.6.6.dist-info → kailash-0.7.0.dist-info}/RECORD +63 -25
- {kailash-0.6.6.dist-info → kailash-0.7.0.dist-info}/WHEEL +0 -0
- {kailash-0.6.6.dist-info → kailash-0.7.0.dist-info}/entry_points.txt +0 -0
- {kailash-0.6.6.dist-info → kailash-0.7.0.dist-info}/licenses/LICENSE +0 -0
- {kailash-0.6.6.dist-info → kailash-0.7.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,661 @@
|
|
1
|
+
"""CLI Channel implementation for interactive command-line interface."""
|
2
|
+
|
3
|
+
import asyncio
|
4
|
+
import logging
|
5
|
+
import sys
|
6
|
+
from dataclasses import dataclass, field
|
7
|
+
from typing import Any, Callable, Dict, List, Optional, TextIO
|
8
|
+
|
9
|
+
from ..nodes.system.command_parser import (
|
10
|
+
CommandParserNode,
|
11
|
+
CommandRouterNode,
|
12
|
+
InteractiveShellNode,
|
13
|
+
ParsedCommand,
|
14
|
+
)
|
15
|
+
from ..runtime.local import LocalRuntime
|
16
|
+
from ..workflow.builder import WorkflowBuilder
|
17
|
+
from .base import (
|
18
|
+
Channel,
|
19
|
+
ChannelConfig,
|
20
|
+
ChannelEvent,
|
21
|
+
ChannelResponse,
|
22
|
+
ChannelStatus,
|
23
|
+
ChannelType,
|
24
|
+
)
|
25
|
+
|
26
|
+
logger = logging.getLogger(__name__)
|
27
|
+
|
28
|
+
|
29
|
+
@dataclass
|
30
|
+
class CLISession:
|
31
|
+
"""Represents a CLI session."""
|
32
|
+
|
33
|
+
session_id: str
|
34
|
+
user_id: Optional[str] = None
|
35
|
+
shell_state: Dict[str, Any] = field(default_factory=dict)
|
36
|
+
command_history: List[str] = field(default_factory=list)
|
37
|
+
active: bool = True
|
38
|
+
last_command_time: Optional[float] = None
|
39
|
+
|
40
|
+
|
41
|
+
class CLIChannel(Channel):
|
42
|
+
"""Command-line interface channel implementation.
|
43
|
+
|
44
|
+
This channel provides an interactive CLI interface for executing workflows
|
45
|
+
and managing the Kailash system through command-line commands.
|
46
|
+
"""
|
47
|
+
|
48
|
+
def __init__(
|
49
|
+
self,
|
50
|
+
config: ChannelConfig,
|
51
|
+
input_stream: Optional[TextIO] = None,
|
52
|
+
output_stream: Optional[TextIO] = None,
|
53
|
+
):
|
54
|
+
"""Initialize CLI channel.
|
55
|
+
|
56
|
+
Args:
|
57
|
+
config: Channel configuration
|
58
|
+
input_stream: Input stream (defaults to sys.stdin)
|
59
|
+
output_stream: Output stream (defaults to sys.stdout)
|
60
|
+
"""
|
61
|
+
super().__init__(config)
|
62
|
+
|
63
|
+
self.input_stream = input_stream or sys.stdin
|
64
|
+
self.output_stream = output_stream or sys.stdout
|
65
|
+
|
66
|
+
# CLI-specific components
|
67
|
+
self.command_parser = CommandParserNode()
|
68
|
+
self.shell_node = InteractiveShellNode()
|
69
|
+
self.router_node = CommandRouterNode()
|
70
|
+
|
71
|
+
# Session management
|
72
|
+
self._sessions: Dict[str, CLISession] = {}
|
73
|
+
self._current_session: Optional[CLISession] = None
|
74
|
+
|
75
|
+
# Command definitions and routing
|
76
|
+
self._command_definitions = self._setup_default_commands()
|
77
|
+
self._routing_config = self._setup_default_routing()
|
78
|
+
|
79
|
+
# Runtime for executing workflows
|
80
|
+
self.runtime = LocalRuntime()
|
81
|
+
|
82
|
+
# CLI state
|
83
|
+
self._running = False
|
84
|
+
self._main_task: Optional[asyncio.Task] = None
|
85
|
+
|
86
|
+
logger.info(f"Initialized CLI channel {self.name}")
|
87
|
+
|
88
|
+
def _setup_default_commands(self) -> Dict[str, Any]:
|
89
|
+
"""Set up default command definitions."""
|
90
|
+
return {
|
91
|
+
"run": {
|
92
|
+
"type": "workflow",
|
93
|
+
"description": "Execute a workflow",
|
94
|
+
"arguments": {
|
95
|
+
"workflow": {
|
96
|
+
"flags": ["workflow", "--workflow", "-w"],
|
97
|
+
"type": str,
|
98
|
+
"required": True,
|
99
|
+
"help": "Name of the workflow to execute",
|
100
|
+
},
|
101
|
+
"input": {
|
102
|
+
"flags": ["--input", "-i"],
|
103
|
+
"type": str,
|
104
|
+
"required": False,
|
105
|
+
"help": "JSON string of input parameters",
|
106
|
+
},
|
107
|
+
"file": {
|
108
|
+
"flags": ["--file", "-f"],
|
109
|
+
"type": str,
|
110
|
+
"required": False,
|
111
|
+
"help": "File path containing input parameters",
|
112
|
+
},
|
113
|
+
},
|
114
|
+
},
|
115
|
+
"list": {
|
116
|
+
"type": "system",
|
117
|
+
"description": "List available items",
|
118
|
+
"subcommands": {
|
119
|
+
"workflows": {
|
120
|
+
"help": "List available workflows",
|
121
|
+
"arguments": {
|
122
|
+
"verbose": {
|
123
|
+
"flags": ["--verbose", "-v"],
|
124
|
+
"action": "store_true",
|
125
|
+
"help": "Show detailed information",
|
126
|
+
}
|
127
|
+
},
|
128
|
+
},
|
129
|
+
"sessions": {"help": "List active sessions", "arguments": {}},
|
130
|
+
},
|
131
|
+
},
|
132
|
+
"status": {
|
133
|
+
"type": "system",
|
134
|
+
"description": "Show system status",
|
135
|
+
"arguments": {
|
136
|
+
"verbose": {
|
137
|
+
"flags": ["--verbose", "-v"],
|
138
|
+
"action": "store_true",
|
139
|
+
"help": "Show detailed status",
|
140
|
+
}
|
141
|
+
},
|
142
|
+
},
|
143
|
+
"config": {
|
144
|
+
"type": "admin",
|
145
|
+
"description": "Manage configuration",
|
146
|
+
"subcommands": {
|
147
|
+
"show": {"help": "Show current configuration", "arguments": {}},
|
148
|
+
"set": {
|
149
|
+
"help": "Set configuration value",
|
150
|
+
"arguments": {
|
151
|
+
"key": {
|
152
|
+
"flags": ["key"],
|
153
|
+
"type": str,
|
154
|
+
"required": True,
|
155
|
+
"help": "Configuration key",
|
156
|
+
},
|
157
|
+
"value": {
|
158
|
+
"flags": ["value"],
|
159
|
+
"type": str,
|
160
|
+
"required": True,
|
161
|
+
"help": "Configuration value",
|
162
|
+
},
|
163
|
+
},
|
164
|
+
},
|
165
|
+
},
|
166
|
+
},
|
167
|
+
}
|
168
|
+
|
169
|
+
def _setup_default_routing(self) -> Dict[str, Any]:
|
170
|
+
"""Set up default command routing configuration."""
|
171
|
+
return {
|
172
|
+
"run": {
|
173
|
+
"type": "workflow_executor",
|
174
|
+
"handler": "execute_workflow",
|
175
|
+
"description": "Execute workflow command",
|
176
|
+
},
|
177
|
+
"list:workflows": {
|
178
|
+
"type": "handler",
|
179
|
+
"handler": "list_workflows",
|
180
|
+
"description": "List available workflows",
|
181
|
+
},
|
182
|
+
"list:sessions": {
|
183
|
+
"type": "handler",
|
184
|
+
"handler": "list_sessions",
|
185
|
+
"description": "List active sessions",
|
186
|
+
},
|
187
|
+
"status": {
|
188
|
+
"type": "handler",
|
189
|
+
"handler": "show_status",
|
190
|
+
"description": "Show system status",
|
191
|
+
},
|
192
|
+
"config:show": {
|
193
|
+
"type": "handler",
|
194
|
+
"handler": "show_config",
|
195
|
+
"description": "Show configuration",
|
196
|
+
},
|
197
|
+
"config:set": {
|
198
|
+
"type": "handler",
|
199
|
+
"handler": "set_config",
|
200
|
+
"description": "Set configuration",
|
201
|
+
},
|
202
|
+
"help": {
|
203
|
+
"type": "handler",
|
204
|
+
"handler": "show_help",
|
205
|
+
"description": "Show help information",
|
206
|
+
},
|
207
|
+
"exit": {
|
208
|
+
"type": "handler",
|
209
|
+
"handler": "exit_cli",
|
210
|
+
"description": "Exit CLI",
|
211
|
+
},
|
212
|
+
"quit": {
|
213
|
+
"type": "handler",
|
214
|
+
"handler": "exit_cli",
|
215
|
+
"description": "Exit CLI",
|
216
|
+
},
|
217
|
+
"type:workflow": {
|
218
|
+
"type": "handler",
|
219
|
+
"handler": "handle_unknown_workflow",
|
220
|
+
"description": "Handle unknown workflow commands",
|
221
|
+
},
|
222
|
+
}
|
223
|
+
|
224
|
+
async def start(self) -> None:
|
225
|
+
"""Start the CLI channel."""
|
226
|
+
if self.status == ChannelStatus.RUNNING:
|
227
|
+
logger.warning(f"CLI channel {self.name} is already running")
|
228
|
+
return
|
229
|
+
|
230
|
+
try:
|
231
|
+
self.status = ChannelStatus.STARTING
|
232
|
+
self._setup_event_queue()
|
233
|
+
|
234
|
+
# Create default session
|
235
|
+
default_session = CLISession(session_id="default", user_id="cli_user")
|
236
|
+
self._sessions["default"] = default_session
|
237
|
+
self._current_session = default_session
|
238
|
+
|
239
|
+
self._running = True
|
240
|
+
|
241
|
+
# Start main CLI loop
|
242
|
+
self._main_task = asyncio.create_task(self._cli_loop())
|
243
|
+
|
244
|
+
self.status = ChannelStatus.RUNNING
|
245
|
+
|
246
|
+
# Emit startup event
|
247
|
+
await self.emit_event(
|
248
|
+
ChannelEvent(
|
249
|
+
event_id=f"cli_startup_{asyncio.get_event_loop().time()}",
|
250
|
+
channel_name=self.name,
|
251
|
+
channel_type=self.channel_type,
|
252
|
+
event_type="channel_started",
|
253
|
+
payload={"session_count": len(self._sessions)},
|
254
|
+
)
|
255
|
+
)
|
256
|
+
|
257
|
+
logger.info(f"CLI channel {self.name} started")
|
258
|
+
|
259
|
+
except Exception as e:
|
260
|
+
self.status = ChannelStatus.ERROR
|
261
|
+
logger.error(f"Failed to start CLI channel {self.name}: {e}")
|
262
|
+
raise
|
263
|
+
|
264
|
+
async def stop(self) -> None:
|
265
|
+
"""Stop the CLI channel."""
|
266
|
+
if self.status == ChannelStatus.STOPPED:
|
267
|
+
return
|
268
|
+
|
269
|
+
try:
|
270
|
+
self.status = ChannelStatus.STOPPING
|
271
|
+
self._running = False
|
272
|
+
|
273
|
+
# Emit shutdown event
|
274
|
+
await self.emit_event(
|
275
|
+
ChannelEvent(
|
276
|
+
event_id=f"cli_shutdown_{asyncio.get_event_loop().time()}",
|
277
|
+
channel_name=self.name,
|
278
|
+
channel_type=self.channel_type,
|
279
|
+
event_type="channel_stopping",
|
280
|
+
payload={},
|
281
|
+
)
|
282
|
+
)
|
283
|
+
|
284
|
+
# Cancel main task
|
285
|
+
if self._main_task and not self._main_task.done():
|
286
|
+
self._main_task.cancel()
|
287
|
+
try:
|
288
|
+
await self._main_task
|
289
|
+
except asyncio.CancelledError:
|
290
|
+
pass
|
291
|
+
|
292
|
+
await self._cleanup()
|
293
|
+
self.status = ChannelStatus.STOPPED
|
294
|
+
|
295
|
+
logger.info(f"CLI channel {self.name} stopped")
|
296
|
+
|
297
|
+
except Exception as e:
|
298
|
+
self.status = ChannelStatus.ERROR
|
299
|
+
logger.error(f"Error stopping CLI channel {self.name}: {e}")
|
300
|
+
raise
|
301
|
+
|
302
|
+
async def handle_request(self, request: Dict[str, Any]) -> ChannelResponse:
|
303
|
+
"""Handle a CLI request.
|
304
|
+
|
305
|
+
Args:
|
306
|
+
request: Request data containing command information
|
307
|
+
|
308
|
+
Returns:
|
309
|
+
ChannelResponse with command execution results
|
310
|
+
"""
|
311
|
+
try:
|
312
|
+
command_input = request.get("command", "")
|
313
|
+
session_id = request.get("session_id", "default")
|
314
|
+
|
315
|
+
# Get or create session
|
316
|
+
session = self._sessions.get(session_id)
|
317
|
+
if not session:
|
318
|
+
session = CLISession(session_id=session_id)
|
319
|
+
self._sessions[session_id] = session
|
320
|
+
|
321
|
+
# Process command through parsing pipeline
|
322
|
+
result = await self._process_command(command_input, session)
|
323
|
+
|
324
|
+
return ChannelResponse(
|
325
|
+
success=True,
|
326
|
+
data=result,
|
327
|
+
metadata={"channel": self.name, "session_id": session_id},
|
328
|
+
)
|
329
|
+
|
330
|
+
except Exception as e:
|
331
|
+
logger.error(f"Error handling CLI request: {e}")
|
332
|
+
return ChannelResponse(
|
333
|
+
success=False, error=str(e), metadata={"channel": self.name}
|
334
|
+
)
|
335
|
+
|
336
|
+
async def _cli_loop(self) -> None:
|
337
|
+
"""Main CLI interaction loop."""
|
338
|
+
self._write_output("Kailash CLI started. Type 'help' for available commands.\n")
|
339
|
+
|
340
|
+
while self._running:
|
341
|
+
try:
|
342
|
+
# Generate prompt
|
343
|
+
prompt = await self._generate_prompt()
|
344
|
+
self._write_output(prompt)
|
345
|
+
|
346
|
+
# Read command (this would be blocking in real implementation)
|
347
|
+
# For now, we'll simulate with a small delay
|
348
|
+
await asyncio.sleep(0.1)
|
349
|
+
|
350
|
+
# In a real implementation, this would read from input_stream
|
351
|
+
# For testing/simulation purposes, we'll break here
|
352
|
+
if not self.config.extra_config.get("interactive_mode", False):
|
353
|
+
break
|
354
|
+
|
355
|
+
except asyncio.CancelledError:
|
356
|
+
break
|
357
|
+
except Exception as e:
|
358
|
+
logger.error(f"Error in CLI loop: {e}")
|
359
|
+
self._write_output(f"Error: {e}\n")
|
360
|
+
|
361
|
+
async def _generate_prompt(self) -> str:
|
362
|
+
"""Generate CLI prompt."""
|
363
|
+
if not self._current_session:
|
364
|
+
return "kailash> "
|
365
|
+
|
366
|
+
# Use shell node to generate prompt
|
367
|
+
shell_result = self.shell_node.execute(
|
368
|
+
session_id=self._current_session.session_id,
|
369
|
+
command_input="", # Empty for prompt generation
|
370
|
+
session_state=self._current_session.shell_state,
|
371
|
+
prompt_template=self.config.extra_config.get(
|
372
|
+
"prompt_template", "kailash> "
|
373
|
+
),
|
374
|
+
)
|
375
|
+
|
376
|
+
return shell_result.get("prompt", "kailash> ")
|
377
|
+
|
378
|
+
async def _process_command(
|
379
|
+
self, command_input: str, session: CLISession
|
380
|
+
) -> Dict[str, Any]:
|
381
|
+
"""Process a command through the parsing and routing pipeline.
|
382
|
+
|
383
|
+
Args:
|
384
|
+
command_input: Raw command input
|
385
|
+
session: CLI session
|
386
|
+
|
387
|
+
Returns:
|
388
|
+
Command processing results
|
389
|
+
"""
|
390
|
+
try:
|
391
|
+
# Update session
|
392
|
+
session.command_history.append(command_input)
|
393
|
+
session.last_command_time = asyncio.get_event_loop().time()
|
394
|
+
|
395
|
+
# Parse command
|
396
|
+
parse_result = self.command_parser.execute(
|
397
|
+
command_input=command_input,
|
398
|
+
command_definitions=self._command_definitions,
|
399
|
+
allow_unknown_commands=True,
|
400
|
+
default_command_type="workflow",
|
401
|
+
)
|
402
|
+
|
403
|
+
if not parse_result["success"]:
|
404
|
+
return {
|
405
|
+
"type": "error",
|
406
|
+
"message": parse_result.get("error", "Failed to parse command"),
|
407
|
+
"command": command_input,
|
408
|
+
}
|
409
|
+
|
410
|
+
# Process through shell node
|
411
|
+
shell_result = self.shell_node.execute(
|
412
|
+
session_id=session.session_id,
|
413
|
+
command_input=command_input,
|
414
|
+
session_state=session.shell_state,
|
415
|
+
)
|
416
|
+
|
417
|
+
# Check if shell handled the command
|
418
|
+
if shell_result["shell_result"]["type"] != "passthrough":
|
419
|
+
return {
|
420
|
+
"type": "shell_command",
|
421
|
+
"result": shell_result["shell_result"],
|
422
|
+
"session_state": shell_result["session_state"],
|
423
|
+
}
|
424
|
+
|
425
|
+
# Route command
|
426
|
+
router_result = self.router_node.execute(
|
427
|
+
parsed_command=parse_result["parsed_command"],
|
428
|
+
routing_config=self._routing_config,
|
429
|
+
default_handler="help",
|
430
|
+
)
|
431
|
+
|
432
|
+
if not router_result["success"]:
|
433
|
+
return {
|
434
|
+
"type": "error",
|
435
|
+
"message": router_result.get("error", "Failed to route command"),
|
436
|
+
"command": command_input,
|
437
|
+
}
|
438
|
+
|
439
|
+
# Execute routed command
|
440
|
+
execution_result = await self._execute_routed_command(
|
441
|
+
router_result["routing_target"],
|
442
|
+
router_result["execution_params"],
|
443
|
+
session,
|
444
|
+
)
|
445
|
+
|
446
|
+
return {
|
447
|
+
"type": "command_execution",
|
448
|
+
"parse_result": parse_result,
|
449
|
+
"routing_result": router_result,
|
450
|
+
"execution_result": execution_result,
|
451
|
+
"session_id": session.session_id,
|
452
|
+
}
|
453
|
+
|
454
|
+
except Exception as e:
|
455
|
+
logger.error(f"Error processing command: {e}")
|
456
|
+
return {"type": "error", "message": str(e), "command": command_input}
|
457
|
+
|
458
|
+
async def _execute_routed_command(
|
459
|
+
self,
|
460
|
+
routing_target: Dict[str, Any],
|
461
|
+
execution_params: Dict[str, Any],
|
462
|
+
session: CLISession,
|
463
|
+
) -> Dict[str, Any]:
|
464
|
+
"""Execute a routed command.
|
465
|
+
|
466
|
+
Args:
|
467
|
+
routing_target: Routing target information
|
468
|
+
execution_params: Execution parameters
|
469
|
+
session: CLI session
|
470
|
+
|
471
|
+
Returns:
|
472
|
+
Execution results
|
473
|
+
"""
|
474
|
+
handler_name = routing_target.get("handler")
|
475
|
+
target_type = routing_target.get("type")
|
476
|
+
|
477
|
+
try:
|
478
|
+
if target_type == "workflow_executor":
|
479
|
+
return await self._execute_workflow_command(execution_params)
|
480
|
+
elif target_type == "handler":
|
481
|
+
return await self._execute_handler_command(
|
482
|
+
handler_name, execution_params, session
|
483
|
+
)
|
484
|
+
else:
|
485
|
+
return {
|
486
|
+
"success": False,
|
487
|
+
"error": f"Unknown target type: {target_type}",
|
488
|
+
}
|
489
|
+
|
490
|
+
except Exception as e:
|
491
|
+
logger.error(f"Error executing routed command: {e}")
|
492
|
+
return {"success": False, "error": str(e)}
|
493
|
+
|
494
|
+
async def _execute_workflow_command(
|
495
|
+
self, execution_params: Dict[str, Any]
|
496
|
+
) -> Dict[str, Any]:
|
497
|
+
"""Execute a workflow command.
|
498
|
+
|
499
|
+
Args:
|
500
|
+
execution_params: Execution parameters
|
501
|
+
|
502
|
+
Returns:
|
503
|
+
Workflow execution results
|
504
|
+
"""
|
505
|
+
# This would integrate with the workflow execution system
|
506
|
+
# For now, return a placeholder
|
507
|
+
return {
|
508
|
+
"success": True,
|
509
|
+
"message": "Workflow execution not yet implemented in CLI channel",
|
510
|
+
"params": execution_params,
|
511
|
+
}
|
512
|
+
|
513
|
+
async def _execute_handler_command(
|
514
|
+
self, handler_name: str, execution_params: Dict[str, Any], session: CLISession
|
515
|
+
) -> Dict[str, Any]:
|
516
|
+
"""Execute a handler command.
|
517
|
+
|
518
|
+
Args:
|
519
|
+
handler_name: Name of the handler
|
520
|
+
execution_params: Execution parameters
|
521
|
+
session: CLI session
|
522
|
+
|
523
|
+
Returns:
|
524
|
+
Handler execution results
|
525
|
+
"""
|
526
|
+
if handler_name == "show_help":
|
527
|
+
return await self._handle_help(execution_params)
|
528
|
+
elif handler_name == "show_status":
|
529
|
+
return await self._handle_status(execution_params)
|
530
|
+
elif handler_name == "list_workflows":
|
531
|
+
return await self._handle_list_workflows(execution_params)
|
532
|
+
elif handler_name == "list_sessions":
|
533
|
+
return await self._handle_list_sessions(execution_params)
|
534
|
+
elif handler_name == "exit_cli":
|
535
|
+
return await self._handle_exit(execution_params, session)
|
536
|
+
else:
|
537
|
+
return {"success": False, "error": f"Unknown handler: {handler_name}"}
|
538
|
+
|
539
|
+
async def _handle_help(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
540
|
+
"""Handle help command."""
|
541
|
+
topic = params.get("command_arguments", {}).get("topic")
|
542
|
+
|
543
|
+
if topic and topic in self._command_definitions:
|
544
|
+
cmd_def = self._command_definitions[topic]
|
545
|
+
help_text = f"{topic}: {cmd_def.get('description', 'No description')}\n"
|
546
|
+
|
547
|
+
# Add argument information
|
548
|
+
arguments = cmd_def.get("arguments", {})
|
549
|
+
if arguments:
|
550
|
+
help_text += "Arguments:\n"
|
551
|
+
for arg_name, arg_config in arguments.items():
|
552
|
+
flags = ", ".join(arg_config.get("flags", [f"--{arg_name}"]))
|
553
|
+
help_desc = arg_config.get("help", "No description")
|
554
|
+
help_text += f" {flags}: {help_desc}\n"
|
555
|
+
|
556
|
+
# Add subcommands
|
557
|
+
subcommands = cmd_def.get("subcommands", {})
|
558
|
+
if subcommands:
|
559
|
+
help_text += "Subcommands:\n"
|
560
|
+
for sub_name, sub_config in subcommands.items():
|
561
|
+
help_desc = sub_config.get("help", "No description")
|
562
|
+
help_text += f" {sub_name}: {help_desc}\n"
|
563
|
+
else:
|
564
|
+
# General help
|
565
|
+
help_text = "Available commands:\n"
|
566
|
+
for cmd_name, cmd_def in self._command_definitions.items():
|
567
|
+
description = cmd_def.get("description", "No description")
|
568
|
+
help_text += f" {cmd_name}: {description}\n"
|
569
|
+
|
570
|
+
help_text += "\nType 'help <command>' for detailed information about a specific command.\n"
|
571
|
+
|
572
|
+
return {"success": True, "message": help_text.strip()}
|
573
|
+
|
574
|
+
async def _handle_status(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
575
|
+
"""Handle status command."""
|
576
|
+
verbose = params.get("command_arguments", {}).get("verbose", False)
|
577
|
+
|
578
|
+
status_info = {
|
579
|
+
"channel": self.name,
|
580
|
+
"status": self.status.value,
|
581
|
+
"sessions": len(self._sessions),
|
582
|
+
"active_sessions": len([s for s in self._sessions.values() if s.active]),
|
583
|
+
}
|
584
|
+
|
585
|
+
if verbose:
|
586
|
+
status_info.update(await self.get_status())
|
587
|
+
|
588
|
+
return {"success": True, "data": status_info}
|
589
|
+
|
590
|
+
async def _handle_list_workflows(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
591
|
+
"""Handle list workflows command."""
|
592
|
+
# This would integrate with workflow registry
|
593
|
+
return {
|
594
|
+
"success": True,
|
595
|
+
"message": "Workflow listing not yet implemented",
|
596
|
+
"workflows": [],
|
597
|
+
}
|
598
|
+
|
599
|
+
async def _handle_list_sessions(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
600
|
+
"""Handle list sessions command."""
|
601
|
+
sessions_info = []
|
602
|
+
for session_id, session in self._sessions.items():
|
603
|
+
sessions_info.append(
|
604
|
+
{
|
605
|
+
"session_id": session_id,
|
606
|
+
"user_id": session.user_id,
|
607
|
+
"active": session.active,
|
608
|
+
"command_count": len(session.command_history),
|
609
|
+
"last_command_time": session.last_command_time,
|
610
|
+
}
|
611
|
+
)
|
612
|
+
|
613
|
+
return {"success": True, "data": {"sessions": sessions_info}}
|
614
|
+
|
615
|
+
async def _handle_exit(
|
616
|
+
self, params: Dict[str, Any], session: CLISession
|
617
|
+
) -> Dict[str, Any]:
|
618
|
+
"""Handle exit command."""
|
619
|
+
force = params.get("command_arguments", {}).get("force", False)
|
620
|
+
|
621
|
+
if force or len(self._sessions) == 1:
|
622
|
+
# Stop the entire CLI
|
623
|
+
asyncio.create_task(self.stop())
|
624
|
+
return {"success": True, "message": "Exiting CLI..."}
|
625
|
+
else:
|
626
|
+
# Just deactivate current session
|
627
|
+
session.active = False
|
628
|
+
return {
|
629
|
+
"success": True,
|
630
|
+
"message": f"Session {session.session_id} deactivated",
|
631
|
+
}
|
632
|
+
|
633
|
+
def _write_output(self, text: str) -> None:
|
634
|
+
"""Write text to output stream."""
|
635
|
+
if self.output_stream:
|
636
|
+
self.output_stream.write(text)
|
637
|
+
self.output_stream.flush()
|
638
|
+
|
639
|
+
async def health_check(self) -> Dict[str, Any]:
|
640
|
+
"""Perform comprehensive health check."""
|
641
|
+
base_health = await super().health_check()
|
642
|
+
|
643
|
+
# Add CLI-specific health checks
|
644
|
+
cli_checks = {
|
645
|
+
"sessions_active": len([s for s in self._sessions.values() if s.active])
|
646
|
+
> 0,
|
647
|
+
"command_parser_ready": self.command_parser is not None,
|
648
|
+
"routing_configured": len(self._routing_config) > 0,
|
649
|
+
"runtime_available": self.runtime is not None,
|
650
|
+
}
|
651
|
+
|
652
|
+
all_healthy = base_health["healthy"] and all(cli_checks.values())
|
653
|
+
|
654
|
+
return {
|
655
|
+
**base_health,
|
656
|
+
"healthy": all_healthy,
|
657
|
+
"checks": {**base_health["checks"], **cli_checks},
|
658
|
+
"sessions": len(self._sessions),
|
659
|
+
"active_sessions": len([s for s in self._sessions.values() if s.active]),
|
660
|
+
"commands_available": len(self._command_definitions),
|
661
|
+
}
|