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.
Files changed (309) hide show
  1. praisonai_code/__init__.py +17 -0
  2. praisonai_code/cli/__init__.py +12 -0
  3. praisonai_code/cli/_forward_shim.py +10 -0
  4. praisonai_code/cli/_paths.py +88 -0
  5. praisonai_code/cli/_warnings.py +58 -0
  6. praisonai_code/cli/app.py +757 -0
  7. praisonai_code/cli/approval_backend.py +272 -0
  8. praisonai_code/cli/branding.py +94 -0
  9. praisonai_code/cli/commands/__init__.py +114 -0
  10. praisonai_code/cli/commands/acp.py +80 -0
  11. praisonai_code/cli/commands/agent.py +116 -0
  12. praisonai_code/cli/commands/agents.py +80 -0
  13. praisonai_code/cli/commands/app.py +139 -0
  14. praisonai_code/cli/commands/attach.py +95 -0
  15. praisonai_code/cli/commands/audit.py +102 -0
  16. praisonai_code/cli/commands/auth.py +508 -0
  17. praisonai_code/cli/commands/batch.py +848 -0
  18. praisonai_code/cli/commands/benchmark.py +286 -0
  19. praisonai_code/cli/commands/browser.py +299 -0
  20. praisonai_code/cli/commands/call.py +45 -0
  21. praisonai_code/cli/commands/chat.py +332 -0
  22. praisonai_code/cli/commands/checkpoint.py +170 -0
  23. praisonai_code/cli/commands/code.py +276 -0
  24. praisonai_code/cli/commands/command.py +114 -0
  25. praisonai_code/cli/commands/commit.py +47 -0
  26. praisonai_code/cli/commands/completion.py +333 -0
  27. praisonai_code/cli/commands/config.py +681 -0
  28. praisonai_code/cli/commands/context.py +414 -0
  29. praisonai_code/cli/commands/daemon.py +203 -0
  30. praisonai_code/cli/commands/debug.py +142 -0
  31. praisonai_code/cli/commands/deploy.py +71 -0
  32. praisonai_code/cli/commands/diag.py +55 -0
  33. praisonai_code/cli/commands/docs.py +1575 -0
  34. praisonai_code/cli/commands/doctor.py +332 -0
  35. praisonai_code/cli/commands/endpoints.py +51 -0
  36. praisonai_code/cli/commands/environment.py +179 -0
  37. praisonai_code/cli/commands/eval.py +131 -0
  38. praisonai_code/cli/commands/examples.py +953 -0
  39. praisonai_code/cli/commands/flow.py +436 -0
  40. praisonai_code/cli/commands/github.py +752 -0
  41. praisonai_code/cli/commands/hooks.py +74 -0
  42. praisonai_code/cli/commands/init.py +174 -0
  43. praisonai_code/cli/commands/knowledge.py +440 -0
  44. praisonai_code/cli/commands/langextract.py +120 -0
  45. praisonai_code/cli/commands/langfuse.py +984 -0
  46. praisonai_code/cli/commands/loop.py +211 -0
  47. praisonai_code/cli/commands/lsp.py +112 -0
  48. praisonai_code/cli/commands/managed.py +659 -0
  49. praisonai_code/cli/commands/mcp.py +763 -0
  50. praisonai_code/cli/commands/memory.py +298 -0
  51. praisonai_code/cli/commands/models.py +264 -0
  52. praisonai_code/cli/commands/n8n.py +326 -0
  53. praisonai_code/cli/commands/obs.py +19 -0
  54. praisonai_code/cli/commands/package.py +76 -0
  55. praisonai_code/cli/commands/paths.py +106 -0
  56. praisonai_code/cli/commands/permissions.py +272 -0
  57. praisonai_code/cli/commands/plugins.py +609 -0
  58. praisonai_code/cli/commands/port.py +530 -0
  59. praisonai_code/cli/commands/profile.py +466 -0
  60. praisonai_code/cli/commands/publish.py +193 -0
  61. praisonai_code/cli/commands/rag.py +913 -0
  62. praisonai_code/cli/commands/realtime.py +52 -0
  63. praisonai_code/cli/commands/recipe.py +684 -0
  64. praisonai_code/cli/commands/registry.py +59 -0
  65. praisonai_code/cli/commands/replay.py +830 -0
  66. praisonai_code/cli/commands/research.py +49 -0
  67. praisonai_code/cli/commands/retrieval.py +377 -0
  68. praisonai_code/cli/commands/rules.py +71 -0
  69. praisonai_code/cli/commands/run.py +1573 -0
  70. praisonai_code/cli/commands/sandbox.py +371 -0
  71. praisonai_code/cli/commands/schedule.py +529 -0
  72. praisonai_code/cli/commands/serve.py +690 -0
  73. praisonai_code/cli/commands/session.py +450 -0
  74. praisonai_code/cli/commands/setup.py +174 -0
  75. praisonai_code/cli/commands/skills.py +545 -0
  76. praisonai_code/cli/commands/standardise.py +711 -0
  77. praisonai_code/cli/commands/templates.py +54 -0
  78. praisonai_code/cli/commands/test.py +558 -0
  79. praisonai_code/cli/commands/todo.py +74 -0
  80. praisonai_code/cli/commands/tools.py +205 -0
  81. praisonai_code/cli/commands/traces.py +145 -0
  82. praisonai_code/cli/commands/tracker.py +852 -0
  83. praisonai_code/cli/commands/train.py +613 -0
  84. praisonai_code/cli/commands/ui.py +172 -0
  85. praisonai_code/cli/commands/up.py +354 -0
  86. praisonai_code/cli/commands/validate.py +291 -0
  87. praisonai_code/cli/commands/version.py +101 -0
  88. praisonai_code/cli/commands/workflow.py +97 -0
  89. praisonai_code/cli/config_loader.py +437 -0
  90. praisonai_code/cli/configuration/__init__.py +27 -0
  91. praisonai_code/cli/configuration/config.schema.json +57 -0
  92. praisonai_code/cli/configuration/credentials.py +446 -0
  93. praisonai_code/cli/configuration/loader.py +364 -0
  94. praisonai_code/cli/configuration/model_resolver.py +161 -0
  95. praisonai_code/cli/configuration/oauth.py +389 -0
  96. praisonai_code/cli/configuration/paths.py +224 -0
  97. praisonai_code/cli/configuration/resolver.py +687 -0
  98. praisonai_code/cli/configuration/schema.py +317 -0
  99. praisonai_code/cli/execution/__init__.py +99 -0
  100. praisonai_code/cli/execution/core.py +208 -0
  101. praisonai_code/cli/execution/profiler.py +898 -0
  102. praisonai_code/cli/execution/request.py +85 -0
  103. praisonai_code/cli/execution/result.py +74 -0
  104. praisonai_code/cli/fallback_schema.py +416 -0
  105. praisonai_code/cli/features/__init__.py +278 -0
  106. praisonai_code/cli/features/_endpoint_registry.py +64 -0
  107. praisonai_code/cli/features/_search_registry.py +43 -0
  108. praisonai_code/cli/features/acp.py +236 -0
  109. praisonai_code/cli/features/action_orchestrator.py +576 -0
  110. praisonai_code/cli/features/agent_scheduler.py +773 -0
  111. praisonai_code/cli/features/agent_tools.py +603 -0
  112. praisonai_code/cli/features/agents.py +397 -0
  113. praisonai_code/cli/features/at_mentions.py +471 -0
  114. praisonai_code/cli/features/audit_cli.py +270 -0
  115. praisonai_code/cli/features/auto_memory.py +182 -0
  116. praisonai_code/cli/features/auto_mode.py +552 -0
  117. praisonai_code/cli/features/autonomy_mode.py +546 -0
  118. praisonai_code/cli/features/background.py +356 -0
  119. praisonai_code/cli/features/base.py +168 -0
  120. praisonai_code/cli/features/benchmark.py +1462 -0
  121. praisonai_code/cli/features/capabilities.py +1326 -0
  122. praisonai_code/cli/features/checkpoints.py +345 -0
  123. praisonai_code/cli/features/cli_profiler.py +335 -0
  124. praisonai_code/cli/features/code_intelligence.py +666 -0
  125. praisonai_code/cli/features/compaction.py +294 -0
  126. praisonai_code/cli/features/compare.py +534 -0
  127. praisonai_code/cli/features/config_hierarchy.py +366 -0
  128. praisonai_code/cli/features/context_manager.py +597 -0
  129. praisonai_code/cli/features/cost_tracker.py +514 -0
  130. praisonai_code/cli/features/csv_test_runner.py +736 -0
  131. praisonai_code/cli/features/custom_definitions.py +790 -0
  132. praisonai_code/cli/features/debug.py +810 -0
  133. praisonai_code/cli/features/deploy.py +605 -0
  134. praisonai_code/cli/features/diag.py +289 -0
  135. praisonai_code/cli/features/display_jsonl.py +173 -0
  136. praisonai_code/cli/features/doctor/__init__.py +63 -0
  137. praisonai_code/cli/features/doctor/checks/__init__.py +29 -0
  138. praisonai_code/cli/features/doctor/checks/acp_checks.py +220 -0
  139. praisonai_code/cli/features/doctor/checks/bot_checks.py +340 -0
  140. praisonai_code/cli/features/doctor/checks/config_checks.py +373 -0
  141. praisonai_code/cli/features/doctor/checks/db_checks.py +366 -0
  142. praisonai_code/cli/features/doctor/checks/env_checks.py +637 -0
  143. praisonai_code/cli/features/doctor/checks/gateway_checks.py +387 -0
  144. praisonai_code/cli/features/doctor/checks/lsp_checks.py +231 -0
  145. praisonai_code/cli/features/doctor/checks/mcp_checks.py +367 -0
  146. praisonai_code/cli/features/doctor/checks/memory_checks.py +268 -0
  147. praisonai_code/cli/features/doctor/checks/network_checks.py +251 -0
  148. praisonai_code/cli/features/doctor/checks/obs_checks.py +328 -0
  149. praisonai_code/cli/features/doctor/checks/packaging_checks.py +422 -0
  150. praisonai_code/cli/features/doctor/checks/performance_checks.py +235 -0
  151. praisonai_code/cli/features/doctor/checks/permissions_checks.py +259 -0
  152. praisonai_code/cli/features/doctor/checks/runtime_checks.py +650 -0
  153. praisonai_code/cli/features/doctor/checks/runtime_migration_checks.py +220 -0
  154. praisonai_code/cli/features/doctor/checks/selftest_checks.py +322 -0
  155. praisonai_code/cli/features/doctor/checks/serve_checks.py +426 -0
  156. praisonai_code/cli/features/doctor/checks/skills_checks.py +327 -0
  157. praisonai_code/cli/features/doctor/checks/tools_checks.py +371 -0
  158. praisonai_code/cli/features/doctor/engine.py +266 -0
  159. praisonai_code/cli/features/doctor/formatters.py +377 -0
  160. praisonai_code/cli/features/doctor/handler.py +564 -0
  161. praisonai_code/cli/features/doctor/models.py +276 -0
  162. praisonai_code/cli/features/doctor/registry.py +239 -0
  163. praisonai_code/cli/features/endpoints.py +1016 -0
  164. praisonai_code/cli/features/eval.py +559 -0
  165. praisonai_code/cli/features/examples.py +707 -0
  166. praisonai_code/cli/features/external_agents.py +231 -0
  167. praisonai_code/cli/features/fast_context.py +410 -0
  168. praisonai_code/cli/features/file_history.py +320 -0
  169. praisonai_code/cli/features/flow_display.py +566 -0
  170. praisonai_code/cli/features/git_attribution.py +159 -0
  171. praisonai_code/cli/features/git_integration.py +651 -0
  172. praisonai_code/cli/features/guardrail.py +171 -0
  173. praisonai_code/cli/features/handoff.py +252 -0
  174. praisonai_code/cli/features/hooks.py +583 -0
  175. praisonai_code/cli/features/hybrid_workflow.py +391 -0
  176. praisonai_code/cli/features/image.py +384 -0
  177. praisonai_code/cli/features/interactive_core_headless.py +450 -0
  178. praisonai_code/cli/features/interactive_runtime.py +600 -0
  179. praisonai_code/cli/features/interactive_test_harness.py +537 -0
  180. praisonai_code/cli/features/interactive_tools.py +428 -0
  181. praisonai_code/cli/features/interactive_tui.py +603 -0
  182. praisonai_code/cli/features/job_workflow.py +906 -0
  183. praisonai_code/cli/features/jobs.py +632 -0
  184. praisonai_code/cli/features/knowledge.py +531 -0
  185. praisonai_code/cli/features/knowledge_cli.py +438 -0
  186. praisonai_code/cli/features/lite.py +244 -0
  187. praisonai_code/cli/features/logs.py +200 -0
  188. praisonai_code/cli/features/lsp_cli.py +225 -0
  189. praisonai_code/cli/features/lsp_diagnostics.py +185 -0
  190. praisonai_code/cli/features/mcp.py +344 -0
  191. praisonai_code/cli/features/message_queue.py +587 -0
  192. praisonai_code/cli/features/metrics.py +210 -0
  193. praisonai_code/cli/features/migrate.py +1329 -0
  194. praisonai_code/cli/features/migration_flow.py +463 -0
  195. praisonai_code/cli/features/migration_spec.py +276 -0
  196. praisonai_code/cli/features/n8n.py +703 -0
  197. praisonai_code/cli/features/observability.py +293 -0
  198. praisonai_code/cli/features/ollama.py +361 -0
  199. praisonai_code/cli/features/output_modes.py +155 -0
  200. praisonai_code/cli/features/output_style.py +273 -0
  201. praisonai_code/cli/features/package.py +631 -0
  202. praisonai_code/cli/features/performance.py +308 -0
  203. praisonai_code/cli/features/persistence.py +636 -0
  204. praisonai_code/cli/features/profiler/__init__.py +81 -0
  205. praisonai_code/cli/features/profiler/core.py +558 -0
  206. praisonai_code/cli/features/profiler/optimizations.py +652 -0
  207. praisonai_code/cli/features/profiler/suite.py +386 -0
  208. praisonai_code/cli/features/queue/__init__.py +73 -0
  209. praisonai_code/cli/features/queue/manager.py +435 -0
  210. praisonai_code/cli/features/queue/models.py +289 -0
  211. praisonai_code/cli/features/queue/persistence.py +564 -0
  212. praisonai_code/cli/features/queue/scheduler.py +529 -0
  213. praisonai_code/cli/features/queue/worker.py +400 -0
  214. praisonai_code/cli/features/recipe.py +2187 -0
  215. praisonai_code/cli/features/recipe_creator.py +996 -0
  216. praisonai_code/cli/features/recipe_optimizer.py +1364 -0
  217. praisonai_code/cli/features/recipe_prompts.py +226 -0
  218. praisonai_code/cli/features/registry.py +229 -0
  219. praisonai_code/cli/features/repo_map.py +860 -0
  220. praisonai_code/cli/features/router.py +466 -0
  221. praisonai_code/cli/features/safe_shell.py +427 -0
  222. praisonai_code/cli/features/sandbox_cli.py +283 -0
  223. praisonai_code/cli/features/sandbox_executor.py +536 -0
  224. praisonai_code/cli/features/sdk_knowledge.py +500 -0
  225. praisonai_code/cli/features/session.py +222 -0
  226. praisonai_code/cli/features/session_checkpoints.py +208 -0
  227. praisonai_code/cli/features/setup/__init__.py +9 -0
  228. praisonai_code/cli/features/setup/handler.py +355 -0
  229. praisonai_code/cli/features/setup/templates.py +62 -0
  230. praisonai_code/cli/features/skills.py +940 -0
  231. praisonai_code/cli/features/slash_commands.py +692 -0
  232. praisonai_code/cli/features/telemetry.py +179 -0
  233. praisonai_code/cli/features/templates.py +1390 -0
  234. praisonai_code/cli/features/thinking.py +343 -0
  235. praisonai_code/cli/features/todo.py +334 -0
  236. praisonai_code/cli/features/tools.py +680 -0
  237. praisonai_code/cli/features/tui/__init__.py +83 -0
  238. praisonai_code/cli/features/tui/app.py +871 -0
  239. praisonai_code/cli/features/tui/cli.py +580 -0
  240. praisonai_code/cli/features/tui/config.py +150 -0
  241. praisonai_code/cli/features/tui/debug.py +526 -0
  242. praisonai_code/cli/features/tui/events.py +99 -0
  243. praisonai_code/cli/features/tui/mock_provider.py +328 -0
  244. praisonai_code/cli/features/tui/orchestrator.py +652 -0
  245. praisonai_code/cli/features/tui/screens/__init__.py +50 -0
  246. praisonai_code/cli/features/tui/screens/help.py +157 -0
  247. praisonai_code/cli/features/tui/screens/main.py +568 -0
  248. praisonai_code/cli/features/tui/screens/queue.py +174 -0
  249. praisonai_code/cli/features/tui/screens/session.py +124 -0
  250. praisonai_code/cli/features/tui/screens/settings.py +148 -0
  251. praisonai_code/cli/features/tui/session_store.py +198 -0
  252. praisonai_code/cli/features/tui/widgets/__init__.py +56 -0
  253. praisonai_code/cli/features/tui/widgets/chat.py +263 -0
  254. praisonai_code/cli/features/tui/widgets/command_popup.py +258 -0
  255. praisonai_code/cli/features/tui/widgets/composer.py +292 -0
  256. praisonai_code/cli/features/tui/widgets/file_popup.py +207 -0
  257. praisonai_code/cli/features/tui/widgets/queue_panel.py +223 -0
  258. praisonai_code/cli/features/tui/widgets/status.py +181 -0
  259. praisonai_code/cli/features/tui/widgets/tool_panel.py +307 -0
  260. praisonai_code/cli/features/wizard.py +289 -0
  261. praisonai_code/cli/features/workflow.py +802 -0
  262. praisonai_code/cli/features/yaml_utils.py +321 -0
  263. praisonai_code/cli/interactive/__init__.py +48 -0
  264. praisonai_code/cli/interactive/async_tui.py +1218 -0
  265. praisonai_code/cli/interactive/config.py +139 -0
  266. praisonai_code/cli/interactive/core.py +618 -0
  267. praisonai_code/cli/interactive/events.py +131 -0
  268. praisonai_code/cli/interactive/frontends/__init__.py +31 -0
  269. praisonai_code/cli/interactive/frontends/rich_frontend.py +462 -0
  270. praisonai_code/cli/interactive/frontends/textual_frontend.py +157 -0
  271. praisonai_code/cli/interactive/praison_io.py +502 -0
  272. praisonai_code/cli/interactive/repl.py +297 -0
  273. praisonai_code/cli/interactive/split_tui.py +456 -0
  274. praisonai_code/cli/interactive/tui_app.py +457 -0
  275. praisonai_code/cli/langfuse_client.py +360 -0
  276. praisonai_code/cli/main.py +7421 -0
  277. praisonai_code/cli/output/__init__.py +25 -0
  278. praisonai_code/cli/output/console.py +456 -0
  279. praisonai_code/cli/output/event_bridge.py +191 -0
  280. praisonai_code/cli/schedule_cli.py +54 -0
  281. praisonai_code/cli/schema_provider.py +23 -0
  282. praisonai_code/cli/session/__init__.py +16 -0
  283. praisonai_code/cli/session/resume.py +148 -0
  284. praisonai_code/cli/session/unified.py +548 -0
  285. praisonai_code/cli/state/__init__.py +31 -0
  286. praisonai_code/cli/state/identifiers.py +161 -0
  287. praisonai_code/cli/state/project_sessions.py +383 -0
  288. praisonai_code/cli/state/sessions.py +390 -0
  289. praisonai_code/cli/ui/__init__.py +160 -0
  290. praisonai_code/cli/ui/config.py +46 -0
  291. praisonai_code/cli/ui/events.py +61 -0
  292. praisonai_code/cli/ui/mg_backend.py +342 -0
  293. praisonai_code/cli/ui/plain.py +133 -0
  294. praisonai_code/cli/ui/rich_backend.py +162 -0
  295. praisonai_code/cli/unified_schema.py +655 -0
  296. praisonai_code/cli/utils/env_utils.py +126 -0
  297. praisonai_code/cli/utils/project.py +131 -0
  298. praisonai_code/cli_backends/__init__.py +73 -0
  299. praisonai_code/cli_backends/claude.py +373 -0
  300. praisonai_code/cli_backends/registry.py +113 -0
  301. praisonai_code/runtime/__init__.py +36 -0
  302. praisonai_code/runtime/__main__.py +81 -0
  303. praisonai_code/runtime/client.py +131 -0
  304. praisonai_code/runtime/descriptor.py +209 -0
  305. praisonai_code/runtime/server.py +356 -0
  306. praisonai_code-0.0.1.dist-info/METADATA +80 -0
  307. praisonai_code-0.0.1.dist-info/RECORD +309 -0
  308. praisonai_code-0.0.1.dist-info/WHEEL +5 -0
  309. praisonai_code-0.0.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,387 @@
1
+ """
2
+ Gateway-specific doctor checks for PraisonAI.
3
+
4
+ Validates gateway/bot configuration, performs migrations, and checks security.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import os
10
+ import time
11
+ from typing import List, Optional
12
+
13
+ from ..models import CheckResult, CheckStatus, CheckCategory, CheckSeverity, DoctorConfig
14
+ from ..registry import register_check
15
+
16
+
17
+ @register_check(
18
+ id="gateway_config_validation",
19
+ title="Gateway Config Validation",
20
+ description="Validate gateway/bot configuration files",
21
+ category=CheckCategory.BOTS,
22
+ severity=CheckSeverity.HIGH,
23
+ )
24
+ def check_gateway_config_validation(config: DoctorConfig) -> CheckResult:
25
+ """Validate gateway/bot configuration using canonical schema."""
26
+ start = time.time()
27
+
28
+ # Check multiple possible config locations
29
+ config_paths = []
30
+ if hasattr(config, 'config_file') and config.config_file:
31
+ config_paths.append(config.config_file)
32
+
33
+ # Check common locations
34
+ from praisonai.cli._paths import resolve_bot_config_path
35
+ config_paths.extend([
36
+ resolve_bot_config_path("gateway.yaml"),
37
+ resolve_bot_config_path("bot.yaml"),
38
+ "gateway.yaml",
39
+ "bot.yaml",
40
+ ])
41
+
42
+ # Find first existing config
43
+ config_path = None
44
+ for path in config_paths:
45
+ if os.path.exists(path):
46
+ config_path = path
47
+ break
48
+
49
+ if not config_path:
50
+ return CheckResult(
51
+ id="gateway_config_validation",
52
+ title="Gateway Config Validation",
53
+ category=CheckCategory.BOTS,
54
+ status=CheckStatus.WARN,
55
+ message="No gateway/bot config found",
56
+ remediation="Run 'praisonai onboard' to create bot.yaml",
57
+ duration_ms=(time.time() - start) * 1000,
58
+ )
59
+
60
+ try:
61
+ from praisonai.bots._config_schema import load_and_validate_gateway_yaml
62
+ validated_config = load_and_validate_gateway_yaml(config_path)
63
+
64
+ channel_count = len(validated_config.channels)
65
+ agent_count = 1 if validated_config.agent else 0
66
+ if validated_config.agents:
67
+ agent_count = len(validated_config.agents)
68
+
69
+ return CheckResult(
70
+ id="gateway_config_validation",
71
+ title="Gateway Config Validation",
72
+ category=CheckCategory.BOTS,
73
+ status=CheckStatus.PASS,
74
+ message=f"{config_path} valid ({channel_count} channels, {agent_count} agents)",
75
+ duration_ms=(time.time() - start) * 1000,
76
+ )
77
+ except ValueError as e:
78
+ return CheckResult(
79
+ id="gateway_config_validation",
80
+ title="Gateway Config Validation",
81
+ category=CheckCategory.BOTS,
82
+ status=CheckStatus.FAIL,
83
+ message=f"Invalid config: {str(e)[:100]}",
84
+ details=str(e),
85
+ remediation="Fix validation errors in your config file",
86
+ duration_ms=(time.time() - start) * 1000,
87
+ )
88
+ except Exception as e:
89
+ return CheckResult(
90
+ id="gateway_config_validation",
91
+ title="Gateway Config Validation",
92
+ category=CheckCategory.BOTS,
93
+ status=CheckStatus.ERROR,
94
+ message=f"Error validating config: {str(e)[:100]}",
95
+ remediation="Check config file syntax",
96
+ duration_ms=(time.time() - start) * 1000,
97
+ )
98
+
99
+
100
+ @register_check(
101
+ id="gateway_security",
102
+ title="Gateway Security Settings",
103
+ description="Check gateway security configuration",
104
+ category=CheckCategory.BOTS,
105
+ severity=CheckSeverity.HIGH,
106
+ )
107
+ def check_gateway_security(config: DoctorConfig) -> CheckResult:
108
+ """Check gateway security settings for safe defaults."""
109
+ start = time.time()
110
+
111
+ # Find config file
112
+ from praisonai.cli._paths import resolve_bot_config_path
113
+ config_path = None
114
+ for path in [resolve_bot_config_path("gateway.yaml"), resolve_bot_config_path("bot.yaml"), "gateway.yaml", "bot.yaml"]:
115
+ if os.path.exists(path):
116
+ config_path = path
117
+ break
118
+
119
+ if not config_path:
120
+ return CheckResult(
121
+ id="gateway_security",
122
+ title="Gateway Security Settings",
123
+ category=CheckCategory.BOTS,
124
+ status=CheckStatus.SKIP,
125
+ message="No config found - security check skipped",
126
+ duration_ms=(time.time() - start) * 1000,
127
+ )
128
+
129
+ try:
130
+ from praisonai.bots._config_schema import load_and_validate_gateway_yaml
131
+ validated_config = load_and_validate_gateway_yaml(config_path)
132
+
133
+ security_issues = []
134
+ warnings = []
135
+
136
+ for channel_name, channel in validated_config.channels.items():
137
+ # Check for open allowlists (CRITICAL)
138
+ if not channel.allowed_users and not channel.allowlist and not channel.blocklist:
139
+ security_issues.append(
140
+ f"{channel_name}: No user restrictions - bot responds to EVERYONE!"
141
+ )
142
+
143
+ # Check for overly permissive group policies (WARNING)
144
+ if channel.group_policy == "respond_all":
145
+ warnings.append(
146
+ f"{channel_name}: Using 'respond_all' - consider 'mention_only'"
147
+ )
148
+
149
+ # Check for missing tokens
150
+ if not channel.token:
151
+ security_issues.append(
152
+ f"{channel_name}: No token configured"
153
+ )
154
+
155
+ # Check gateway auth token
156
+ if not os.environ.get("GATEWAY_AUTH_TOKEN"):
157
+ warnings.append("GATEWAY_AUTH_TOKEN not set - gateway has no authentication")
158
+
159
+ if security_issues:
160
+ return CheckResult(
161
+ id="gateway_security",
162
+ title="Gateway Security Settings",
163
+ category=CheckCategory.BOTS,
164
+ status=CheckStatus.FAIL,
165
+ message=f"Critical security issues: {len(security_issues)} problem(s)",
166
+ details="\n".join(security_issues + warnings),
167
+ remediation="Add allowed_users to restrict bot access. Use 'mention_only' for groups.",
168
+ duration_ms=(time.time() - start) * 1000,
169
+ )
170
+ elif warnings:
171
+ return CheckResult(
172
+ id="gateway_security",
173
+ title="Gateway Security Settings",
174
+ category=CheckCategory.BOTS,
175
+ status=CheckStatus.WARN,
176
+ message=f"Security recommendations: {len(warnings)} improvement(s)",
177
+ details="\n".join(warnings),
178
+ remediation="Consider stricter security settings",
179
+ duration_ms=(time.time() - start) * 1000,
180
+ )
181
+ else:
182
+ return CheckResult(
183
+ id="gateway_security",
184
+ title="Gateway Security Settings",
185
+ category=CheckCategory.BOTS,
186
+ status=CheckStatus.PASS,
187
+ message="Security configuration looks good",
188
+ duration_ms=(time.time() - start) * 1000,
189
+ )
190
+
191
+ except Exception as e:
192
+ return CheckResult(
193
+ id="gateway_security",
194
+ title="Gateway Security Settings",
195
+ category=CheckCategory.BOTS,
196
+ status=CheckStatus.ERROR,
197
+ message=f"Error checking security: {str(e)[:100]}",
198
+ remediation="Fix config errors first",
199
+ duration_ms=(time.time() - start) * 1000,
200
+ )
201
+
202
+
203
+ @register_check(
204
+ id="gateway_config_migration",
205
+ title="Gateway Config Migration",
206
+ description="Check if config needs migration to canonical format",
207
+ category=CheckCategory.BOTS,
208
+ severity=CheckSeverity.MEDIUM,
209
+ )
210
+ def check_gateway_config_migration(config: DoctorConfig) -> CheckResult:
211
+ """Check if configuration needs migration to canonical format."""
212
+ start = time.time()
213
+
214
+ # Find config file
215
+ from praisonai.cli._paths import resolve_bot_config_path
216
+ config_path = None
217
+ for path in [resolve_bot_config_path("gateway.yaml"), resolve_bot_config_path("bot.yaml"), "gateway.yaml", "bot.yaml"]:
218
+ if os.path.exists(path):
219
+ config_path = path
220
+ break
221
+
222
+ if not config_path:
223
+ return CheckResult(
224
+ id="gateway_config_migration",
225
+ title="Gateway Config Migration",
226
+ category=CheckCategory.BOTS,
227
+ status=CheckStatus.SKIP,
228
+ message="No config found - migration check skipped",
229
+ duration_ms=(time.time() - start) * 1000,
230
+ )
231
+
232
+ try:
233
+ import yaml
234
+ import copy
235
+ with open(config_path) as f:
236
+ raw = yaml.safe_load(f) or {}
237
+
238
+ # Deep copy to preserve original for comparison
239
+ original = copy.deepcopy(raw)
240
+
241
+ from praisonai.bots._config_schema import migrate_legacy_config
242
+ migrated = migrate_legacy_config(copy.deepcopy(original))
243
+
244
+ # Check if migration changed anything
245
+ if migrated != original:
246
+ changes = []
247
+
248
+ # Detect specific migrations
249
+ if "platform" in raw and "token" in raw and "channels" not in raw:
250
+ changes.append("Single-bot format can be migrated to multi-channel format")
251
+
252
+ if "platforms" in raw and "channels" not in raw:
253
+ changes.append("BotOS platforms format can be migrated to channels format")
254
+
255
+ # Check for string allowed_users
256
+ if "channels" in raw:
257
+ for channel_name, channel in raw["channels"].items():
258
+ if isinstance(channel.get("allowed_users"), str):
259
+ changes.append(f"{channel_name}: allowed_users string → list migration available")
260
+
261
+ return CheckResult(
262
+ id="gateway_config_migration",
263
+ title="Gateway Config Migration",
264
+ category=CheckCategory.BOTS,
265
+ status=CheckStatus.WARN,
266
+ message=f"Config can be migrated: {len(changes)} change(s)",
267
+ details="\n".join(changes),
268
+ remediation="Run 'praisonai gateway migrate' to update config to latest format",
269
+ duration_ms=(time.time() - start) * 1000,
270
+ )
271
+ else:
272
+ return CheckResult(
273
+ id="gateway_config_migration",
274
+ title="Gateway Config Migration",
275
+ category=CheckCategory.BOTS,
276
+ status=CheckStatus.PASS,
277
+ message="Config uses current format",
278
+ duration_ms=(time.time() - start) * 1000,
279
+ )
280
+
281
+ except Exception as e:
282
+ return CheckResult(
283
+ id="gateway_config_migration",
284
+ title="Gateway Config Migration",
285
+ category=CheckCategory.BOTS,
286
+ status=CheckStatus.ERROR,
287
+ message=f"Error checking migration: {str(e)[:100]}",
288
+ remediation="Fix config syntax errors first",
289
+ duration_ms=(time.time() - start) * 1000,
290
+ )
291
+
292
+
293
+ @register_check(
294
+ id="gateway_env_substitution",
295
+ title="Gateway Environment Variables",
296
+ description="Check environment variable substitution in config",
297
+ category=CheckCategory.BOTS,
298
+ severity=CheckSeverity.MEDIUM,
299
+ )
300
+ def check_gateway_env_substitution(config: DoctorConfig) -> CheckResult:
301
+ """Check that environment variables referenced in config are set."""
302
+ start = time.time()
303
+
304
+ # Find config file
305
+ from praisonai.cli._paths import resolve_bot_config_path
306
+ config_path = None
307
+ for path in [resolve_bot_config_path("gateway.yaml"), resolve_bot_config_path("bot.yaml"), "gateway.yaml", "bot.yaml"]:
308
+ if os.path.exists(path):
309
+ config_path = path
310
+ break
311
+
312
+ if not config_path:
313
+ return CheckResult(
314
+ id="gateway_env_substitution",
315
+ title="Gateway Environment Variables",
316
+ category=CheckCategory.BOTS,
317
+ status=CheckStatus.SKIP,
318
+ message="No config found - env check skipped",
319
+ duration_ms=(time.time() - start) * 1000,
320
+ )
321
+
322
+ try:
323
+ import re
324
+ from praisonai.cli.utils.env_utils import load_env_file
325
+
326
+ # Load .env file first to ensure all env vars are available
327
+ try:
328
+ load_env_file()
329
+ except:
330
+ pass # .env file might not exist, continue anyway
331
+
332
+ with open(config_path) as f:
333
+ content = f.read()
334
+
335
+ # Find all ${VAR} references
336
+ env_refs = re.findall(r'\$\{([^}]+)\}', content)
337
+
338
+ if not env_refs:
339
+ return CheckResult(
340
+ id="gateway_env_substitution",
341
+ title="Gateway Environment Variables",
342
+ category=CheckCategory.BOTS,
343
+ status=CheckStatus.PASS,
344
+ message="No environment variables referenced",
345
+ duration_ms=(time.time() - start) * 1000,
346
+ )
347
+
348
+ # Check which are set
349
+ missing = []
350
+ found = []
351
+ for env_var in sorted(set(env_refs)):
352
+ if env_var in os.environ:
353
+ found.append(env_var)
354
+ else:
355
+ missing.append(env_var)
356
+
357
+ if missing:
358
+ return CheckResult(
359
+ id="gateway_env_substitution",
360
+ title="Gateway Environment Variables",
361
+ category=CheckCategory.BOTS,
362
+ status=CheckStatus.FAIL,
363
+ message=f"Missing environment variables: {len(missing)}",
364
+ details=f"Not set: {', '.join(missing)}",
365
+ remediation=f"Set missing variables: {' '.join(f'export {v}=...' for v in missing)}",
366
+ duration_ms=(time.time() - start) * 1000,
367
+ )
368
+ else:
369
+ return CheckResult(
370
+ id="gateway_env_substitution",
371
+ title="Gateway Environment Variables",
372
+ category=CheckCategory.BOTS,
373
+ status=CheckStatus.PASS,
374
+ message=f"All {len(found)} environment variables are set",
375
+ duration_ms=(time.time() - start) * 1000,
376
+ )
377
+
378
+ except Exception as e:
379
+ return CheckResult(
380
+ id="gateway_env_substitution",
381
+ title="Gateway Environment Variables",
382
+ category=CheckCategory.BOTS,
383
+ status=CheckStatus.ERROR,
384
+ message=f"Error checking env vars: {str(e)[:100]}",
385
+ remediation="Check config file access",
386
+ duration_ms=(time.time() - start) * 1000,
387
+ )
@@ -0,0 +1,231 @@
1
+ """
2
+ LSP health checks for PraisonAI Doctor.
3
+ """
4
+
5
+ import asyncio
6
+ import logging
7
+ import shutil
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="lsp_module",
17
+ title="LSP Module Available",
18
+ category=CheckCategory.TOOLS,
19
+ description="Check if LSP module is installed"
20
+ )
21
+ def check_lsp_module(config=None) -> CheckResult:
22
+ """Check if LSP module is available."""
23
+ try:
24
+ import importlib.util
25
+ spec = importlib.util.find_spec("praisonaiagents.lsp")
26
+
27
+ if spec is not None:
28
+ return CheckResult(
29
+ id="lsp_module",
30
+ title="LSP Module Available",
31
+ category=CheckCategory.TOOLS,
32
+ status=CheckStatus.PASS,
33
+ message="LSP module is available",
34
+ metadata={"module": "praisonaiagents.lsp"}
35
+ )
36
+ else:
37
+ return CheckResult(
38
+ id="lsp_module",
39
+ title="LSP Module Available",
40
+ category=CheckCategory.TOOLS,
41
+ status=CheckStatus.WARN,
42
+ message="LSP module not found",
43
+ remediation="Install praisonaiagents with LSP support"
44
+ )
45
+ except Exception as e:
46
+ return CheckResult(
47
+ id="lsp_module",
48
+ title="LSP Module Available",
49
+ category=CheckCategory.TOOLS,
50
+ status=CheckStatus.FAIL,
51
+ message=f"Error checking LSP module: {e}",
52
+ metadata={"error": str(e)}
53
+ )
54
+
55
+
56
+ @register_check(
57
+ id="lsp_client",
58
+ title="LSP Client Import",
59
+ category=CheckCategory.TOOLS,
60
+ description="Check if LSP client can be imported"
61
+ )
62
+ def check_lsp_client(config=None) -> CheckResult:
63
+ """Check if LSP client can be imported."""
64
+ try:
65
+ from praisonaiagents.lsp import LSPClient # noqa: F401
66
+
67
+ return CheckResult(
68
+ id="lsp_client",
69
+ title="LSP Client Import",
70
+ category=CheckCategory.TOOLS,
71
+ status=CheckStatus.PASS,
72
+ message="LSP client can be imported",
73
+ metadata={"class": "LSPClient"}
74
+ )
75
+ except ImportError as e:
76
+ return CheckResult(
77
+ id="lsp_client",
78
+ title="LSP Client Import",
79
+ category=CheckCategory.TOOLS,
80
+ status=CheckStatus.WARN,
81
+ message=f"LSP client import failed: {e}",
82
+ metadata={"error": str(e)}
83
+ )
84
+ except Exception as e:
85
+ return CheckResult(
86
+ id="lsp_client",
87
+ title="LSP Client Import",
88
+ category=CheckCategory.TOOLS,
89
+ status=CheckStatus.FAIL,
90
+ message=f"Error importing LSP client: {e}",
91
+ metadata={"error": str(e)}
92
+ )
93
+
94
+
95
+ @register_check(
96
+ id="lsp_server_python",
97
+ title="Python LSP Server",
98
+ category=CheckCategory.TOOLS,
99
+ description="Check if Python language server is available"
100
+ )
101
+ def check_lsp_server_python(config=None) -> CheckResult:
102
+ """Check if Python language server is available."""
103
+ servers = [
104
+ ("pyright-langserver", "pyright"),
105
+ ("pylsp", "python-lsp-server"),
106
+ ("pyls", "python-language-server"),
107
+ ]
108
+
109
+ found = []
110
+ for cmd, package in servers:
111
+ if shutil.which(cmd):
112
+ found.append({"command": cmd, "package": package})
113
+
114
+ if found:
115
+ return CheckResult(
116
+ id="lsp_server_python",
117
+ title="Python LSP Server",
118
+ category=CheckCategory.TOOLS,
119
+ status=CheckStatus.PASS,
120
+ message=f"Python LSP server available: {found[0]['command']}",
121
+ metadata={"servers": found}
122
+ )
123
+ else:
124
+ return CheckResult(
125
+ id="lsp_server_python",
126
+ title="Python LSP Server",
127
+ category=CheckCategory.TOOLS,
128
+ status=CheckStatus.WARN,
129
+ message="No Python LSP server found",
130
+ remediation="Install with: npm install -g pyright OR pip install python-lsp-server"
131
+ )
132
+
133
+
134
+ @register_check(
135
+ id="lsp_server_typescript",
136
+ title="TypeScript LSP Server",
137
+ category=CheckCategory.TOOLS,
138
+ description="Check if TypeScript language server is available"
139
+ )
140
+ def check_lsp_server_typescript(config=None) -> CheckResult:
141
+ """Check if TypeScript language server is available."""
142
+ if shutil.which("typescript-language-server"):
143
+ return CheckResult(
144
+ id="lsp_server_typescript",
145
+ title="TypeScript LSP Server",
146
+ category=CheckCategory.TOOLS,
147
+ status=CheckStatus.PASS,
148
+ message="TypeScript LSP server available",
149
+ metadata={"command": "typescript-language-server"}
150
+ )
151
+ elif shutil.which("tsserver"):
152
+ return CheckResult(
153
+ id="lsp_server_typescript",
154
+ title="TypeScript LSP Server",
155
+ category=CheckCategory.TOOLS,
156
+ status=CheckStatus.PASS,
157
+ message="TypeScript server available (tsserver)",
158
+ metadata={"command": "tsserver"}
159
+ )
160
+ else:
161
+ return CheckResult(
162
+ id="lsp_server_typescript",
163
+ title="TypeScript LSP Server",
164
+ category=CheckCategory.TOOLS,
165
+ status=CheckStatus.SKIP,
166
+ message="TypeScript LSP server not found (optional)",
167
+ remediation="Install with: npm install -g typescript-language-server typescript"
168
+ )
169
+
170
+
171
+ @register_check(
172
+ id="lsp_runtime",
173
+ title="LSP Runtime Check",
174
+ category=CheckCategory.TOOLS,
175
+ description="Check if LSP runtime can start",
176
+ requires_deep=True
177
+ )
178
+ def check_lsp_runtime(config=None) -> CheckResult:
179
+ """Check if LSP runtime can start."""
180
+ try:
181
+ from praisonai.cli.features.interactive_runtime import create_runtime
182
+
183
+ async def _check():
184
+ runtime = create_runtime(
185
+ workspace=".",
186
+ lsp=True,
187
+ acp=False
188
+ )
189
+ try:
190
+ await runtime.start()
191
+ ready = runtime.lsp_ready
192
+ error = runtime._lsp_state.error
193
+ return ready, error
194
+ finally:
195
+ await runtime.stop()
196
+
197
+ ready, error = asyncio.run(_check())
198
+
199
+ if ready:
200
+ return CheckResult(
201
+ id="lsp_runtime",
202
+ title="LSP Runtime Check",
203
+ category=CheckCategory.TOOLS,
204
+ status=CheckStatus.PASS,
205
+ message="LSP runtime started successfully",
206
+ metadata={"ready": True}
207
+ )
208
+ else:
209
+ return CheckResult(
210
+ id="lsp_runtime",
211
+ title="LSP Runtime Check",
212
+ category=CheckCategory.TOOLS,
213
+ status=CheckStatus.WARN,
214
+ message=f"LSP runtime failed to start: {error}",
215
+ metadata={"ready": False, "error": str(error)}
216
+ )
217
+
218
+ except Exception as e:
219
+ return CheckResult(
220
+ id="lsp_runtime",
221
+ title="LSP Runtime Check",
222
+ category=CheckCategory.TOOLS,
223
+ status=CheckStatus.FAIL,
224
+ message=f"LSP runtime check failed: {e}",
225
+ metadata={"error": str(e)}
226
+ )
227
+
228
+
229
+ def register_lsp_checks():
230
+ """Register all LSP checks."""
231
+ pass