talkops-opscloud 0.3.0b1__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.
- opscloud/__init__.py +5 -0
- opscloud/__main__.py +6 -0
- opscloud/_constants.py +53 -0
- opscloud/_debug.py +145 -0
- opscloud/_debug_buffer.py +250 -0
- opscloud/_textual_patches.py +367 -0
- opscloud/_version.py +15 -0
- opscloud/agent/__init__.py +27 -0
- opscloud/agent/config.py +168 -0
- opscloud/agent/factory.py +1103 -0
- opscloud/approval_mode.py +44 -0
- opscloud/backend/__init__.py +49 -0
- opscloud/backend/composite.py +89 -0
- opscloud/backend/local.py +183 -0
- opscloud/backend/registry.py +79 -0
- opscloud/backend/sandbox_config.py +107 -0
- opscloud/backend/sandbox_factory.py +432 -0
- opscloud/built_in_skills/remember/SKILL.md +124 -0
- opscloud/built_in_skills/skill-creator/SKILL.md +348 -0
- opscloud/built_in_skills/skill-creator/scripts/init_skill.py +371 -0
- opscloud/built_in_skills/skill-creator/scripts/quick_validate.py +165 -0
- opscloud/cli/__init__.py +1 -0
- opscloud/cli/commands/__init__.py +15 -0
- opscloud/cli/commands/auth.py +45 -0
- opscloud/cli/commands/doctor.py +100 -0
- opscloud/cli/commands/mcp.py +137 -0
- opscloud/cli/commands/threads.py +51 -0
- opscloud/cli/interactive.py +154 -0
- opscloud/cli/main.py +903 -0
- opscloud/cli/non_interactive.py +425 -0
- opscloud/cli/server_manager.py +58 -0
- opscloud/commands/__init__.py +19 -0
- opscloud/commands/_base.py +110 -0
- opscloud/commands/_guards.py +52 -0
- opscloud/commands/_router.py +146 -0
- opscloud/commands/_types.py +36 -0
- opscloud/commands/core/__init__.py +47 -0
- opscloud/commands/core/auth.py +94 -0
- opscloud/commands/core/bug.py +46 -0
- opscloud/commands/core/clear.py +106 -0
- opscloud/commands/core/cloud.py +185 -0
- opscloud/commands/core/compact.py +128 -0
- opscloud/commands/core/config_cmd.py +120 -0
- opscloud/commands/core/context.py +220 -0
- opscloud/commands/core/cost.py +44 -0
- opscloud/commands/core/doctor.py +535 -0
- opscloud/commands/core/effort.py +108 -0
- opscloud/commands/core/exit_cmd.py +41 -0
- opscloud/commands/core/fast.py +148 -0
- opscloud/commands/core/help_cmd.py +94 -0
- opscloud/commands/core/mcp.py +134 -0
- opscloud/commands/core/model.py +109 -0
- opscloud/commands/core/permissions.py +196 -0
- opscloud/commands/core/plugins.py +92 -0
- opscloud/commands/core/pool.py +108 -0
- opscloud/commands/core/resume.py +97 -0
- opscloud/commands/core/skills.py +178 -0
- opscloud/commands/power/__init__.py +51 -0
- opscloud/commands/power/agents.py +98 -0
- opscloud/commands/power/btw.py +85 -0
- opscloud/commands/power/copy.py +97 -0
- opscloud/commands/power/goal.py +548 -0
- opscloud/commands/power/loop.py +295 -0
- opscloud/commands/power/memory.py +287 -0
- opscloud/commands/power/review.py +152 -0
- opscloud/commands/power/rubric.py +287 -0
- opscloud/commands/power/runtime.py +321 -0
- opscloud/commands/power/skill_creator.py +70 -0
- opscloud/commands/power/skill_invoke.py +165 -0
- opscloud/commands/power/tasks.py +209 -0
- opscloud/commands/power/trace.py +117 -0
- opscloud/commands/power/ui_toggles.py +135 -0
- opscloud/commands/power/version.py +52 -0
- opscloud/commands/registry.py +176 -0
- opscloud/config/__init__.py +114 -0
- opscloud/config/adapters/__init__.py +5 -0
- opscloud/config/adapters/postgres.py +776 -0
- opscloud/config/adapters/sqlite.py +769 -0
- opscloud/config/aws.py +248 -0
- opscloud/config/cloud_profiles.py +437 -0
- opscloud/config/env_vars.py +81 -0
- opscloud/config/langsmith.py +319 -0
- opscloud/config/manifest.py +817 -0
- opscloud/config/metadata.py +205 -0
- opscloud/config/paths.py +604 -0
- opscloud/config/plugins.py +130 -0
- opscloud/config/settings.py +834 -0
- opscloud/config/store.py +453 -0
- opscloud/config/store_factory.py +97 -0
- opscloud/config/toml_config.py +562 -0
- opscloud/editor.py +173 -0
- opscloud/exceptions.py +65 -0
- opscloud/file_ops.py +217 -0
- opscloud/hooks/__init__.py +139 -0
- opscloud/hooks/env.py +44 -0
- opscloud/hooks/interrupt.py +121 -0
- opscloud/hooks/models/__init__.py +109 -0
- opscloud/hooks/models/adapters.py +19 -0
- opscloud/hooks/models/config.py +79 -0
- opscloud/hooks/models/domain.py +434 -0
- opscloud/hooks/models/transport.py +37 -0
- opscloud/hooks/tools.py +231 -0
- opscloud/input.py +48 -0
- opscloud/integrations/__init__.py +80 -0
- opscloud/integrations/base.py +153 -0
- opscloud/integrations/content_filter.py +119 -0
- opscloud/integrations/event_bus.py +480 -0
- opscloud/integrations/hooks.py +238 -0
- opscloud/integrations/identity.py +181 -0
- opscloud/integrations/notifications.py +134 -0
- opscloud/integrations/stream_bridge.py +397 -0
- opscloud/integrations/thread_store.py +81 -0
- opscloud/json_types.py +20 -0
- opscloud/mcp/__init__.py +57 -0
- opscloud/mcp/config.py +203 -0
- opscloud/mcp/discovery.py +229 -0
- opscloud/mcp/mcp_info.py +109 -0
- opscloud/mcp/middleware.py +102 -0
- opscloud/mcp/preload.py +505 -0
- opscloud/mcp/raw_config.py +149 -0
- opscloud/mcp/semantic_profiler.py +305 -0
- opscloud/mcp/session_manager.py +727 -0
- opscloud/mcp/trust.py +105 -0
- opscloud/memory/__init__.py +27 -0
- opscloud/memory/branch.py +61 -0
- opscloud/memory/guard.py +19 -0
- opscloud/memory/onboarding.py +120 -0
- opscloud/memory/registry.py +134 -0
- opscloud/memory/store.py +223 -0
- opscloud/middleware/__init__.py +106 -0
- opscloud/middleware/_repository_bounds.py +459 -0
- opscloud/middleware/ask_user.py +152 -0
- opscloud/middleware/auto_mode.py +60 -0
- opscloud/middleware/auto_mode_hitl.py +2434 -0
- opscloud/middleware/compaction.py +566 -0
- opscloud/middleware/configurable_model.py +233 -0
- opscloud/middleware/cost_tracking.py +1021 -0
- opscloud/middleware/glm_stall_recovery.py +109 -0
- opscloud/middleware/goal_criteria.py +1234 -0
- opscloud/middleware/goal_state_notice.py +712 -0
- opscloud/middleware/goal_tools.py +407 -0
- opscloud/middleware/headless_mcp_guard.py +88 -0
- opscloud/middleware/jev_model_router.py +1018 -0
- opscloud/middleware/local_context.py +460 -0
- opscloud/middleware/mcp_context.py +151 -0
- opscloud/middleware/mcp_middleware.py +146 -0
- opscloud/middleware/memory_guard.py +393 -0
- opscloud/middleware/model_retry.py +205 -0
- opscloud/middleware/registry.py +137 -0
- opscloud/middleware/reliable_rubric.py +709 -0
- opscloud/middleware/resume_state.py +91 -0
- opscloud/middleware/server_hooks.py +1189 -0
- opscloud/middleware/shell_allow_list.py +90 -0
- opscloud/middleware/skills.py +382 -0
- opscloud/middleware/subagent_artifacts.py +84 -0
- opscloud/middleware/subagent_telemetry.py +261 -0
- opscloud/middleware/subagents.py +243 -0
- opscloud/middleware/tool_filter.py +384 -0
- opscloud/middleware/unified_system_message.py +62 -0
- opscloud/model/__init__.py +37 -0
- opscloud/model/config.py +2992 -0
- opscloud/model/factory.py +477 -0
- opscloud/model/pool.py +459 -0
- opscloud/model/reasoning.py +282 -0
- opscloud/offload.py +173 -0
- opscloud/output.py +64 -0
- opscloud/plugins/__init__.py +189 -0
- opscloud/plugins/_json.py +22 -0
- opscloud/plugins/adapters/__init__.py +13 -0
- opscloud/plugins/adapters/agents.py +112 -0
- opscloud/plugins/adapters/commands.py +190 -0
- opscloud/plugins/adapters/mcp.py +363 -0
- opscloud/plugins/adapters/skills.py +132 -0
- opscloud/plugins/commands_cli.py +541 -0
- opscloud/plugins/discovery.py +512 -0
- opscloud/plugins/manifest.py +335 -0
- opscloud/plugins/marketplace.py +893 -0
- opscloud/plugins/models.py +334 -0
- opscloud/plugins/project_plugins.py +317 -0
- opscloud/plugins/protocol.py +31 -0
- opscloud/plugins/store.py +905 -0
- opscloud/plugins/substitution.py +122 -0
- opscloud/project_utils.py +145 -0
- opscloud/prompts/__init__.py +37 -0
- opscloud/prompts/system.py +483 -0
- opscloud/prompts/templates/system_prompt.md +166 -0
- opscloud/prompts/types.py +54 -0
- opscloud/rubrics/__init__.py +42 -0
- opscloud/rubrics/evaluator.py +404 -0
- opscloud/rubrics/evidence_extractor.py +229 -0
- opscloud/rubrics/generator.py +239 -0
- opscloud/rubrics/jev_compiler.py +175 -0
- opscloud/rubrics/jev_grader.py +297 -0
- opscloud/rubrics/middleware.py +8 -0
- opscloud/schema/__init__.py +107 -0
- opscloud/schema/interrupts.py +981 -0
- opscloud/security/__init__.py +91 -0
- opscloud/security/approval_mode.py +528 -0
- opscloud/security/approval_mode_source.py +230 -0
- opscloud/security/cli_ast_evaluator.py +552 -0
- opscloud/security/jev_classifier.py +651 -0
- opscloud/security/shell_safety.py +340 -0
- opscloud/security/unicode_security.py +439 -0
- opscloud/security/url_validation.py +160 -0
- opscloud/server/__init__.py +13 -0
- opscloud/server/_server_config.py +158 -0
- opscloud/server/server.py +242 -0
- opscloud/server/server_graph.py +93 -0
- opscloud/skills/__init__.py +91 -0
- opscloud/skills/commands.py +1099 -0
- opscloud/skills/invocation.py +200 -0
- opscloud/skills/load.py +388 -0
- opscloud/skills/loader.py +23 -0
- opscloud/skills/merge.py +65 -0
- opscloud/skills/registry.py +263 -0
- opscloud/skills/sources.py +174 -0
- opscloud/skills/trust.py +417 -0
- opscloud/state/__init__.py +142 -0
- opscloud/state/base.py +154 -0
- opscloud/state/cloud_context.py +262 -0
- opscloud/state/goal_channels.py +124 -0
- opscloud/state/goal_state_limits.py +175 -0
- opscloud/state/resume_state.py +58 -0
- opscloud/state/service_context.py +281 -0
- opscloud/state/session.py +957 -0
- opscloud/state/state_migration.py +86 -0
- opscloud/subagents/__init__.py +30 -0
- opscloud/subagents/loader.py +331 -0
- opscloud/subagents/subagents_parser.py +117 -0
- opscloud/subagents/types.py +25 -0
- opscloud/tools/__init__.py +36 -0
- opscloud/tools/catalog.py +37 -0
- opscloud/tools/display.py +24 -0
- opscloud/tools/fetch_url.py +176 -0
- opscloud/tools/goal_tools.py +423 -0
- opscloud/tools/registry.py +105 -0
- opscloud/tools/thread.py +32 -0
- opscloud/tools/web_search.py +215 -0
- opscloud/ui/__init__.py +119 -0
- opscloud/ui/app.py +5400 -0
- opscloud/ui/app.tcss +479 -0
- opscloud/ui/clipboard.py +147 -0
- opscloud/ui/command_registry.py +482 -0
- opscloud/ui/message_store.py +152 -0
- opscloud/ui/permission_store.py +360 -0
- opscloud/ui/preferences.py +113 -0
- opscloud/ui/prompt_manager.py +96 -0
- opscloud/ui/remote_client.py +505 -0
- opscloud/ui/textual_adapter.py +1269 -0
- opscloud/ui/theme.py +728 -0
- opscloud/ui/ui_help.py +415 -0
- opscloud/ui/widgets/__init__.py +383 -0
- opscloud/ui/widgets/_ask_user_types.py +81 -0
- opscloud/ui/widgets/_copy_spans.py +52 -0
- opscloud/ui/widgets/_inline_prompt.py +290 -0
- opscloud/ui/widgets/_js_eval_display.py +139 -0
- opscloud/ui/widgets/_links.py +175 -0
- opscloud/ui/widgets/_tool_stream.py +308 -0
- opscloud/ui/widgets/agent_selector.py +297 -0
- opscloud/ui/widgets/approval.py +741 -0
- opscloud/ui/widgets/ask_user.py +539 -0
- opscloud/ui/widgets/auth.py +695 -0
- opscloud/ui/widgets/auth_manager.py +214 -0
- opscloud/ui/widgets/auto_mode_notice.py +146 -0
- opscloud/ui/widgets/autocomplete.py +709 -0
- opscloud/ui/widgets/chat_input.py +805 -0
- opscloud/ui/widgets/cloud_selector.py +528 -0
- opscloud/ui/widgets/config_manager.py +652 -0
- opscloud/ui/widgets/debug_console.py +1269 -0
- opscloud/ui/widgets/devops_renderers.py +184 -0
- opscloud/ui/widgets/diff.py +237 -0
- opscloud/ui/widgets/effort_selector.py +166 -0
- opscloud/ui/widgets/goal_review.py +612 -0
- opscloud/ui/widgets/goal_status.py +57 -0
- opscloud/ui/widgets/infra_panel.py +141 -0
- opscloud/ui/widgets/install_confirm.py +114 -0
- opscloud/ui/widgets/loading.py +170 -0
- opscloud/ui/widgets/mcp_viewer.py +1204 -0
- opscloud/ui/widgets/messages.py +4461 -0
- opscloud/ui/widgets/model_selector.py +863 -0
- opscloud/ui/widgets/notification_center.py +108 -0
- opscloud/ui/widgets/notification_settings.py +119 -0
- opscloud/ui/widgets/operation_card.py +106 -0
- opscloud/ui/widgets/permissions_manager.py +667 -0
- opscloud/ui/widgets/plugin_manager.py +1212 -0
- opscloud/ui/widgets/pool_selector.py +376 -0
- opscloud/ui/widgets/preamble.py +53 -0
- opscloud/ui/widgets/skills_viewer.py +432 -0
- opscloud/ui/widgets/status.py +1037 -0
- opscloud/ui/widgets/subagent_panel.py +927 -0
- opscloud/ui/widgets/theme_selector.py +232 -0
- opscloud/ui/widgets/thread_selector.py +358 -0
- opscloud/ui/widgets/toast.py +31 -0
- opscloud/ui/widgets/tool_display.py +325 -0
- opscloud/ui/widgets/tool_renderers.py +234 -0
- opscloud/ui/widgets/tool_widgets.py +278 -0
- opscloud/ui/widgets/welcome.py +421 -0
- opscloud/ui/widgets/welcome_popup.py +102 -0
- opscloud/utils/__init__.py +24 -0
- opscloud/utils/cost_estimation.py +169 -0
- opscloud/utils/git.py +390 -0
- opscloud/utils/logger.py +571 -0
- opscloud/utils/session_stats.py +115 -0
- opscloud/utils/startup_error.py +37 -0
- talkops_opscloud-0.3.0b1.dist-info/METADATA +449 -0
- talkops_opscloud-0.3.0b1.dist-info/RECORD +309 -0
- talkops_opscloud-0.3.0b1.dist-info/WHEEL +4 -0
- talkops_opscloud-0.3.0b1.dist-info/entry_points.txt +2 -0
- talkops_opscloud-0.3.0b1.dist-info/licenses/LICENSE +175 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"""Doctor command verifying AWS credentials, tools, and environment."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import shutil
|
|
6
|
+
import sys
|
|
7
|
+
|
|
8
|
+
from rich.console import Console
|
|
9
|
+
from rich.table import Table
|
|
10
|
+
|
|
11
|
+
from opscloud.config.aws import get_aws_context
|
|
12
|
+
from opscloud.output import OutputFormat, write_json
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def run_doctor_command(output_format: OutputFormat = "text") -> int:
|
|
16
|
+
aws_ctx = get_aws_context()
|
|
17
|
+
tools = ["aws", "kubectl", "terraform", "helm", "docker", "git"]
|
|
18
|
+
tool_statuses: dict[str, str | None] = {}
|
|
19
|
+
for tool_name in tools:
|
|
20
|
+
tool_statuses[tool_name] = shutil.which(tool_name)
|
|
21
|
+
|
|
22
|
+
py_ver = f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}"
|
|
23
|
+
|
|
24
|
+
from opscloud.utils.logger import (
|
|
25
|
+
get_active_log_dir,
|
|
26
|
+
get_active_log_file,
|
|
27
|
+
get_configured_log_level,
|
|
28
|
+
is_console_logging_enabled,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
log_dir = str(get_active_log_dir())
|
|
32
|
+
log_file = str(get_active_log_file())
|
|
33
|
+
log_level = get_configured_log_level()
|
|
34
|
+
console_logging = is_console_logging_enabled()
|
|
35
|
+
|
|
36
|
+
if output_format == "json":
|
|
37
|
+
write_json(
|
|
38
|
+
"doctor",
|
|
39
|
+
{
|
|
40
|
+
"aws_sts": {
|
|
41
|
+
"authenticated": aws_ctx.is_authenticated,
|
|
42
|
+
"account_id": aws_ctx.account_id,
|
|
43
|
+
"region": aws_ctx.region,
|
|
44
|
+
"profile": aws_ctx.profile,
|
|
45
|
+
"error": aws_ctx.error,
|
|
46
|
+
},
|
|
47
|
+
"tools": tool_statuses,
|
|
48
|
+
"python": py_ver,
|
|
49
|
+
"logging": {
|
|
50
|
+
"directory": log_dir,
|
|
51
|
+
"file": log_file,
|
|
52
|
+
"level": log_level,
|
|
53
|
+
"console_enabled": console_logging,
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
)
|
|
57
|
+
return 0
|
|
58
|
+
|
|
59
|
+
console = Console()
|
|
60
|
+
console.print("\n[bold yellow]OpsCloud System & Cloud Environment Doctor[/bold yellow]\n")
|
|
61
|
+
|
|
62
|
+
table = Table(title="Environment Diagnostics", border_style="cyan")
|
|
63
|
+
table.add_column("Component", style="bold")
|
|
64
|
+
table.add_column("Status")
|
|
65
|
+
table.add_column("Details")
|
|
66
|
+
|
|
67
|
+
# 1. AWS Credentials
|
|
68
|
+
if aws_ctx.is_authenticated:
|
|
69
|
+
table.add_row(
|
|
70
|
+
"AWS STS Identity",
|
|
71
|
+
"[green]OK[/green]",
|
|
72
|
+
f"Account: {aws_ctx.account_id}, Region: {aws_ctx.region}, Profile: {aws_ctx.profile}",
|
|
73
|
+
)
|
|
74
|
+
else:
|
|
75
|
+
table.add_row(
|
|
76
|
+
"AWS STS Identity",
|
|
77
|
+
"[yellow]Unauthenticated[/yellow]",
|
|
78
|
+
aws_ctx.error or "No active AWS credentials found via STS",
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
# 2. Tooling
|
|
82
|
+
for tool_name, path in tool_statuses.items():
|
|
83
|
+
if path:
|
|
84
|
+
table.add_row(f"CLI: {tool_name}", "[green]Found[/green]", path)
|
|
85
|
+
else:
|
|
86
|
+
table.add_row(f"CLI: {tool_name}", "[dim]Not installed[/dim]", "Optional")
|
|
87
|
+
|
|
88
|
+
# 3. Python version
|
|
89
|
+
table.add_row("Python Runtime", "[green]OK[/green]", f"Python {py_ver}")
|
|
90
|
+
|
|
91
|
+
# 4. Logging subsystem
|
|
92
|
+
table.add_row(
|
|
93
|
+
"Log Destination",
|
|
94
|
+
"[green]Active[/green]",
|
|
95
|
+
f"{log_file} (level={log_level}, console={'enabled' if console_logging else 'silent'})",
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
console.print(table)
|
|
99
|
+
console.print()
|
|
100
|
+
return 0
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"""MCP management CLI command for opscloud."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import asyncio
|
|
7
|
+
import json
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
from rich.console import Console
|
|
12
|
+
from rich.table import Table
|
|
13
|
+
|
|
14
|
+
from opscloud.mcp.discovery import MCPDiscovery
|
|
15
|
+
from opscloud.mcp.preload import preload_mcp_metadata
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def setup_mcp_parser(subparsers: Any) -> None:
|
|
19
|
+
"""Setup parser for `opscloud mcp`."""
|
|
20
|
+
mcp_parser = subparsers.add_parser(
|
|
21
|
+
"mcp",
|
|
22
|
+
help="List, probe, and inspect configured Model Context Protocol (MCP) servers.",
|
|
23
|
+
)
|
|
24
|
+
mcp_parser.add_argument(
|
|
25
|
+
"action",
|
|
26
|
+
nargs="?",
|
|
27
|
+
default="list",
|
|
28
|
+
choices=["list", "ls", "status", "probe"],
|
|
29
|
+
help="Action to perform (default: list).",
|
|
30
|
+
)
|
|
31
|
+
mcp_parser.add_argument(
|
|
32
|
+
"--json",
|
|
33
|
+
action="store_true",
|
|
34
|
+
dest="json_output",
|
|
35
|
+
help="Output in JSON format.",
|
|
36
|
+
)
|
|
37
|
+
mcp_parser.add_argument(
|
|
38
|
+
"--probe",
|
|
39
|
+
action="store_true",
|
|
40
|
+
help="Actively probe configured servers to inspect tools and health status.",
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def run_mcp_command(args: argparse.Namespace | None = None) -> int:
|
|
45
|
+
"""Execute the `opscloud mcp` CLI command."""
|
|
46
|
+
console = Console()
|
|
47
|
+
action = getattr(args, "action", "list") if args else "list"
|
|
48
|
+
json_output = getattr(args, "json_output", False) if args else False
|
|
49
|
+
do_probe = getattr(args, "probe", False) if args else False
|
|
50
|
+
|
|
51
|
+
if action in ("status", "probe"):
|
|
52
|
+
do_probe = True
|
|
53
|
+
|
|
54
|
+
discovery = MCPDiscovery()
|
|
55
|
+
configs = discovery.discover()
|
|
56
|
+
|
|
57
|
+
if not configs:
|
|
58
|
+
if json_output:
|
|
59
|
+
console.print(json.dumps({"mcp_servers": [], "total_servers": 0}, indent=2))
|
|
60
|
+
else:
|
|
61
|
+
console.print("\n[yellow]No MCP servers configured.[/yellow]")
|
|
62
|
+
console.print("Add servers in [bold].mcp.json[/bold] or [bold]~/.opscloud/.mcp.json[/bold].\n")
|
|
63
|
+
return 0
|
|
64
|
+
|
|
65
|
+
server_infos = None
|
|
66
|
+
if do_probe:
|
|
67
|
+
with console.status("[cyan]Probing MCP servers...[/cyan]"):
|
|
68
|
+
server_infos = asyncio.run(preload_mcp_metadata(configs))
|
|
69
|
+
|
|
70
|
+
if json_output:
|
|
71
|
+
if server_infos:
|
|
72
|
+
payload = {
|
|
73
|
+
"mcp_servers": [s.to_dict() for s in server_infos],
|
|
74
|
+
"total_servers": len(server_infos),
|
|
75
|
+
"total_tools": sum(s.tool_count for s in server_infos),
|
|
76
|
+
}
|
|
77
|
+
else:
|
|
78
|
+
payload = {
|
|
79
|
+
"mcp_servers": [
|
|
80
|
+
{"name": k, **v} for k, v in configs.items()
|
|
81
|
+
],
|
|
82
|
+
"total_servers": len(configs),
|
|
83
|
+
}
|
|
84
|
+
console.print(json.dumps(payload, indent=2))
|
|
85
|
+
return 0
|
|
86
|
+
|
|
87
|
+
console.print("\n[bold cyan]Configured MCP Servers[/bold cyan]\n")
|
|
88
|
+
table = Table(show_header=True, header_style="bold magenta")
|
|
89
|
+
table.add_column("Status", width=8, justify="center")
|
|
90
|
+
table.add_column("Server Name", style="bold")
|
|
91
|
+
table.add_column("Transport", style="dim")
|
|
92
|
+
table.add_column("Command / URL")
|
|
93
|
+
table.add_column("Source", style="dim")
|
|
94
|
+
table.add_column("Tools", justify="right")
|
|
95
|
+
|
|
96
|
+
if server_infos:
|
|
97
|
+
for srv in server_infos:
|
|
98
|
+
if srv.status == "ok":
|
|
99
|
+
status_glyph = "[bold green]● OK[/bold green]"
|
|
100
|
+
elif srv.status == "disabled":
|
|
101
|
+
status_glyph = "[dim]○ OFF[/dim]"
|
|
102
|
+
elif srv.status == "unauthenticated":
|
|
103
|
+
status_glyph = "[bold yellow]▲ AUTH[/bold yellow]"
|
|
104
|
+
else:
|
|
105
|
+
status_glyph = "[bold red]✗ ERR[/bold red]"
|
|
106
|
+
|
|
107
|
+
cmd_or_url = srv.url or f"{srv.command} {' '.join(srv.args or [])}".strip()
|
|
108
|
+
table.add_row(
|
|
109
|
+
status_glyph,
|
|
110
|
+
srv.name,
|
|
111
|
+
srv.transport,
|
|
112
|
+
cmd_or_url or "-",
|
|
113
|
+
srv.source or "unknown",
|
|
114
|
+
str(srv.tool_count),
|
|
115
|
+
)
|
|
116
|
+
else:
|
|
117
|
+
for name, cfg in configs.items():
|
|
118
|
+
enabled = cfg.get("enabled", True)
|
|
119
|
+
status_glyph = "[green]● ON[/green]" if enabled else "[dim]○ OFF[/dim]"
|
|
120
|
+
transport = cfg.get("transport") or ("http" if cfg.get("url") else "stdio")
|
|
121
|
+
cmd_or_url = cfg.get("url") or f"{cfg.get('command', '')} {' '.join(cfg.get('args') or [])}".strip()
|
|
122
|
+
source = cfg.get("source", "project")
|
|
123
|
+
table.add_row(
|
|
124
|
+
status_glyph,
|
|
125
|
+
name,
|
|
126
|
+
transport,
|
|
127
|
+
cmd_or_url or "-",
|
|
128
|
+
source,
|
|
129
|
+
"-",
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
console.print(table)
|
|
133
|
+
if not do_probe:
|
|
134
|
+
console.print("\n[dim]Tip: Run `opscloud mcp probe` to test live server connections and tool schemas.[/dim]\n")
|
|
135
|
+
else:
|
|
136
|
+
console.print("")
|
|
137
|
+
return 0
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""Threads management CLI command."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
from rich.console import Console
|
|
7
|
+
from rich.table import Table
|
|
8
|
+
|
|
9
|
+
from opscloud.output import OutputFormat, write_json
|
|
10
|
+
from opscloud.state.session import (
|
|
11
|
+
format_relative_timestamp,
|
|
12
|
+
format_timestamp,
|
|
13
|
+
list_threads,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def run_threads_command(output_format: OutputFormat = "text") -> int:
|
|
18
|
+
threads = asyncio.run(list_threads(include_message_count=True))
|
|
19
|
+
|
|
20
|
+
if output_format == "json":
|
|
21
|
+
write_json("threads list", threads)
|
|
22
|
+
return 0
|
|
23
|
+
|
|
24
|
+
console = Console()
|
|
25
|
+
console.print("\n[bold yellow]Saved Session Threads[/bold yellow]\n")
|
|
26
|
+
if not threads:
|
|
27
|
+
console.print("[dim]No saved sessions found in SQLite database.[/dim]\n")
|
|
28
|
+
return 0
|
|
29
|
+
|
|
30
|
+
table = Table(border_style="cyan", show_lines=False)
|
|
31
|
+
table.add_column("Thread ID", style="bold green", no_wrap=True)
|
|
32
|
+
table.add_column("Cloud / Env", style="bold magenta", no_wrap=True)
|
|
33
|
+
table.add_column("Msgs", justify="right", style="cyan", no_wrap=True)
|
|
34
|
+
table.add_column("Initial Prompt", style="white")
|
|
35
|
+
table.add_column("Updated", style="dim", no_wrap=True)
|
|
36
|
+
|
|
37
|
+
for t in threads:
|
|
38
|
+
tid = t.get("thread_id", "")
|
|
39
|
+
short_id = tid[:8] if len(tid) > 8 else tid
|
|
40
|
+
cloud = t.get("cloud_badge") or t.get("cloud_provider") or "-"
|
|
41
|
+
msg_count = str(t.get("message_count", 0))
|
|
42
|
+
prompt = t.get("initial_prompt") or "(No initial prompt)"
|
|
43
|
+
if len(prompt) > 50:
|
|
44
|
+
prompt = prompt[:47] + "..."
|
|
45
|
+
updated = format_relative_timestamp(t.get("updated_at")) or format_timestamp(t.get("updated_at"))
|
|
46
|
+
|
|
47
|
+
table.add_row(short_id, cloud, msg_count, prompt, updated)
|
|
48
|
+
|
|
49
|
+
console.print(table)
|
|
50
|
+
console.print("\n[dim]To resume a thread, run:[/dim] opscloud -r <thread_id>\n")
|
|
51
|
+
return 0
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"""Interactive TUI runner for OpsCloud."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import subprocess
|
|
7
|
+
import sys
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from opscloud.approval_mode import ApprovalMode
|
|
11
|
+
from opscloud.config.settings import sync_aws_env_aliases
|
|
12
|
+
from opscloud.server import ServerConfig, ServerProcess
|
|
13
|
+
from opscloud.ui.app import OpsCloudApp
|
|
14
|
+
from opscloud.utils.logger import get_logger
|
|
15
|
+
|
|
16
|
+
logger = get_logger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def run_interactive(
|
|
20
|
+
*,
|
|
21
|
+
model: str | None = None,
|
|
22
|
+
approval_mode: ApprovalMode | str = ApprovalMode.MANUAL,
|
|
23
|
+
resume_thread: str | None = None,
|
|
24
|
+
cwd: str | None = None,
|
|
25
|
+
goal: str | None = None,
|
|
26
|
+
initial_prompt: str | None = None,
|
|
27
|
+
initial_skill: str | None = None,
|
|
28
|
+
startup_cmd: str | None = None,
|
|
29
|
+
read_only: bool = False,
|
|
30
|
+
aws_profile: str | None = None,
|
|
31
|
+
aws_region: str | None = None,
|
|
32
|
+
shell_allow_list: list[str] | None = None,
|
|
33
|
+
mcp_config_path: str | None = None,
|
|
34
|
+
no_mcp: bool = False,
|
|
35
|
+
trust_project_mcp: bool = False,
|
|
36
|
+
) -> int:
|
|
37
|
+
"""Launch interactive TUI session with background server and cloud context."""
|
|
38
|
+
if resume_thread:
|
|
39
|
+
from opscloud.utils.logger import bind_logging_to_thread
|
|
40
|
+
|
|
41
|
+
bind_logging_to_thread(resume_thread)
|
|
42
|
+
|
|
43
|
+
# 1. Startup command
|
|
44
|
+
if startup_cmd:
|
|
45
|
+
try:
|
|
46
|
+
print(f"[Running startup command: {startup_cmd}]", file=sys.stderr)
|
|
47
|
+
subprocess.run(startup_cmd, shell=True, check=False)
|
|
48
|
+
except Exception as e:
|
|
49
|
+
logger.warning("Startup command failed: %s", e)
|
|
50
|
+
|
|
51
|
+
# 2. AWS Profile & Region sync
|
|
52
|
+
from opscloud.config.cloud_profiles import get_active_aws_profile, get_active_aws_region
|
|
53
|
+
|
|
54
|
+
if not aws_profile:
|
|
55
|
+
aws_profile = get_active_aws_profile()
|
|
56
|
+
|
|
57
|
+
if not aws_region:
|
|
58
|
+
aws_region = get_active_aws_region()
|
|
59
|
+
|
|
60
|
+
if aws_profile:
|
|
61
|
+
os.environ["AWS_PROFILE"] = aws_profile
|
|
62
|
+
if aws_region:
|
|
63
|
+
os.environ["AWS_REGION"] = aws_region
|
|
64
|
+
if aws_profile or aws_region:
|
|
65
|
+
sync_aws_env_aliases()
|
|
66
|
+
|
|
67
|
+
from pathlib import Path
|
|
68
|
+
from opscloud.project_utils import ProjectContext
|
|
69
|
+
|
|
70
|
+
project_context = (
|
|
71
|
+
ProjectContext.from_user_cwd(Path(cwd))
|
|
72
|
+
if cwd is not None
|
|
73
|
+
else ProjectContext.from_user_cwd(Path.cwd())
|
|
74
|
+
)
|
|
75
|
+
resolved_cwd = str(project_context.user_cwd)
|
|
76
|
+
|
|
77
|
+
defer_server_start = False
|
|
78
|
+
resolved_model = model
|
|
79
|
+
if not resolved_model:
|
|
80
|
+
try:
|
|
81
|
+
from opscloud.model.factory import _get_default_model_spec
|
|
82
|
+
|
|
83
|
+
resolved_model = _get_default_model_spec()
|
|
84
|
+
except Exception:
|
|
85
|
+
resolved_model = None
|
|
86
|
+
defer_server_start = True
|
|
87
|
+
|
|
88
|
+
proc: ServerProcess | None = None
|
|
89
|
+
url: str | None = None
|
|
90
|
+
if not defer_server_start and resolved_model:
|
|
91
|
+
config = ServerConfig.from_cli_args(
|
|
92
|
+
project_context=project_context,
|
|
93
|
+
model=resolved_model,
|
|
94
|
+
interactive=True,
|
|
95
|
+
approval_mode=str(approval_mode),
|
|
96
|
+
read_only=read_only,
|
|
97
|
+
aws_profile=aws_profile,
|
|
98
|
+
aws_region=aws_region,
|
|
99
|
+
shell_allow_list=tuple(shell_allow_list) if shell_allow_list else None,
|
|
100
|
+
mcp_config_path=mcp_config_path,
|
|
101
|
+
no_mcp=no_mcp,
|
|
102
|
+
trust_project_mcp=trust_project_mcp,
|
|
103
|
+
)
|
|
104
|
+
proc = ServerProcess(config)
|
|
105
|
+
url = proc.start()
|
|
106
|
+
|
|
107
|
+
effective_prompt = initial_prompt
|
|
108
|
+
if initial_skill and not effective_prompt:
|
|
109
|
+
effective_prompt = f"Use skill {initial_skill}"
|
|
110
|
+
|
|
111
|
+
mcp_preload_kwargs: dict[str, Any] = {
|
|
112
|
+
"mcp_config_path": mcp_config_path,
|
|
113
|
+
"no_mcp": no_mcp,
|
|
114
|
+
"trust_project_mcp": trust_project_mcp,
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
try:
|
|
118
|
+
app = OpsCloudApp(
|
|
119
|
+
server_url=url,
|
|
120
|
+
server=proc,
|
|
121
|
+
server_proc=proc,
|
|
122
|
+
thread_id=resume_thread,
|
|
123
|
+
model=resolved_model,
|
|
124
|
+
approval_mode=approval_mode,
|
|
125
|
+
defer_server_start=defer_server_start,
|
|
126
|
+
goal=goal,
|
|
127
|
+
cwd=resolved_cwd,
|
|
128
|
+
mcp_preload_kwargs=mcp_preload_kwargs,
|
|
129
|
+
)
|
|
130
|
+
if effective_prompt and hasattr(app, "_initial_prompt"):
|
|
131
|
+
app._initial_prompt = effective_prompt
|
|
132
|
+
|
|
133
|
+
thread_id = app.run()
|
|
134
|
+
if thread_id and isinstance(thread_id, str) and thread_id != "local_session":
|
|
135
|
+
try:
|
|
136
|
+
from rich.console import Console
|
|
137
|
+
from rich.text import Text
|
|
138
|
+
|
|
139
|
+
console = Console()
|
|
140
|
+
console.print()
|
|
141
|
+
console.print("[dim]Resume this thread with:[/dim]")
|
|
142
|
+
hint = Text("opscloud -r ", style="cyan")
|
|
143
|
+
hint.append(thread_id, style="cyan")
|
|
144
|
+
console.print(hint)
|
|
145
|
+
except Exception:
|
|
146
|
+
pass
|
|
147
|
+
return 0
|
|
148
|
+
except Exception as e:
|
|
149
|
+
logger.critical("OpsCloud TUI run failed: %s", e, exc_info=True)
|
|
150
|
+
print(f"Error starting OpsCloud: {e}")
|
|
151
|
+
return 1
|
|
152
|
+
finally:
|
|
153
|
+
if proc is not None:
|
|
154
|
+
proc.stop()
|