praisonai-code 0.0.1__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.
- praisonai_code/__init__.py +17 -0
- praisonai_code/cli/__init__.py +12 -0
- praisonai_code/cli/_forward_shim.py +10 -0
- praisonai_code/cli/_paths.py +88 -0
- praisonai_code/cli/_warnings.py +58 -0
- praisonai_code/cli/app.py +757 -0
- praisonai_code/cli/approval_backend.py +272 -0
- praisonai_code/cli/branding.py +94 -0
- praisonai_code/cli/commands/__init__.py +114 -0
- praisonai_code/cli/commands/acp.py +80 -0
- praisonai_code/cli/commands/agent.py +116 -0
- praisonai_code/cli/commands/agents.py +80 -0
- praisonai_code/cli/commands/app.py +139 -0
- praisonai_code/cli/commands/attach.py +95 -0
- praisonai_code/cli/commands/audit.py +102 -0
- praisonai_code/cli/commands/auth.py +508 -0
- praisonai_code/cli/commands/batch.py +848 -0
- praisonai_code/cli/commands/benchmark.py +286 -0
- praisonai_code/cli/commands/browser.py +299 -0
- praisonai_code/cli/commands/call.py +45 -0
- praisonai_code/cli/commands/chat.py +332 -0
- praisonai_code/cli/commands/checkpoint.py +170 -0
- praisonai_code/cli/commands/code.py +276 -0
- praisonai_code/cli/commands/command.py +114 -0
- praisonai_code/cli/commands/commit.py +47 -0
- praisonai_code/cli/commands/completion.py +333 -0
- praisonai_code/cli/commands/config.py +681 -0
- praisonai_code/cli/commands/context.py +414 -0
- praisonai_code/cli/commands/daemon.py +203 -0
- praisonai_code/cli/commands/debug.py +142 -0
- praisonai_code/cli/commands/deploy.py +71 -0
- praisonai_code/cli/commands/diag.py +55 -0
- praisonai_code/cli/commands/docs.py +1575 -0
- praisonai_code/cli/commands/doctor.py +332 -0
- praisonai_code/cli/commands/endpoints.py +51 -0
- praisonai_code/cli/commands/environment.py +179 -0
- praisonai_code/cli/commands/eval.py +131 -0
- praisonai_code/cli/commands/examples.py +953 -0
- praisonai_code/cli/commands/flow.py +436 -0
- praisonai_code/cli/commands/github.py +752 -0
- praisonai_code/cli/commands/hooks.py +74 -0
- praisonai_code/cli/commands/init.py +174 -0
- praisonai_code/cli/commands/knowledge.py +440 -0
- praisonai_code/cli/commands/langextract.py +120 -0
- praisonai_code/cli/commands/langfuse.py +984 -0
- praisonai_code/cli/commands/loop.py +211 -0
- praisonai_code/cli/commands/lsp.py +112 -0
- praisonai_code/cli/commands/managed.py +659 -0
- praisonai_code/cli/commands/mcp.py +763 -0
- praisonai_code/cli/commands/memory.py +298 -0
- praisonai_code/cli/commands/models.py +264 -0
- praisonai_code/cli/commands/n8n.py +326 -0
- praisonai_code/cli/commands/obs.py +19 -0
- praisonai_code/cli/commands/package.py +76 -0
- praisonai_code/cli/commands/paths.py +106 -0
- praisonai_code/cli/commands/permissions.py +272 -0
- praisonai_code/cli/commands/plugins.py +609 -0
- praisonai_code/cli/commands/port.py +530 -0
- praisonai_code/cli/commands/profile.py +466 -0
- praisonai_code/cli/commands/publish.py +193 -0
- praisonai_code/cli/commands/rag.py +913 -0
- praisonai_code/cli/commands/realtime.py +52 -0
- praisonai_code/cli/commands/recipe.py +684 -0
- praisonai_code/cli/commands/registry.py +59 -0
- praisonai_code/cli/commands/replay.py +830 -0
- praisonai_code/cli/commands/research.py +49 -0
- praisonai_code/cli/commands/retrieval.py +377 -0
- praisonai_code/cli/commands/rules.py +71 -0
- praisonai_code/cli/commands/run.py +1573 -0
- praisonai_code/cli/commands/sandbox.py +371 -0
- praisonai_code/cli/commands/schedule.py +529 -0
- praisonai_code/cli/commands/serve.py +690 -0
- praisonai_code/cli/commands/session.py +450 -0
- praisonai_code/cli/commands/setup.py +174 -0
- praisonai_code/cli/commands/skills.py +545 -0
- praisonai_code/cli/commands/standardise.py +711 -0
- praisonai_code/cli/commands/templates.py +54 -0
- praisonai_code/cli/commands/test.py +558 -0
- praisonai_code/cli/commands/todo.py +74 -0
- praisonai_code/cli/commands/tools.py +205 -0
- praisonai_code/cli/commands/traces.py +145 -0
- praisonai_code/cli/commands/tracker.py +852 -0
- praisonai_code/cli/commands/train.py +613 -0
- praisonai_code/cli/commands/ui.py +172 -0
- praisonai_code/cli/commands/up.py +354 -0
- praisonai_code/cli/commands/validate.py +291 -0
- praisonai_code/cli/commands/version.py +101 -0
- praisonai_code/cli/commands/workflow.py +97 -0
- praisonai_code/cli/config_loader.py +437 -0
- praisonai_code/cli/configuration/__init__.py +27 -0
- praisonai_code/cli/configuration/config.schema.json +57 -0
- praisonai_code/cli/configuration/credentials.py +446 -0
- praisonai_code/cli/configuration/loader.py +364 -0
- praisonai_code/cli/configuration/model_resolver.py +161 -0
- praisonai_code/cli/configuration/oauth.py +389 -0
- praisonai_code/cli/configuration/paths.py +224 -0
- praisonai_code/cli/configuration/resolver.py +687 -0
- praisonai_code/cli/configuration/schema.py +317 -0
- praisonai_code/cli/execution/__init__.py +99 -0
- praisonai_code/cli/execution/core.py +208 -0
- praisonai_code/cli/execution/profiler.py +898 -0
- praisonai_code/cli/execution/request.py +85 -0
- praisonai_code/cli/execution/result.py +74 -0
- praisonai_code/cli/fallback_schema.py +416 -0
- praisonai_code/cli/features/__init__.py +278 -0
- praisonai_code/cli/features/_endpoint_registry.py +64 -0
- praisonai_code/cli/features/_search_registry.py +43 -0
- praisonai_code/cli/features/acp.py +236 -0
- praisonai_code/cli/features/action_orchestrator.py +576 -0
- praisonai_code/cli/features/agent_scheduler.py +773 -0
- praisonai_code/cli/features/agent_tools.py +603 -0
- praisonai_code/cli/features/agents.py +397 -0
- praisonai_code/cli/features/at_mentions.py +471 -0
- praisonai_code/cli/features/audit_cli.py +270 -0
- praisonai_code/cli/features/auto_memory.py +182 -0
- praisonai_code/cli/features/auto_mode.py +552 -0
- praisonai_code/cli/features/autonomy_mode.py +546 -0
- praisonai_code/cli/features/background.py +356 -0
- praisonai_code/cli/features/base.py +168 -0
- praisonai_code/cli/features/benchmark.py +1462 -0
- praisonai_code/cli/features/capabilities.py +1326 -0
- praisonai_code/cli/features/checkpoints.py +345 -0
- praisonai_code/cli/features/cli_profiler.py +335 -0
- praisonai_code/cli/features/code_intelligence.py +666 -0
- praisonai_code/cli/features/compaction.py +294 -0
- praisonai_code/cli/features/compare.py +534 -0
- praisonai_code/cli/features/config_hierarchy.py +366 -0
- praisonai_code/cli/features/context_manager.py +597 -0
- praisonai_code/cli/features/cost_tracker.py +514 -0
- praisonai_code/cli/features/csv_test_runner.py +736 -0
- praisonai_code/cli/features/custom_definitions.py +790 -0
- praisonai_code/cli/features/debug.py +810 -0
- praisonai_code/cli/features/deploy.py +605 -0
- praisonai_code/cli/features/diag.py +289 -0
- praisonai_code/cli/features/display_jsonl.py +173 -0
- praisonai_code/cli/features/doctor/__init__.py +63 -0
- praisonai_code/cli/features/doctor/checks/__init__.py +29 -0
- praisonai_code/cli/features/doctor/checks/acp_checks.py +220 -0
- praisonai_code/cli/features/doctor/checks/bot_checks.py +340 -0
- praisonai_code/cli/features/doctor/checks/config_checks.py +373 -0
- praisonai_code/cli/features/doctor/checks/db_checks.py +366 -0
- praisonai_code/cli/features/doctor/checks/env_checks.py +637 -0
- praisonai_code/cli/features/doctor/checks/gateway_checks.py +387 -0
- praisonai_code/cli/features/doctor/checks/lsp_checks.py +231 -0
- praisonai_code/cli/features/doctor/checks/mcp_checks.py +367 -0
- praisonai_code/cli/features/doctor/checks/memory_checks.py +268 -0
- praisonai_code/cli/features/doctor/checks/network_checks.py +251 -0
- praisonai_code/cli/features/doctor/checks/obs_checks.py +328 -0
- praisonai_code/cli/features/doctor/checks/packaging_checks.py +422 -0
- praisonai_code/cli/features/doctor/checks/performance_checks.py +235 -0
- praisonai_code/cli/features/doctor/checks/permissions_checks.py +259 -0
- praisonai_code/cli/features/doctor/checks/runtime_checks.py +650 -0
- praisonai_code/cli/features/doctor/checks/runtime_migration_checks.py +220 -0
- praisonai_code/cli/features/doctor/checks/selftest_checks.py +322 -0
- praisonai_code/cli/features/doctor/checks/serve_checks.py +426 -0
- praisonai_code/cli/features/doctor/checks/skills_checks.py +327 -0
- praisonai_code/cli/features/doctor/checks/tools_checks.py +371 -0
- praisonai_code/cli/features/doctor/engine.py +266 -0
- praisonai_code/cli/features/doctor/formatters.py +377 -0
- praisonai_code/cli/features/doctor/handler.py +564 -0
- praisonai_code/cli/features/doctor/models.py +276 -0
- praisonai_code/cli/features/doctor/registry.py +239 -0
- praisonai_code/cli/features/endpoints.py +1016 -0
- praisonai_code/cli/features/eval.py +559 -0
- praisonai_code/cli/features/examples.py +707 -0
- praisonai_code/cli/features/external_agents.py +231 -0
- praisonai_code/cli/features/fast_context.py +410 -0
- praisonai_code/cli/features/file_history.py +320 -0
- praisonai_code/cli/features/flow_display.py +566 -0
- praisonai_code/cli/features/git_attribution.py +159 -0
- praisonai_code/cli/features/git_integration.py +651 -0
- praisonai_code/cli/features/guardrail.py +171 -0
- praisonai_code/cli/features/handoff.py +252 -0
- praisonai_code/cli/features/hooks.py +583 -0
- praisonai_code/cli/features/hybrid_workflow.py +391 -0
- praisonai_code/cli/features/image.py +384 -0
- praisonai_code/cli/features/interactive_core_headless.py +450 -0
- praisonai_code/cli/features/interactive_runtime.py +600 -0
- praisonai_code/cli/features/interactive_test_harness.py +537 -0
- praisonai_code/cli/features/interactive_tools.py +428 -0
- praisonai_code/cli/features/interactive_tui.py +603 -0
- praisonai_code/cli/features/job_workflow.py +906 -0
- praisonai_code/cli/features/jobs.py +632 -0
- praisonai_code/cli/features/knowledge.py +531 -0
- praisonai_code/cli/features/knowledge_cli.py +438 -0
- praisonai_code/cli/features/lite.py +244 -0
- praisonai_code/cli/features/logs.py +200 -0
- praisonai_code/cli/features/lsp_cli.py +225 -0
- praisonai_code/cli/features/lsp_diagnostics.py +185 -0
- praisonai_code/cli/features/mcp.py +344 -0
- praisonai_code/cli/features/message_queue.py +587 -0
- praisonai_code/cli/features/metrics.py +210 -0
- praisonai_code/cli/features/migrate.py +1329 -0
- praisonai_code/cli/features/migration_flow.py +463 -0
- praisonai_code/cli/features/migration_spec.py +276 -0
- praisonai_code/cli/features/n8n.py +703 -0
- praisonai_code/cli/features/observability.py +293 -0
- praisonai_code/cli/features/ollama.py +361 -0
- praisonai_code/cli/features/output_modes.py +155 -0
- praisonai_code/cli/features/output_style.py +273 -0
- praisonai_code/cli/features/package.py +631 -0
- praisonai_code/cli/features/performance.py +308 -0
- praisonai_code/cli/features/persistence.py +636 -0
- praisonai_code/cli/features/profiler/__init__.py +81 -0
- praisonai_code/cli/features/profiler/core.py +558 -0
- praisonai_code/cli/features/profiler/optimizations.py +652 -0
- praisonai_code/cli/features/profiler/suite.py +386 -0
- praisonai_code/cli/features/queue/__init__.py +73 -0
- praisonai_code/cli/features/queue/manager.py +435 -0
- praisonai_code/cli/features/queue/models.py +289 -0
- praisonai_code/cli/features/queue/persistence.py +564 -0
- praisonai_code/cli/features/queue/scheduler.py +529 -0
- praisonai_code/cli/features/queue/worker.py +400 -0
- praisonai_code/cli/features/recipe.py +2187 -0
- praisonai_code/cli/features/recipe_creator.py +996 -0
- praisonai_code/cli/features/recipe_optimizer.py +1364 -0
- praisonai_code/cli/features/recipe_prompts.py +226 -0
- praisonai_code/cli/features/registry.py +229 -0
- praisonai_code/cli/features/repo_map.py +860 -0
- praisonai_code/cli/features/router.py +466 -0
- praisonai_code/cli/features/safe_shell.py +427 -0
- praisonai_code/cli/features/sandbox_cli.py +283 -0
- praisonai_code/cli/features/sandbox_executor.py +536 -0
- praisonai_code/cli/features/sdk_knowledge.py +500 -0
- praisonai_code/cli/features/session.py +222 -0
- praisonai_code/cli/features/session_checkpoints.py +208 -0
- praisonai_code/cli/features/setup/__init__.py +9 -0
- praisonai_code/cli/features/setup/handler.py +355 -0
- praisonai_code/cli/features/setup/templates.py +62 -0
- praisonai_code/cli/features/skills.py +940 -0
- praisonai_code/cli/features/slash_commands.py +692 -0
- praisonai_code/cli/features/telemetry.py +179 -0
- praisonai_code/cli/features/templates.py +1390 -0
- praisonai_code/cli/features/thinking.py +343 -0
- praisonai_code/cli/features/todo.py +334 -0
- praisonai_code/cli/features/tools.py +680 -0
- praisonai_code/cli/features/tui/__init__.py +83 -0
- praisonai_code/cli/features/tui/app.py +871 -0
- praisonai_code/cli/features/tui/cli.py +580 -0
- praisonai_code/cli/features/tui/config.py +150 -0
- praisonai_code/cli/features/tui/debug.py +526 -0
- praisonai_code/cli/features/tui/events.py +99 -0
- praisonai_code/cli/features/tui/mock_provider.py +328 -0
- praisonai_code/cli/features/tui/orchestrator.py +652 -0
- praisonai_code/cli/features/tui/screens/__init__.py +50 -0
- praisonai_code/cli/features/tui/screens/help.py +157 -0
- praisonai_code/cli/features/tui/screens/main.py +568 -0
- praisonai_code/cli/features/tui/screens/queue.py +174 -0
- praisonai_code/cli/features/tui/screens/session.py +124 -0
- praisonai_code/cli/features/tui/screens/settings.py +148 -0
- praisonai_code/cli/features/tui/session_store.py +198 -0
- praisonai_code/cli/features/tui/widgets/__init__.py +56 -0
- praisonai_code/cli/features/tui/widgets/chat.py +263 -0
- praisonai_code/cli/features/tui/widgets/command_popup.py +258 -0
- praisonai_code/cli/features/tui/widgets/composer.py +292 -0
- praisonai_code/cli/features/tui/widgets/file_popup.py +207 -0
- praisonai_code/cli/features/tui/widgets/queue_panel.py +223 -0
- praisonai_code/cli/features/tui/widgets/status.py +181 -0
- praisonai_code/cli/features/tui/widgets/tool_panel.py +307 -0
- praisonai_code/cli/features/wizard.py +289 -0
- praisonai_code/cli/features/workflow.py +802 -0
- praisonai_code/cli/features/yaml_utils.py +321 -0
- praisonai_code/cli/interactive/__init__.py +48 -0
- praisonai_code/cli/interactive/async_tui.py +1218 -0
- praisonai_code/cli/interactive/config.py +139 -0
- praisonai_code/cli/interactive/core.py +618 -0
- praisonai_code/cli/interactive/events.py +131 -0
- praisonai_code/cli/interactive/frontends/__init__.py +31 -0
- praisonai_code/cli/interactive/frontends/rich_frontend.py +462 -0
- praisonai_code/cli/interactive/frontends/textual_frontend.py +157 -0
- praisonai_code/cli/interactive/praison_io.py +502 -0
- praisonai_code/cli/interactive/repl.py +297 -0
- praisonai_code/cli/interactive/split_tui.py +456 -0
- praisonai_code/cli/interactive/tui_app.py +457 -0
- praisonai_code/cli/langfuse_client.py +360 -0
- praisonai_code/cli/main.py +7421 -0
- praisonai_code/cli/output/__init__.py +25 -0
- praisonai_code/cli/output/console.py +456 -0
- praisonai_code/cli/output/event_bridge.py +191 -0
- praisonai_code/cli/schedule_cli.py +54 -0
- praisonai_code/cli/schema_provider.py +23 -0
- praisonai_code/cli/session/__init__.py +16 -0
- praisonai_code/cli/session/resume.py +148 -0
- praisonai_code/cli/session/unified.py +548 -0
- praisonai_code/cli/state/__init__.py +31 -0
- praisonai_code/cli/state/identifiers.py +161 -0
- praisonai_code/cli/state/project_sessions.py +383 -0
- praisonai_code/cli/state/sessions.py +390 -0
- praisonai_code/cli/ui/__init__.py +160 -0
- praisonai_code/cli/ui/config.py +46 -0
- praisonai_code/cli/ui/events.py +61 -0
- praisonai_code/cli/ui/mg_backend.py +342 -0
- praisonai_code/cli/ui/plain.py +133 -0
- praisonai_code/cli/ui/rich_backend.py +162 -0
- praisonai_code/cli/unified_schema.py +655 -0
- praisonai_code/cli/utils/env_utils.py +126 -0
- praisonai_code/cli/utils/project.py +131 -0
- praisonai_code/cli_backends/__init__.py +73 -0
- praisonai_code/cli_backends/claude.py +373 -0
- praisonai_code/cli_backends/registry.py +113 -0
- praisonai_code/runtime/__init__.py +36 -0
- praisonai_code/runtime/__main__.py +81 -0
- praisonai_code/runtime/client.py +131 -0
- praisonai_code/runtime/descriptor.py +209 -0
- praisonai_code/runtime/server.py +356 -0
- praisonai_code-0.0.1.dist-info/METADATA +80 -0
- praisonai_code-0.0.1.dist-info/RECORD +309 -0
- praisonai_code-0.0.1.dist-info/WHEEL +5 -0
- praisonai_code-0.0.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
"""
|
|
2
|
+
ACP health checks for PraisonAI Doctor.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import logging
|
|
7
|
+
import os
|
|
8
|
+
|
|
9
|
+
from ..models import CheckCategory, CheckResult, CheckStatus
|
|
10
|
+
from ..registry import register_check
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@register_check(
|
|
16
|
+
id="acp_module",
|
|
17
|
+
title="ACP Module Available",
|
|
18
|
+
category=CheckCategory.TOOLS,
|
|
19
|
+
description="Check if ACP module is installed"
|
|
20
|
+
)
|
|
21
|
+
def check_acp_module(config=None) -> CheckResult:
|
|
22
|
+
"""Check if ACP module is available."""
|
|
23
|
+
try:
|
|
24
|
+
import importlib.util
|
|
25
|
+
spec = importlib.util.find_spec("praisonai.acp")
|
|
26
|
+
|
|
27
|
+
if spec is not None:
|
|
28
|
+
return CheckResult(
|
|
29
|
+
id="acp_module",
|
|
30
|
+
title="ACP Module Available",
|
|
31
|
+
category=CheckCategory.TOOLS,
|
|
32
|
+
status=CheckStatus.PASS,
|
|
33
|
+
message="ACP module is available",
|
|
34
|
+
metadata={"module": "praisonai.acp"}
|
|
35
|
+
)
|
|
36
|
+
else:
|
|
37
|
+
return CheckResult(
|
|
38
|
+
id="acp_module",
|
|
39
|
+
title="ACP Module Available",
|
|
40
|
+
category=CheckCategory.TOOLS,
|
|
41
|
+
status=CheckStatus.WARN,
|
|
42
|
+
message="ACP module not found",
|
|
43
|
+
remediation="ACP module should be part of praisonai package"
|
|
44
|
+
)
|
|
45
|
+
except Exception as e:
|
|
46
|
+
return CheckResult(
|
|
47
|
+
id="acp_module",
|
|
48
|
+
title="ACP Module Available",
|
|
49
|
+
category=CheckCategory.TOOLS,
|
|
50
|
+
status=CheckStatus.FAIL,
|
|
51
|
+
message=f"Error checking ACP module: {e}",
|
|
52
|
+
metadata={"error": str(e)}
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@register_check(
|
|
57
|
+
id="acp_sdk",
|
|
58
|
+
title="ACP SDK Available",
|
|
59
|
+
category=CheckCategory.TOOLS,
|
|
60
|
+
description="Check if agent-client-protocol SDK is installed"
|
|
61
|
+
)
|
|
62
|
+
def check_acp_sdk(config=None) -> CheckResult:
|
|
63
|
+
"""Check if ACP SDK is available."""
|
|
64
|
+
try:
|
|
65
|
+
import importlib.util
|
|
66
|
+
spec = importlib.util.find_spec("acp")
|
|
67
|
+
|
|
68
|
+
if spec is not None:
|
|
69
|
+
return CheckResult(
|
|
70
|
+
id="acp_sdk",
|
|
71
|
+
title="ACP SDK Available",
|
|
72
|
+
category=CheckCategory.TOOLS,
|
|
73
|
+
status=CheckStatus.PASS,
|
|
74
|
+
message="ACP SDK (agent-client-protocol) is installed",
|
|
75
|
+
metadata={"package": "agent-client-protocol"}
|
|
76
|
+
)
|
|
77
|
+
else:
|
|
78
|
+
return CheckResult(
|
|
79
|
+
id="acp_sdk",
|
|
80
|
+
title="ACP SDK Available",
|
|
81
|
+
category=CheckCategory.TOOLS,
|
|
82
|
+
status=CheckStatus.SKIP,
|
|
83
|
+
message="ACP SDK not installed (optional)",
|
|
84
|
+
remediation="Install with: pip install agent-client-protocol"
|
|
85
|
+
)
|
|
86
|
+
except Exception as e:
|
|
87
|
+
return CheckResult(
|
|
88
|
+
id="acp_sdk",
|
|
89
|
+
title="ACP SDK Available",
|
|
90
|
+
category=CheckCategory.TOOLS,
|
|
91
|
+
status=CheckStatus.FAIL,
|
|
92
|
+
message=f"Error checking ACP SDK: {e}",
|
|
93
|
+
metadata={"error": str(e)}
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
@register_check(
|
|
98
|
+
id="acp_server",
|
|
99
|
+
title="ACP Server Import",
|
|
100
|
+
category=CheckCategory.TOOLS,
|
|
101
|
+
description="Check if ACP server can be imported"
|
|
102
|
+
)
|
|
103
|
+
def check_acp_server(config=None) -> CheckResult:
|
|
104
|
+
"""Check if ACP server can be imported."""
|
|
105
|
+
try:
|
|
106
|
+
from praisonai.acp import ACPServer # noqa: F401
|
|
107
|
+
|
|
108
|
+
return CheckResult(
|
|
109
|
+
id="acp_server",
|
|
110
|
+
title="ACP Server Import",
|
|
111
|
+
category=CheckCategory.TOOLS,
|
|
112
|
+
status=CheckStatus.PASS,
|
|
113
|
+
message="ACP server can be imported",
|
|
114
|
+
metadata={"class": "ACPServer"}
|
|
115
|
+
)
|
|
116
|
+
except ImportError as e:
|
|
117
|
+
return CheckResult(
|
|
118
|
+
id="acp_server",
|
|
119
|
+
title="ACP Server Import",
|
|
120
|
+
category=CheckCategory.TOOLS,
|
|
121
|
+
status=CheckStatus.WARN,
|
|
122
|
+
message=f"ACP server import failed: {e}",
|
|
123
|
+
metadata={"error": str(e)}
|
|
124
|
+
)
|
|
125
|
+
except Exception as e:
|
|
126
|
+
return CheckResult(
|
|
127
|
+
id="acp_server",
|
|
128
|
+
title="ACP Server Import",
|
|
129
|
+
category=CheckCategory.TOOLS,
|
|
130
|
+
status=CheckStatus.FAIL,
|
|
131
|
+
message=f"Error importing ACP server: {e}",
|
|
132
|
+
metadata={"error": str(e)}
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
@register_check(
|
|
137
|
+
id="acp_config",
|
|
138
|
+
title="ACP Configuration",
|
|
139
|
+
category=CheckCategory.CONFIG,
|
|
140
|
+
description="Check ACP configuration"
|
|
141
|
+
)
|
|
142
|
+
def check_acp_config(config=None) -> CheckResult:
|
|
143
|
+
"""Check ACP configuration."""
|
|
144
|
+
acp_conf = {
|
|
145
|
+
"approval_mode": os.environ.get("PRAISONAI_APPROVAL_MODE", "manual"),
|
|
146
|
+
"allow_write": os.environ.get("PRAISONAI_ALLOW_WRITE", "false"),
|
|
147
|
+
"allow_shell": os.environ.get("PRAISONAI_ALLOW_SHELL", "false"),
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return CheckResult(
|
|
151
|
+
id="acp_config",
|
|
152
|
+
title="ACP Configuration",
|
|
153
|
+
category=CheckCategory.CONFIG,
|
|
154
|
+
status=CheckStatus.PASS,
|
|
155
|
+
message=f"ACP configured with approval_mode={acp_conf['approval_mode']}",
|
|
156
|
+
metadata=acp_conf
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
@register_check(
|
|
161
|
+
id="acp_runtime",
|
|
162
|
+
title="ACP Runtime Check",
|
|
163
|
+
category=CheckCategory.TOOLS,
|
|
164
|
+
description="Check if ACP runtime can start",
|
|
165
|
+
requires_deep=True
|
|
166
|
+
)
|
|
167
|
+
def check_acp_runtime(config=None) -> CheckResult:
|
|
168
|
+
"""Check if ACP runtime can start."""
|
|
169
|
+
try:
|
|
170
|
+
from praisonai.cli.features.interactive_runtime import create_runtime
|
|
171
|
+
|
|
172
|
+
async def _check():
|
|
173
|
+
runtime = create_runtime(
|
|
174
|
+
workspace=".",
|
|
175
|
+
lsp=False,
|
|
176
|
+
acp=True
|
|
177
|
+
)
|
|
178
|
+
try:
|
|
179
|
+
await runtime.start()
|
|
180
|
+
ready = runtime.acp_ready
|
|
181
|
+
error = runtime._acp_state.error
|
|
182
|
+
return ready, error
|
|
183
|
+
finally:
|
|
184
|
+
await runtime.stop()
|
|
185
|
+
|
|
186
|
+
ready, error = asyncio.run(_check())
|
|
187
|
+
|
|
188
|
+
if ready:
|
|
189
|
+
return CheckResult(
|
|
190
|
+
id="acp_runtime",
|
|
191
|
+
title="ACP Runtime Check",
|
|
192
|
+
category=CheckCategory.TOOLS,
|
|
193
|
+
status=CheckStatus.PASS,
|
|
194
|
+
message="ACP runtime started successfully",
|
|
195
|
+
metadata={"ready": True}
|
|
196
|
+
)
|
|
197
|
+
else:
|
|
198
|
+
return CheckResult(
|
|
199
|
+
id="acp_runtime",
|
|
200
|
+
title="ACP Runtime Check",
|
|
201
|
+
category=CheckCategory.TOOLS,
|
|
202
|
+
status=CheckStatus.WARN,
|
|
203
|
+
message=f"ACP runtime failed to start: {error}",
|
|
204
|
+
metadata={"ready": False, "error": str(error)}
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
except Exception as e:
|
|
208
|
+
return CheckResult(
|
|
209
|
+
id="acp_runtime",
|
|
210
|
+
title="ACP Runtime Check",
|
|
211
|
+
category=CheckCategory.TOOLS,
|
|
212
|
+
status=CheckStatus.FAIL,
|
|
213
|
+
message=f"ACP runtime check failed: {e}",
|
|
214
|
+
metadata={"error": str(e)}
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def register_acp_checks():
|
|
219
|
+
"""Register all ACP checks."""
|
|
220
|
+
pass
|
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Bot-specific doctor checks for PraisonAI.
|
|
3
|
+
|
|
4
|
+
Registers checks for bot tokens, config validation, and channel probes.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import os
|
|
10
|
+
import time
|
|
11
|
+
from typing import List
|
|
12
|
+
|
|
13
|
+
from ..models import CheckResult, CheckStatus, CheckCategory, CheckSeverity, DoctorConfig
|
|
14
|
+
from ..registry import register_check
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@register_check(
|
|
18
|
+
id="bot_tokens",
|
|
19
|
+
title="Bot Tokens",
|
|
20
|
+
description="Check bot token environment variables",
|
|
21
|
+
category=CheckCategory.BOTS,
|
|
22
|
+
severity=CheckSeverity.MEDIUM,
|
|
23
|
+
)
|
|
24
|
+
def check_bot_tokens(config: DoctorConfig) -> CheckResult:
|
|
25
|
+
"""Check bot token environment variables."""
|
|
26
|
+
from praisonai.cli._paths import resolve_bot_config_path
|
|
27
|
+
config_path = getattr(config, 'config_file', None) or resolve_bot_config_path("bot.yaml")
|
|
28
|
+
token_vars = {
|
|
29
|
+
"TELEGRAM_BOT_TOKEN": "Telegram",
|
|
30
|
+
"DISCORD_BOT_TOKEN": "Discord",
|
|
31
|
+
"SLACK_BOT_TOKEN": "Slack",
|
|
32
|
+
"WHATSAPP_ACCESS_TOKEN": "WhatsApp",
|
|
33
|
+
}
|
|
34
|
+
found = []
|
|
35
|
+
for var, name in token_vars.items():
|
|
36
|
+
if os.environ.get(var):
|
|
37
|
+
found.append(name)
|
|
38
|
+
|
|
39
|
+
if found:
|
|
40
|
+
return CheckResult(
|
|
41
|
+
id="bot_tokens",
|
|
42
|
+
title="Bot Tokens",
|
|
43
|
+
category=CheckCategory.BOTS,
|
|
44
|
+
status=CheckStatus.PASS,
|
|
45
|
+
message=f"Found: {', '.join(found)}",
|
|
46
|
+
)
|
|
47
|
+
return CheckResult(
|
|
48
|
+
id="bot_tokens",
|
|
49
|
+
title="Bot Tokens",
|
|
50
|
+
category=CheckCategory.BOTS,
|
|
51
|
+
status=CheckStatus.WARN,
|
|
52
|
+
message="No bot tokens found in environment",
|
|
53
|
+
remediation="Set at least one: export TELEGRAM_BOT_TOKEN=your_token",
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@register_check(
|
|
58
|
+
id="bot_config",
|
|
59
|
+
title="Bot Config",
|
|
60
|
+
description="Check bot.yaml exists and is valid",
|
|
61
|
+
category=CheckCategory.BOTS,
|
|
62
|
+
severity=CheckSeverity.MEDIUM,
|
|
63
|
+
)
|
|
64
|
+
def check_bot_config(config: DoctorConfig) -> CheckResult:
|
|
65
|
+
"""Check bot.yaml exists and is valid."""
|
|
66
|
+
start = time.time()
|
|
67
|
+
from praisonai.cli._paths import resolve_bot_config_path
|
|
68
|
+
config_path = getattr(config, 'config_file', None) or resolve_bot_config_path("bot.yaml")
|
|
69
|
+
if not os.path.exists(config_path):
|
|
70
|
+
return CheckResult(
|
|
71
|
+
id="bot_config",
|
|
72
|
+
title="Bot Config",
|
|
73
|
+
category=CheckCategory.BOTS,
|
|
74
|
+
status=CheckStatus.WARN,
|
|
75
|
+
message=f"{config_path} not found",
|
|
76
|
+
remediation="Run 'praisonai onboard' to create one",
|
|
77
|
+
duration_ms=(time.time() - start) * 1000,
|
|
78
|
+
)
|
|
79
|
+
try:
|
|
80
|
+
from praisonai.bots._config_schema import load_and_validate_bot_yaml
|
|
81
|
+
load_and_validate_bot_yaml(config_path)
|
|
82
|
+
return CheckResult(
|
|
83
|
+
id="bot_config",
|
|
84
|
+
title="Bot Config",
|
|
85
|
+
category=CheckCategory.BOTS,
|
|
86
|
+
status=CheckStatus.PASS,
|
|
87
|
+
message=f"{config_path} valid",
|
|
88
|
+
duration_ms=(time.time() - start) * 1000,
|
|
89
|
+
)
|
|
90
|
+
except Exception as e:
|
|
91
|
+
return CheckResult(
|
|
92
|
+
id="bot_config",
|
|
93
|
+
title="Bot Config",
|
|
94
|
+
category=CheckCategory.BOTS,
|
|
95
|
+
status=CheckStatus.FAIL,
|
|
96
|
+
message=f"Invalid: {str(e)[:200]}",
|
|
97
|
+
remediation="Fix errors in bot.yaml or run 'praisonai onboard'",
|
|
98
|
+
duration_ms=(time.time() - start) * 1000,
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
@register_check(
|
|
103
|
+
id="bot_security",
|
|
104
|
+
title="Bot Security Config",
|
|
105
|
+
description="Check bot security configuration for safe defaults",
|
|
106
|
+
category=CheckCategory.BOTS,
|
|
107
|
+
severity=CheckSeverity.HIGH,
|
|
108
|
+
)
|
|
109
|
+
def check_bot_security(config: DoctorConfig) -> CheckResult:
|
|
110
|
+
"""Check bot security configuration for safe defaults."""
|
|
111
|
+
start = time.time()
|
|
112
|
+
from praisonai.cli._paths import resolve_bot_config_path
|
|
113
|
+
config_path = getattr(config, 'config_file', None) or resolve_bot_config_path("bot.yaml")
|
|
114
|
+
channel_warnings = []
|
|
115
|
+
global_warnings = []
|
|
116
|
+
|
|
117
|
+
# Check if bot.yaml exists
|
|
118
|
+
if not os.path.exists(config_path):
|
|
119
|
+
return CheckResult(
|
|
120
|
+
id="bot_security",
|
|
121
|
+
title="Bot Security Config",
|
|
122
|
+
category=CheckCategory.BOTS,
|
|
123
|
+
status=CheckStatus.SKIP,
|
|
124
|
+
message=f"{config_path} not found - security check skipped",
|
|
125
|
+
duration_ms=(time.time() - start) * 1000,
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
try:
|
|
129
|
+
from praisonai.bots._config_schema import load_and_validate_bot_yaml
|
|
130
|
+
config = load_and_validate_bot_yaml(config_path)
|
|
131
|
+
|
|
132
|
+
# Check for risky configurations across channels
|
|
133
|
+
for channel_name, channel_config in config.channels.items():
|
|
134
|
+
# Check for missing allowlists in production-like setups
|
|
135
|
+
if not channel_config.allowlist and not channel_config.blocklist:
|
|
136
|
+
channel_warnings.append(f"{channel_name}: No allowlist/blocklist configured")
|
|
137
|
+
|
|
138
|
+
# Check for overly permissive group policies
|
|
139
|
+
if channel_config.group_policy == "respond_all":
|
|
140
|
+
channel_warnings.append(f"{channel_name}: group_policy='respond_all' - consider 'mention_only' for security")
|
|
141
|
+
|
|
142
|
+
# Check for missing gateway pairing settings
|
|
143
|
+
gateway_secret = os.environ.get("PRAISONAI_GATEWAY_SECRET")
|
|
144
|
+
if not gateway_secret:
|
|
145
|
+
global_warnings.append("PRAISONAI_GATEWAY_SECRET not set - pairing codes will not persist across restarts")
|
|
146
|
+
|
|
147
|
+
# Determine status
|
|
148
|
+
all_warnings = channel_warnings + global_warnings
|
|
149
|
+
if all_warnings:
|
|
150
|
+
channel_count = len(channel_warnings)
|
|
151
|
+
global_count = len(global_warnings)
|
|
152
|
+
|
|
153
|
+
if channel_count > 0 and global_count > 0:
|
|
154
|
+
message = f"Security recommendations: {channel_count} channel(s) and {global_count} global setting(s) could be improved"
|
|
155
|
+
elif channel_count > 0:
|
|
156
|
+
message = f"Security recommendations: {channel_count} channel(s) could use stricter defaults"
|
|
157
|
+
else:
|
|
158
|
+
message = f"Security recommendations: {global_count} global setting(s) could be improved"
|
|
159
|
+
|
|
160
|
+
return CheckResult(
|
|
161
|
+
id="bot_security",
|
|
162
|
+
title="Bot Security Config",
|
|
163
|
+
category=CheckCategory.BOTS,
|
|
164
|
+
status=CheckStatus.WARN,
|
|
165
|
+
message=message,
|
|
166
|
+
details='\n'.join(all_warnings),
|
|
167
|
+
remediation="Consider allowlists for DM security and 'mention_only' group policy. See security docs for safe defaults.",
|
|
168
|
+
duration_ms=(time.time() - start) * 1000,
|
|
169
|
+
)
|
|
170
|
+
else:
|
|
171
|
+
return CheckResult(
|
|
172
|
+
id="bot_security",
|
|
173
|
+
title="Bot Security Config",
|
|
174
|
+
category=CheckCategory.BOTS,
|
|
175
|
+
status=CheckStatus.PASS,
|
|
176
|
+
message="Security configuration looks good",
|
|
177
|
+
duration_ms=(time.time() - start) * 1000,
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
except ValueError as e:
|
|
181
|
+
return CheckResult(
|
|
182
|
+
id="bot_security",
|
|
183
|
+
title="Bot Security Config",
|
|
184
|
+
category=CheckCategory.BOTS,
|
|
185
|
+
status=CheckStatus.FAIL,
|
|
186
|
+
message=f"Invalid bot security config: {str(e)[:100]}",
|
|
187
|
+
details=str(e)[:200],
|
|
188
|
+
remediation="Fix bot.yaml security settings and re-run doctor",
|
|
189
|
+
duration_ms=(time.time() - start) * 1000,
|
|
190
|
+
)
|
|
191
|
+
except Exception as e:
|
|
192
|
+
return CheckResult(
|
|
193
|
+
id="bot_security",
|
|
194
|
+
title="Bot Security Config",
|
|
195
|
+
category=CheckCategory.BOTS,
|
|
196
|
+
status=CheckStatus.ERROR,
|
|
197
|
+
message=f"Error checking security: {str(e)[:100]}",
|
|
198
|
+
remediation="Fix bot.yaml syntax errors first",
|
|
199
|
+
duration_ms=(time.time() - start) * 1000,
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
@register_check(
|
|
204
|
+
id="multi_channel_tokens",
|
|
205
|
+
title="Multi-Channel Token Configuration",
|
|
206
|
+
description="Check for duplicate tokens across channels and validate multi-channel setup",
|
|
207
|
+
category=CheckCategory.BOTS,
|
|
208
|
+
severity=CheckSeverity.MEDIUM,
|
|
209
|
+
)
|
|
210
|
+
def check_multi_channel_tokens(config: DoctorConfig) -> CheckResult:
|
|
211
|
+
"""Check multi-channel token configuration for duplicates and naming conventions."""
|
|
212
|
+
start = time.time()
|
|
213
|
+
from praisonai.cli._paths import resolve_bot_config_path
|
|
214
|
+
config_path = getattr(config, 'config_file', None) or resolve_bot_config_path("bot.yaml")
|
|
215
|
+
|
|
216
|
+
# Check if bot.yaml exists
|
|
217
|
+
if not os.path.exists(config_path):
|
|
218
|
+
return CheckResult(
|
|
219
|
+
id="multi_channel_tokens",
|
|
220
|
+
title="Multi-Channel Token Configuration",
|
|
221
|
+
category=CheckCategory.BOTS,
|
|
222
|
+
status=CheckStatus.SKIP,
|
|
223
|
+
message=f"{config_path} not found - multi-channel check skipped",
|
|
224
|
+
duration_ms=(time.time() - start) * 1000,
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
try:
|
|
228
|
+
from praisonai.bots._config_schema import load_and_validate_bot_yaml
|
|
229
|
+
config_data = load_and_validate_bot_yaml(config_path)
|
|
230
|
+
|
|
231
|
+
warnings = []
|
|
232
|
+
errors = []
|
|
233
|
+
|
|
234
|
+
# Extract token environment variables from channels
|
|
235
|
+
channel_tokens = {} # env_var -> [channel_keys_using_it]
|
|
236
|
+
channel_platforms = {} # platform -> [channel_keys]
|
|
237
|
+
|
|
238
|
+
for channel_name, channel_config in config_data.channels.items():
|
|
239
|
+
platform = channel_config.platform
|
|
240
|
+
token_ref = channel_config.token
|
|
241
|
+
|
|
242
|
+
# Track platforms
|
|
243
|
+
if platform not in channel_platforms:
|
|
244
|
+
channel_platforms[platform] = []
|
|
245
|
+
channel_platforms[platform].append(channel_name)
|
|
246
|
+
|
|
247
|
+
# Extract environment variable from token reference like ${TELEGRAM_BOT_TOKEN}
|
|
248
|
+
if token_ref and token_ref.startswith("${") and token_ref.endswith("}"):
|
|
249
|
+
env_var = token_ref[2:-1] # Remove ${ and }
|
|
250
|
+
if env_var not in channel_tokens:
|
|
251
|
+
channel_tokens[env_var] = []
|
|
252
|
+
channel_tokens[env_var].append(channel_name)
|
|
253
|
+
|
|
254
|
+
# Check for duplicate token usage by env var reference
|
|
255
|
+
for env_var, channels in channel_tokens.items():
|
|
256
|
+
if len(channels) > 1:
|
|
257
|
+
errors.append(f"Token {env_var} is used by multiple channels: {', '.join(channels)}")
|
|
258
|
+
|
|
259
|
+
# Also check duplicate token usage by resolved token value
|
|
260
|
+
token_value_to_channels = {} # token_value -> [channel_keys]
|
|
261
|
+
for env_var, channels in channel_tokens.items():
|
|
262
|
+
token_value = os.environ.get(env_var)
|
|
263
|
+
if token_value:
|
|
264
|
+
token_value_to_channels.setdefault(token_value, []).extend(channels)
|
|
265
|
+
|
|
266
|
+
for _, channels in token_value_to_channels.items():
|
|
267
|
+
unique_channels = sorted(set(channels))
|
|
268
|
+
if len(unique_channels) > 1:
|
|
269
|
+
errors.append(
|
|
270
|
+
f"Same bot token value is used by multiple channels: {', '.join(unique_channels)}"
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
# Check for multi-platform channels with good naming conventions
|
|
274
|
+
for platform, channels in channel_platforms.items():
|
|
275
|
+
if len(channels) > 1:
|
|
276
|
+
# Multiple channels on same platform - check naming convention
|
|
277
|
+
for channel_name in channels:
|
|
278
|
+
channel_config = config_data.channels[channel_name]
|
|
279
|
+
token_ref = channel_config.token
|
|
280
|
+
if token_ref and token_ref.startswith("${") and token_ref.endswith("}"):
|
|
281
|
+
env_var = token_ref[2:-1]
|
|
282
|
+
|
|
283
|
+
# Check if follows naming convention: PLATFORM_ROLE_BOT_TOKEN
|
|
284
|
+
expected_pattern = f"{platform.upper()}_"
|
|
285
|
+
if not env_var.startswith(expected_pattern) or not env_var.endswith("_BOT_TOKEN"):
|
|
286
|
+
warnings.append(f"Channel '{channel_name}' token '{env_var}' doesn't follow naming convention '{platform.upper()}_<ROLE>_BOT_TOKEN'")
|
|
287
|
+
|
|
288
|
+
# Check for missing tokens
|
|
289
|
+
missing_tokens = []
|
|
290
|
+
for env_var in channel_tokens.keys():
|
|
291
|
+
if not os.environ.get(env_var):
|
|
292
|
+
missing_tokens.append(env_var)
|
|
293
|
+
|
|
294
|
+
# Determine status
|
|
295
|
+
if errors:
|
|
296
|
+
return CheckResult(
|
|
297
|
+
id="multi_channel_tokens",
|
|
298
|
+
title="Multi-Channel Token Configuration",
|
|
299
|
+
category=CheckCategory.BOTS,
|
|
300
|
+
status=CheckStatus.FAIL,
|
|
301
|
+
message=f"Token configuration errors: {len(errors)} duplicate(s) found",
|
|
302
|
+
details='\n'.join(errors + warnings),
|
|
303
|
+
remediation="Each channel must have a unique bot token. Create separate bots in @BotFather and use unique environment variables.",
|
|
304
|
+
duration_ms=(time.time() - start) * 1000,
|
|
305
|
+
)
|
|
306
|
+
elif warnings or missing_tokens:
|
|
307
|
+
all_issues = warnings + [f"Missing token: {token}" for token in missing_tokens]
|
|
308
|
+
return CheckResult(
|
|
309
|
+
id="multi_channel_tokens",
|
|
310
|
+
title="Multi-Channel Token Configuration",
|
|
311
|
+
category=CheckCategory.BOTS,
|
|
312
|
+
status=CheckStatus.WARN,
|
|
313
|
+
message=f"Multi-channel setup with {len(all_issues)} recommendation(s)",
|
|
314
|
+
details='\n'.join(all_issues),
|
|
315
|
+
remediation="Consider following the naming convention PLATFORM_ROLE_BOT_TOKEN for clarity.",
|
|
316
|
+
duration_ms=(time.time() - start) * 1000,
|
|
317
|
+
)
|
|
318
|
+
else:
|
|
319
|
+
channel_count = sum(len(channels) for channels in channel_platforms.values())
|
|
320
|
+
return CheckResult(
|
|
321
|
+
id="multi_channel_tokens",
|
|
322
|
+
title="Multi-Channel Token Configuration",
|
|
323
|
+
category=CheckCategory.BOTS,
|
|
324
|
+
status=CheckStatus.PASS,
|
|
325
|
+
message=f"Multi-channel configuration looks good ({channel_count} channels)",
|
|
326
|
+
duration_ms=(time.time() - start) * 1000,
|
|
327
|
+
)
|
|
328
|
+
|
|
329
|
+
except Exception as e:
|
|
330
|
+
return CheckResult(
|
|
331
|
+
id="multi_channel_tokens",
|
|
332
|
+
title="Multi-Channel Token Configuration",
|
|
333
|
+
category=CheckCategory.BOTS,
|
|
334
|
+
status=CheckStatus.ERROR,
|
|
335
|
+
message=f"Error checking multi-channel config: {str(e)[:100]}",
|
|
336
|
+
remediation="Fix bot.yaml syntax errors first",
|
|
337
|
+
duration_ms=(time.time() - start) * 1000,
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
|