cct-cli 0.7.9.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (325) hide show
  1. calc_terminal/__init__.py +14 -0
  2. calc_terminal/__main__.py +14 -0
  3. calc_terminal/activity.py +1334 -0
  4. calc_terminal/agent.py +3387 -0
  5. calc_terminal/agent_runtime.py +519 -0
  6. calc_terminal/ai_context.py +447 -0
  7. calc_terminal/ai_modes.py +752 -0
  8. calc_terminal/ai_personalization.py +286 -0
  9. calc_terminal/ai_preview_feedback.py +213 -0
  10. calc_terminal/aicore.py +2572 -0
  11. calc_terminal/anim.py +367 -0
  12. calc_terminal/app.py +3685 -0
  13. calc_terminal/art.py +639 -0
  14. calc_terminal/atomsim.py +368 -0
  15. calc_terminal/attachments.py +743 -0
  16. calc_terminal/benchmark_system.py +414 -0
  17. calc_terminal/browser/__init__.py +36 -0
  18. calc_terminal/browser/browser_state.py +346 -0
  19. calc_terminal/browser/devserver.py +176 -0
  20. calc_terminal/browser/engine.py +494 -0
  21. calc_terminal/browser/navigation.py +84 -0
  22. calc_terminal/browser/preview.py +429 -0
  23. calc_terminal/browser/preview_entry.py +95 -0
  24. calc_terminal/browser/project_detector.py +144 -0
  25. calc_terminal/browser/server.py +449 -0
  26. calc_terminal/browser/state.py +75 -0
  27. calc_terminal/browser/watcher.py +99 -0
  28. calc_terminal/browser_gui/__init__.py +1 -0
  29. calc_terminal/browser_gui/__main__.py +3 -0
  30. calc_terminal/browser_gui/launcher.py +173 -0
  31. calc_terminal/browser_gui/playwright_browser.py +117 -0
  32. calc_terminal/browser_gui/qt_browser.py +1501 -0
  33. calc_terminal/browser_gui/webview_browser.py +57 -0
  34. calc_terminal/capabilities/__init__.py +35 -0
  35. calc_terminal/capabilities/adapters/__init__.py +33 -0
  36. calc_terminal/capabilities/adapters/bioinformatics.py +204 -0
  37. calc_terminal/capabilities/adapters/browser_adapter.py +205 -0
  38. calc_terminal/capabilities/adapters/filesystem.py +206 -0
  39. calc_terminal/capabilities/adapters/git_adapter.py +202 -0
  40. calc_terminal/capabilities/adapters/jupyter_adapter.py +138 -0
  41. calc_terminal/capabilities/adapters/ml_frameworks.py +158 -0
  42. calc_terminal/capabilities/adapters/platforms.py +200 -0
  43. calc_terminal/capabilities/adapters/python_exec.py +93 -0
  44. calc_terminal/capabilities/adapters/quantum_adapter.py +150 -0
  45. calc_terminal/capabilities/adapters/scientific_comp.py +123 -0
  46. calc_terminal/capabilities/adapters/structural_bio.py +161 -0
  47. calc_terminal/capabilities/adapters/terminal.py +99 -0
  48. calc_terminal/capabilities/bus.py +178 -0
  49. calc_terminal/capabilities/discovery.py +207 -0
  50. calc_terminal/capabilities/schema.py +221 -0
  51. calc_terminal/cat.ico +0 -0
  52. calc_terminal/cat_browser.py +2018 -0
  53. calc_terminal/chat_store.py +703 -0
  54. calc_terminal/cli.py +1178 -0
  55. calc_terminal/code_editor.py +640 -0
  56. calc_terminal/collaboration.py +723 -0
  57. calc_terminal/commands_data.py +139 -0
  58. calc_terminal/compatibility_engine.py +352 -0
  59. calc_terminal/compute/__init__.py +31 -0
  60. calc_terminal/compute/fabric.py +350 -0
  61. calc_terminal/config.py +227 -0
  62. calc_terminal/core/__init__.py +41 -0
  63. calc_terminal/core/checkpoint.py +156 -0
  64. calc_terminal/core/input/__init__.py +45 -0
  65. calc_terminal/core/mode_registry.py +300 -0
  66. calc_terminal/core/project_graph.py +172 -0
  67. calc_terminal/core/recovery.py +129 -0
  68. calc_terminal/core/security_layer.py +112 -0
  69. calc_terminal/core/task_graph.py +202 -0
  70. calc_terminal/core/unified_runtime.py +184 -0
  71. calc_terminal/core/verification.py +257 -0
  72. calc_terminal/customization.py +1566 -0
  73. calc_terminal/derivations.py +153 -0
  74. calc_terminal/device_control.py +263 -0
  75. calc_terminal/diagnostics/__init__.py +27 -0
  76. calc_terminal/diagnostics/doctor_engine.py +382 -0
  77. calc_terminal/diagnostics/self_test.py +247 -0
  78. calc_terminal/doctor.py +519 -0
  79. calc_terminal/easter_eggs.py +274 -0
  80. calc_terminal/editor/__init__.py +1 -0
  81. calc_terminal/editor/actions.py +263 -0
  82. calc_terminal/editor/commands.py +160 -0
  83. calc_terminal/editor/shortcuts.py +226 -0
  84. calc_terminal/engine.py +259 -0
  85. calc_terminal/errors.py +120 -0
  86. calc_terminal/event_stream.py +146 -0
  87. calc_terminal/eventbus.py +133 -0
  88. calc_terminal/extensions.py +733 -0
  89. calc_terminal/fallback_cli.py +1321 -0
  90. calc_terminal/first_run.py +265 -0
  91. calc_terminal/fomoji_auth.py +1043 -0
  92. calc_terminal/formulas.py +82 -0
  93. calc_terminal/fs_cache.py +121 -0
  94. calc_terminal/fs_watcher.py +277 -0
  95. calc_terminal/game.py +193 -0
  96. calc_terminal/gen1.py +5 -0
  97. calc_terminal/generators.py +245 -0
  98. calc_terminal/gestures/__init__.py +42 -0
  99. calc_terminal/gestures/bindings.py +175 -0
  100. calc_terminal/gestures/manager.py +477 -0
  101. calc_terminal/goodbye.py +363 -0
  102. calc_terminal/gpu3d.py +290 -0
  103. calc_terminal/graphs.py +358 -0
  104. calc_terminal/hardware_analyzer.py +440 -0
  105. calc_terminal/host/__init__.py +30 -0
  106. calc_terminal/host/browser_manager.py +187 -0
  107. calc_terminal/host/desktop.py +1386 -0
  108. calc_terminal/host/launcher.py +395 -0
  109. calc_terminal/host/terminal.py +279 -0
  110. calc_terminal/identity.py +216 -0
  111. calc_terminal/input/__init__.py +54 -0
  112. calc_terminal/input/capabilities.py +258 -0
  113. calc_terminal/input/focus.py +87 -0
  114. calc_terminal/input/gestures.py +64 -0
  115. calc_terminal/input/pointer.py +114 -0
  116. calc_terminal/input/touch.py +345 -0
  117. calc_terminal/keys.py +84 -0
  118. calc_terminal/live_automation.py +165 -0
  119. calc_terminal/mathtext.py +433 -0
  120. calc_terminal/mcp.py +386 -0
  121. calc_terminal/memory.py +337 -0
  122. calc_terminal/memory_v2.py +479 -0
  123. calc_terminal/metrics.py +333 -0
  124. calc_terminal/mode_detection.py +146 -0
  125. calc_terminal/model.py +2431 -0
  126. calc_terminal/model_router.py +665 -0
  127. calc_terminal/models/__init__.py +0 -0
  128. calc_terminal/models/active_state.py +187 -0
  129. calc_terminal/models/dynamic_registry.py +584 -0
  130. calc_terminal/models/manager.py +781 -0
  131. calc_terminal/models/model_metadata.json +3526 -0
  132. calc_terminal/models/profiles.py +194 -0
  133. calc_terminal/models/registry.py +265 -0
  134. calc_terminal/models/schema.py +197 -0
  135. calc_terminal/models/validator.py +287 -0
  136. calc_terminal/models/verification_engine.py +368 -0
  137. calc_terminal/native_picker.py +215 -0
  138. calc_terminal/ollama_catalog.py +279 -0
  139. calc_terminal/ollama_download.py +233 -0
  140. calc_terminal/orchestrator.py +304 -0
  141. calc_terminal/package_research.py +322 -0
  142. calc_terminal/packages.py +1024 -0
  143. calc_terminal/pc_specs.py +116 -0
  144. calc_terminal/permissions.py +334 -0
  145. calc_terminal/pet.py +106 -0
  146. calc_terminal/pipeline.py +505 -0
  147. calc_terminal/platform/__init__.py +491 -0
  148. calc_terminal/platform/desktop.py +491 -0
  149. calc_terminal/platform/web.py +781 -0
  150. calc_terminal/preview/__init__.py +1 -0
  151. calc_terminal/preview/dev_server.py +303 -0
  152. calc_terminal/preview/diagnostics.py +131 -0
  153. calc_terminal/preview/live_reload.py +66 -0
  154. calc_terminal/preview/manager.py +129 -0
  155. calc_terminal/project_stats.py +209 -0
  156. calc_terminal/projects.py +328 -0
  157. calc_terminal/providers/__init__.py +0 -0
  158. calc_terminal/providers/adapters/__init__.py +80 -0
  159. calc_terminal/providers/adapters/anthropic_adapter.py +127 -0
  160. calc_terminal/providers/adapters/base.py +106 -0
  161. calc_terminal/providers/adapters/chinese_adapters.py +420 -0
  162. calc_terminal/providers/adapters/gemini_adapter.py +101 -0
  163. calc_terminal/providers/adapters/ollama_adapter.py +83 -0
  164. calc_terminal/providers/adapters/openai_adapter.py +159 -0
  165. calc_terminal/providers/adapters/other_adapters.py +246 -0
  166. calc_terminal/providers/anthropic_provider.py +172 -0
  167. calc_terminal/providers/auto_update.py +416 -0
  168. calc_terminal/providers/base_provider.py +105 -0
  169. calc_terminal/providers/discovery_manager.py +207 -0
  170. calc_terminal/providers/gemini_provider.py +178 -0
  171. calc_terminal/providers/lifecycle.py +767 -0
  172. calc_terminal/providers/ollama_adapter.py +707 -0
  173. calc_terminal/providers/openai_provider.py +254 -0
  174. calc_terminal/providers/provider_manager.py +1827 -0
  175. calc_terminal/providers/providers.json +4075 -0
  176. calc_terminal/reactionsim.py +279 -0
  177. calc_terminal/registry.py +337 -0
  178. calc_terminal/report.py +162 -0
  179. calc_terminal/research/__init__.py +45 -0
  180. calc_terminal/research/artifact_intel.py +126 -0
  181. calc_terminal/research/data_lineage.py +123 -0
  182. calc_terminal/research/experiment_ledger.py +303 -0
  183. calc_terminal/research/reproducibility.py +131 -0
  184. calc_terminal/resilience/__init__.py +47 -0
  185. calc_terminal/resilience/agent_state.py +121 -0
  186. calc_terminal/resilience/capability_matcher.py +174 -0
  187. calc_terminal/resilience/circuit_breaker.py +158 -0
  188. calc_terminal/resilience/failover_engine.py +230 -0
  189. calc_terminal/resilience/health_monitor.py +192 -0
  190. calc_terminal/resilience/ollama_adapter.py +125 -0
  191. calc_terminal/resilience/orchestrator.py +312 -0
  192. calc_terminal/resilience/types.py +134 -0
  193. calc_terminal/sandbox.py +98 -0
  194. calc_terminal/scires.py +558 -0
  195. calc_terminal/security_scanner.py +126 -0
  196. calc_terminal/session.py +294 -0
  197. calc_terminal/sim3d.py +206 -0
  198. calc_terminal/solver.py +276 -0
  199. calc_terminal/sound.py +127 -0
  200. calc_terminal/task_reports.py +287 -0
  201. calc_terminal/terminal_host.py +201 -0
  202. calc_terminal/terminal_identity.py +411 -0
  203. calc_terminal/test_ai_mode_reliability.py +344 -0
  204. calc_terminal/test_browser.py +368 -0
  205. calc_terminal/test_code_editor_upgrade.py +485 -0
  206. calc_terminal/test_customization.py +1148 -0
  207. calc_terminal/test_customization_ui.py +612 -0
  208. calc_terminal/test_dynamic_registry.py +304 -0
  209. calc_terminal/test_extensions.py +436 -0
  210. calc_terminal/test_overhaul.py +557 -0
  211. calc_terminal/test_project_detect.py +255 -0
  212. calc_terminal/test_root_cause_fix.py +527 -0
  213. calc_terminal/test_stability.py +532 -0
  214. calc_terminal/test_terminal_identity.py +132 -0
  215. calc_terminal/test_v079_speed.py +460 -0
  216. calc_terminal/theme.py +1107 -0
  217. calc_terminal/timeline.py +139 -0
  218. calc_terminal/todos.py +246 -0
  219. calc_terminal/tool_call_normalizer.py +419 -0
  220. calc_terminal/tui.py +104 -0
  221. calc_terminal/ui/__init__.py +8 -0
  222. calc_terminal/ui/activity_panel.py +231 -0
  223. calc_terminal/ui/activity_stream_panel.py +238 -0
  224. calc_terminal/ui/animations.py +122 -0
  225. calc_terminal/ui/app.py +7271 -0
  226. calc_terminal/ui/attach_panel.py +597 -0
  227. calc_terminal/ui/attachments.py +424 -0
  228. calc_terminal/ui/backup_panel.py +810 -0
  229. calc_terminal/ui/browser_shell.py +887 -0
  230. calc_terminal/ui/cat_agent.py +357 -0
  231. calc_terminal/ui/chats_panel.py +899 -0
  232. calc_terminal/ui/command_palette.py +125 -0
  233. calc_terminal/ui/command_palette_modal.py +166 -0
  234. calc_terminal/ui/composer.py +1141 -0
  235. calc_terminal/ui/context_menu.py +197 -0
  236. calc_terminal/ui/conversation.py +1435 -0
  237. calc_terminal/ui/customization_panel.py +1229 -0
  238. calc_terminal/ui/dashboard.py +404 -0
  239. calc_terminal/ui/design_system.py +557 -0
  240. calc_terminal/ui/diff_panel.py +213 -0
  241. calc_terminal/ui/editor.py +2102 -0
  242. calc_terminal/ui/empty_state.py +302 -0
  243. calc_terminal/ui/events.py +487 -0
  244. calc_terminal/ui/extensions_panel.py +815 -0
  245. calc_terminal/ui/footer.py +166 -0
  246. calc_terminal/ui/gestures_panel.py +383 -0
  247. calc_terminal/ui/goodbye_screen.py +100 -0
  248. calc_terminal/ui/header.py +1034 -0
  249. calc_terminal/ui/help_panel.py +254 -0
  250. calc_terminal/ui/live_activities.py +914 -0
  251. calc_terminal/ui/mcp_panel.py +570 -0
  252. calc_terminal/ui/memory_center.py +524 -0
  253. calc_terminal/ui/mode_colors_panel.py +525 -0
  254. calc_terminal/ui/nav_screens.py +747 -0
  255. calc_terminal/ui/ollama_panel.py +536 -0
  256. calc_terminal/ui/palette.py +221 -0
  257. calc_terminal/ui/permission_panel.py +269 -0
  258. calc_terminal/ui/personalization_panel.py +517 -0
  259. calc_terminal/ui/personalize_center.py +1568 -0
  260. calc_terminal/ui/preview_panel.py +441 -0
  261. calc_terminal/ui/resizers.py +402 -0
  262. calc_terminal/ui/sidebar.py +1285 -0
  263. calc_terminal/ui/statusbar.py +168 -0
  264. calc_terminal/ui/theme_css.py +1396 -0
  265. calc_terminal/ui/thinking.py +226 -0
  266. calc_terminal/ui/timeline_panel.py +102 -0
  267. calc_terminal/ui/todo_panel.py +193 -0
  268. calc_terminal/ui/viewport.py +136 -0
  269. calc_terminal/ui/vision_panel.py +489 -0
  270. calc_terminal/ui/welcome_modal.py +343 -0
  271. calc_terminal/ui/widgets.py +160 -0
  272. calc_terminal/ui/workspace.py +831 -0
  273. calc_terminal/viewers/__init__.py +1 -0
  274. calc_terminal/viewers/document_viewer.py +252 -0
  275. calc_terminal/viewers/image_viewer.py +241 -0
  276. calc_terminal/viewers/pdf_viewer.py +203 -0
  277. calc_terminal/viewers/presentation_viewer.py +164 -0
  278. calc_terminal/viewers/registry.py +120 -0
  279. calc_terminal/viewers/spreadsheet_viewer.py +204 -0
  280. calc_terminal/vision/__init__.py +89 -0
  281. calc_terminal/vision/analysis.py +194 -0
  282. calc_terminal/vision/annotations.py +297 -0
  283. calc_terminal/vision/capture.py +171 -0
  284. calc_terminal/vision/context.py +231 -0
  285. calc_terminal/vision/correlation.py +169 -0
  286. calc_terminal/vision/cursor.py +258 -0
  287. calc_terminal/vision/events.py +66 -0
  288. calc_terminal/vision/frame_pipeline.py +259 -0
  289. calc_terminal/vision/priority.py +218 -0
  290. calc_terminal/vision/provider.py +180 -0
  291. calc_terminal/vision/safety.py +149 -0
  292. calc_terminal/vision/session.py +281 -0
  293. calc_terminal/vision/verify.py +162 -0
  294. calc_terminal/vision.py +514 -0
  295. calc_terminal/vscode_integration.py +113 -0
  296. calc_terminal/web/__init__.py +8 -0
  297. calc_terminal/web/cat_runtime.py +710 -0
  298. calc_terminal/web/server.py +2891 -0
  299. calc_terminal/web/static/css/app.css +3152 -0
  300. calc_terminal/web/static/icons/badge-72.png +0 -0
  301. calc_terminal/web/static/icons/cat.ico +0 -0
  302. calc_terminal/web/static/icons/icon-128.png +0 -0
  303. calc_terminal/web/static/icons/icon-144.png +0 -0
  304. calc_terminal/web/static/icons/icon-152.png +0 -0
  305. calc_terminal/web/static/icons/icon-192.png +0 -0
  306. calc_terminal/web/static/icons/icon-384.png +0 -0
  307. calc_terminal/web/static/icons/icon-512.png +0 -0
  308. calc_terminal/web/static/icons/icon-72.png +0 -0
  309. calc_terminal/web/static/icons/icon-96.png +0 -0
  310. calc_terminal/web/static/icons/icon.svg +34 -0
  311. calc_terminal/web/static/icons/new-project.png +0 -0
  312. calc_terminal/web/static/icons/open-project.png +0 -0
  313. calc_terminal/web/static/index.html +734 -0
  314. calc_terminal/web/static/js/app.js +2403 -0
  315. calc_terminal/web/static/manifest.json +88 -0
  316. calc_terminal/web/static/sw.js +230 -0
  317. calc_terminal/workflow_engine.py +769 -0
  318. calc_terminal/workspace.py +593 -0
  319. calc_terminal/workspace_index.py +385 -0
  320. cct_cli-0.7.9.0.dist-info/METADATA +210 -0
  321. cct_cli-0.7.9.0.dist-info/RECORD +325 -0
  322. cct_cli-0.7.9.0.dist-info/WHEEL +5 -0
  323. cct_cli-0.7.9.0.dist-info/entry_points.txt +4 -0
  324. cct_cli-0.7.9.0.dist-info/licenses/LICENSE +21 -0
  325. cct_cli-0.7.9.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,57 @@
1
+ """
2
+ Fallback browser via pywebview (Edge WebView2 on Windows).
3
+ Shows real HTML/CSS/JS, but with minimal chrome when Qt not available.
4
+ Still far better than terminal text — this is a real WebView2 surface.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import os
10
+ import sys
11
+
12
+ def launch_webview_browser(start_url: str = "about:home") -> int:
13
+ try:
14
+ import webview # type: ignore
15
+ except ImportError:
16
+ print("[CAT Browser] pywebview not installed. Run: pip install pywebview", file=sys.stderr)
17
+ return 2
18
+
19
+ # Normalize about:home to a data URL with new-tab HTML
20
+ if start_url in ("about:home", "about:blank", "", None):
21
+ # Load new-tab HTML via data URL
22
+ try:
23
+ from .qt_browser import _new_tab_html # reuse same HTML
24
+ html = _new_tab_html()
25
+ # pywebview can load html string via data URL
26
+ import urllib.parse
27
+ data_url = "data:text/html;charset=utf-8," + urllib.parse.quote(html)
28
+ start_url = data_url
29
+ except Exception:
30
+ start_url = "https://www.google.com"
31
+
32
+ # Ensure localhost URLs are correct
33
+ if start_url.startswith("localhost:"):
34
+ start_url = "http://" + start_url
35
+
36
+ title = "CAT Browser"
37
+ # Try to get Fomoji title if start_url is fomoji
38
+ if "localhost:3000" in start_url:
39
+ title = "CAT Browser — Fomoji"
40
+
41
+ try:
42
+ # Create window with reasonable size, resizable, with min size
43
+ window = webview.create_window(
44
+ title, start_url,
45
+ width=1200, height=800,
46
+ min_size=(900, 600),
47
+ text_select=True,
48
+ )
49
+ # Start the GUI loop (blocks until closed)
50
+ # On Windows, this uses WebView2 and will render JS/WebGL correctly
51
+ webview.start(debug=False)
52
+ return 0
53
+ except Exception as e:
54
+ print(f"[CAT Browser] webview failed: {e}", file=sys.stderr)
55
+ # Fallback: try to open in OS default browser as last resort? But spec says no external.
56
+ # Instead, show error.
57
+ return 1
@@ -0,0 +1,35 @@
1
+ """
2
+ CAT Capabilities Package per §4, §56, §57:
3
+ Central Capability Bus, schema definitions, environment discovery, and adapters.
4
+ """
5
+
6
+ from .schema import (
7
+ AvailabilityStatus,
8
+ Capability,
9
+ CapabilityCategory,
10
+ CapabilitySpec,
11
+ ExecutionError,
12
+ ExecutionResult,
13
+ )
14
+ from .bus import CapabilityBus, capability_bus
15
+ from .discovery import EnvironmentSnapshot, discover_environment
16
+ from .adapters import register_all_adapters
17
+
18
+ # Auto-register all built-in adapters into the singleton bus
19
+ if not capability_bus._initialized:
20
+ register_all_adapters(capability_bus)
21
+ capability_bus._initialized = True
22
+
23
+ __all__ = [
24
+ "AvailabilityStatus",
25
+ "CapabilityCategory",
26
+ "CapabilitySpec",
27
+ "ExecutionError",
28
+ "ExecutionResult",
29
+ "Capability",
30
+ "CapabilityBus",
31
+ "capability_bus",
32
+ "EnvironmentSnapshot",
33
+ "discover_environment",
34
+ "register_all_adapters",
35
+ ]
@@ -0,0 +1,33 @@
1
+ """
2
+ CAT Capability Adapters package.
3
+ Exposes concrete handlers registered into capability_bus.
4
+ """
5
+
6
+ from .filesystem import register_filesystem_capabilities
7
+ from .terminal import register_terminal_capabilities
8
+ from .python_exec import register_python_capabilities
9
+ from .git_adapter import register_git_capabilities
10
+ from .browser_adapter import register_browser_capabilities
11
+ from .jupyter_adapter import register_jupyter_capabilities
12
+ from .ml_frameworks import register_ml_capabilities
13
+ from .bioinformatics import register_bio_capabilities
14
+ from .structural_bio import register_structural_bio_capabilities
15
+ from .scientific_comp import register_scientific_capabilities
16
+ from .platforms import register_platform_capabilities
17
+ from .quantum_adapter import register_quantum_capabilities
18
+
19
+
20
+ def register_all_adapters(bus):
21
+ """Register all built-in capabilities into the given CapabilityBus."""
22
+ register_filesystem_capabilities(bus)
23
+ register_terminal_capabilities(bus)
24
+ register_python_capabilities(bus)
25
+ register_git_capabilities(bus)
26
+ register_browser_capabilities(bus)
27
+ register_jupyter_capabilities(bus)
28
+ register_ml_capabilities(bus)
29
+ register_bio_capabilities(bus)
30
+ register_structural_bio_capabilities(bus)
31
+ register_scientific_capabilities(bus)
32
+ register_platform_capabilities(bus)
33
+ register_quantum_capabilities(bus)
@@ -0,0 +1,204 @@
1
+ """
2
+ Bioinformatics & Genomics Capability Adapter per §45:
3
+ - blast.search
4
+ - ncbi.query
5
+ - biopython.parse
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import os
11
+ import shutil
12
+ import subprocess
13
+ import urllib.request
14
+ import urllib.parse
15
+ import json
16
+ from typing import Any, Dict, Optional, Tuple
17
+
18
+ from ..schema import (
19
+ AvailabilityStatus,
20
+ Capability,
21
+ CapabilityCategory,
22
+ CapabilitySpec,
23
+ ExecutionResult,
24
+ )
25
+
26
+
27
+ def _check_bio_health() -> Tuple[AvailabilityStatus, str]:
28
+ has_local = shutil.which("blastn") is not None or shutil.which("blastp") is not None
29
+ try:
30
+ import Bio # noqa: F401
31
+ has_biopython = True
32
+ except ImportError:
33
+ has_biopython = False
34
+
35
+ if has_local or has_biopython:
36
+ return AvailabilityStatus.AVAILABLE, f"Bioinformatics ready (local BLAST: {has_local}, Biopython: {has_biopython})"
37
+ return AvailabilityStatus.AVAILABLE, "NCBI REST API available (local BLAST/Biopython not installed)"
38
+
39
+
40
+ def _blast_handler(args: Dict[str, Any], context: Optional[Dict[str, Any]] = None) -> ExecutionResult:
41
+ sequence = args.get("sequence", "")
42
+ program = args.get("program", "blastp") # blastp, blastn
43
+ database = args.get("database", "nr")
44
+
45
+ if not sequence:
46
+ return ExecutionResult(success=False, error="sequence argument is required (FASTA or raw sequence)")
47
+
48
+ # Check for local blast tool
49
+ if shutil.which(program):
50
+ try:
51
+ proc = subprocess.run(
52
+ [program, "-db", database, "-outfmt", "0"],
53
+ input=sequence,
54
+ stdout=subprocess.PIPE,
55
+ stderr=subprocess.PIPE,
56
+ text=True,
57
+ timeout=60.0,
58
+ )
59
+ if proc.returncode == 0:
60
+ return ExecutionResult(
61
+ success=True,
62
+ output=proc.stdout,
63
+ metadata={"engine": "local", "program": program, "database": database},
64
+ )
65
+ except Exception:
66
+ pass
67
+
68
+ # Fallback to NCBI Web API
69
+ try:
70
+ url = "https://blast.ncbi.nlm.nih.gov/Blast.cgi"
71
+ data = urllib.parse.urlencode({
72
+ "CMD": "Put",
73
+ "PROGRAM": program,
74
+ "DATABASE": database,
75
+ "QUERY": sequence,
76
+ }).encode("utf-8")
77
+ req = urllib.request.Request(url, data=data, headers={"User-Agent": "CAT-AI-Platform/1.0"})
78
+ with urllib.request.urlopen(req, timeout=10.0) as resp:
79
+ body = resp.read().decode("utf-8", errors="replace")
80
+ # Extract RID (Request ID)
81
+ rid = ""
82
+ for line in body.splitlines():
83
+ if "RID =" in line:
84
+ rid = line.split("RID =")[1].strip()
85
+ break
86
+
87
+ return ExecutionResult(
88
+ success=True,
89
+ output=f"BLAST job submitted to NCBI. Request ID (RID): {rid or 'pending'}",
90
+ metadata={"engine": "ncbi_rest", "rid": rid, "program": program, "database": database},
91
+ )
92
+ except Exception as e:
93
+ return ExecutionResult(success=False, error=f"BLAST query failed: {e}")
94
+
95
+
96
+ def _ncbi_query_handler(args: Dict[str, Any], context: Optional[Dict[str, Any]] = None) -> ExecutionResult:
97
+ term = args.get("term", "")
98
+ db = args.get("db", "protein")
99
+ if not term:
100
+ return ExecutionResult(success=False, error="term argument is required")
101
+
102
+ try:
103
+ url = f"https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi?db={db}&term={urllib.parse.quote_plus(term)}&retmode=json&retmax=10"
104
+ req = urllib.request.Request(url, headers={"User-Agent": "CAT-AI-Platform/1.0"})
105
+ with urllib.request.urlopen(req, timeout=8.0) as resp:
106
+ data = json.loads(resp.read().decode("utf-8"))
107
+ id_list = data.get("esearchresult", {}).get("idlist", [])
108
+ return ExecutionResult(
109
+ success=True,
110
+ output=id_list,
111
+ metadata={"db": db, "count": len(id_list), "ids": id_list},
112
+ )
113
+ except Exception as e:
114
+ return ExecutionResult(success=False, error=f"NCBI query failed: {e}")
115
+
116
+
117
+ def _biopython_parse_handler(args: Dict[str, Any], context: Optional[Dict[str, Any]] = None) -> ExecutionResult:
118
+ file_path = args.get("path", "")
119
+ fmt = args.get("format", "fasta")
120
+ if not file_path:
121
+ return ExecutionResult(success=False, error="path argument is required")
122
+ file_path = os.path.abspath(os.path.expanduser(file_path))
123
+ if not os.path.exists(file_path):
124
+ return ExecutionResult(success=False, error=f"File not found: {file_path}")
125
+
126
+ try:
127
+ from Bio import SeqIO
128
+ records = []
129
+ for rec in SeqIO.parse(file_path, fmt):
130
+ records.append({
131
+ "id": rec.id,
132
+ "name": rec.name,
133
+ "description": rec.description,
134
+ "length": len(rec.seq),
135
+ "sequence": str(rec.seq)[:100] + ("..." if len(rec.seq) > 100 else ""),
136
+ })
137
+ return ExecutionResult(success=True, output=records, metadata={"count": len(records)})
138
+ except ImportError:
139
+ # Simple fallback parser for FASTA if biopython not installed
140
+ if fmt.lower() == "fasta":
141
+ records = []
142
+ with open(file_path, "r", encoding="utf-8") as f:
143
+ cur_id = ""
144
+ cur_seq = []
145
+ for line in f:
146
+ line = line.strip()
147
+ if line.startswith(">"):
148
+ if cur_id:
149
+ records.append({"id": cur_id, "length": sum(len(s) for s in cur_seq)})
150
+ cur_id = line[1:]
151
+ cur_seq = []
152
+ else:
153
+ cur_seq.append(line)
154
+ if cur_id:
155
+ records.append({"id": cur_id, "length": sum(len(s) for s in cur_seq)})
156
+ return ExecutionResult(success=True, output=records, metadata={"count": len(records), "parser": "fallback"})
157
+ return ExecutionResult(success=False, error="Biopython is required for this format (run: pip install biopython)", status=AvailabilityStatus.SOFTWARE_NOT_FOUND)
158
+
159
+
160
+ def register_bio_capabilities(bus):
161
+ bus.register(Capability(
162
+ spec=CapabilitySpec(
163
+ name="blast.search",
164
+ version="1.0.0",
165
+ category=CapabilityCategory.BIO,
166
+ description="Run BLAST sequence similarity search via local BLAST+ or NCBI REST service.",
167
+ input_schema={"sequence": "string", "program": "string?", "database": "string?"},
168
+ output_schema={"output": "string"},
169
+ permissions=["read_workspace"],
170
+ documentation="Executes nucleotide or protein sequence alignment.",
171
+ ),
172
+ handler=_blast_handler,
173
+ health_checker=_check_bio_health,
174
+ ))
175
+
176
+ bus.register(Capability(
177
+ spec=CapabilitySpec(
178
+ name="ncbi.query",
179
+ version="1.0.0",
180
+ category=CapabilityCategory.BIO,
181
+ description="Query NCBI Entrez database for proteins, nucleotides or PubMed identifiers.",
182
+ input_schema={"term": "string", "db": "string?"},
183
+ output_schema={"ids": "list"},
184
+ permissions=["read_workspace"],
185
+ documentation="Queries NCBI E-utilities.",
186
+ ),
187
+ handler=_ncbi_query_handler,
188
+ health_checker=_check_bio_health,
189
+ ))
190
+
191
+ bus.register(Capability(
192
+ spec=CapabilitySpec(
193
+ name="biopython.parse",
194
+ version="1.0.0",
195
+ category=CapabilityCategory.BIO,
196
+ description="Parse biological sequence files (FASTA, GenBank, PDB).",
197
+ input_schema={"path": "string", "format": "string?"},
198
+ output_schema={"records": "list"},
199
+ permissions=["read_workspace"],
200
+ documentation="Extracts biological sequence records from files.",
201
+ ),
202
+ handler=_biopython_parse_handler,
203
+ health_checker=_check_bio_health,
204
+ ))
@@ -0,0 +1,205 @@
1
+ """
2
+ Browser Capabilities Adapter per §68:
3
+ - browser.navigate
4
+ - browser.click
5
+ - browser.type
6
+ - browser.screenshot
7
+ - browser.inspect
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import os
13
+ from typing import Any, Dict, Optional, Tuple
14
+
15
+ from ..schema import (
16
+ AvailabilityStatus,
17
+ Capability,
18
+ CapabilityCategory,
19
+ CapabilitySpec,
20
+ ExecutionResult,
21
+ )
22
+
23
+
24
+ def _check_browser_health() -> Tuple[AvailabilityStatus, str]:
25
+ try:
26
+ from ...browser.engine import PLAYWRIGHT_AVAILABLE
27
+ if PLAYWRIGHT_AVAILABLE:
28
+ return AvailabilityStatus.AVAILABLE, "Playwright Chromium engine available"
29
+ return AvailabilityStatus.SOFTWARE_NOT_FOUND, "playwright package not installed (run: pip install playwright && playwright install chromium)"
30
+ except Exception as e:
31
+ return AvailabilityStatus.UNAVAILABLE, str(e)
32
+
33
+
34
+ def _navigate_handler(args: Dict[str, Any], context: Optional[Dict[str, Any]] = None) -> ExecutionResult:
35
+ url = args.get("url", "")
36
+ if not url:
37
+ return ExecutionResult(success=False, error="url argument is required")
38
+
39
+ try:
40
+ from ...browser.preview import PreviewController
41
+ ctrl = PreviewController.instance() if hasattr(PreviewController, "instance") else None
42
+ if ctrl and ctrl.running:
43
+ res = ctrl.navigate(url)
44
+ return ExecutionResult(success=True, output=f"Navigated to {url}", metadata={"url": url})
45
+ # If preview controller not actively running, check standalone Playwright
46
+ from ...browser.engine import BrowserEngine
47
+ engine = BrowserEngine(headless=True)
48
+ engine.start()
49
+ snap = engine.navigate(url)
50
+ engine.stop()
51
+ return ExecutionResult(
52
+ success=True,
53
+ output=f"Successfully loaded {snap.url} (HTTP {snap.status_code}) - Title: '{snap.title}'",
54
+ metadata={"title": snap.title, "url": snap.url, "status_code": snap.status_code},
55
+ )
56
+ except Exception as e:
57
+ return ExecutionResult(success=False, error=f"Navigation failed: {e}")
58
+
59
+
60
+ def _click_handler(args: Dict[str, Any], context: Optional[Dict[str, Any]] = None) -> ExecutionResult:
61
+ target = args.get("target") or args.get("selector", "")
62
+ if not target:
63
+ return ExecutionResult(success=False, error="target or selector argument is required")
64
+
65
+ try:
66
+ from ...browser.preview import PreviewController
67
+ ctrl = PreviewController.instance() if hasattr(PreviewController, "instance") else None
68
+ if ctrl and ctrl.engine and ctrl.engine.available:
69
+ ok = ctrl.engine.click(target)
70
+ return ExecutionResult(success=ok, output=f"Clicked element '{target}'" if ok else f"Could not find element '{target}'")
71
+ return ExecutionResult(success=False, error="Browser engine is not currently running")
72
+ except Exception as e:
73
+ return ExecutionResult(success=False, error=f"Browser click failed: {e}")
74
+
75
+
76
+ def _type_handler(args: Dict[str, Any], context: Optional[Dict[str, Any]] = None) -> ExecutionResult:
77
+ target = args.get("target") or args.get("selector", "")
78
+ text = args.get("text", "")
79
+ if not target:
80
+ return ExecutionResult(success=False, error="target argument is required")
81
+
82
+ try:
83
+ from ...browser.preview import PreviewController
84
+ ctrl = PreviewController.instance() if hasattr(PreviewController, "instance") else None
85
+ if ctrl and ctrl.engine and ctrl.engine.available:
86
+ ok = ctrl.engine.type_text(target, text)
87
+ return ExecutionResult(success=ok, output=f"Typed text into '{target}'" if ok else f"Could not type into '{target}'")
88
+ return ExecutionResult(success=False, error="Browser engine is not currently running")
89
+ except Exception as e:
90
+ return ExecutionResult(success=False, error=f"Browser type failed: {e}")
91
+
92
+
93
+ def _screenshot_handler(args: Dict[str, Any], context: Optional[Dict[str, Any]] = None) -> ExecutionResult:
94
+ dest = args.get("destination", "screenshot.png")
95
+ dest = os.path.abspath(os.path.expanduser(dest))
96
+ try:
97
+ from ...browser.preview import PreviewController
98
+ ctrl = PreviewController.instance() if hasattr(PreviewController, "instance") else None
99
+ if ctrl and ctrl.engine and ctrl.engine.available:
100
+ snap_path = ctrl.engine.screenshot(dest)
101
+ return ExecutionResult(success=True, output=f"Screenshot saved to {dest}", metadata={"path": dest})
102
+ return ExecutionResult(success=False, error="Browser engine is not running to take screenshot")
103
+ except Exception as e:
104
+ return ExecutionResult(success=False, error=f"Screenshot failed: {e}")
105
+
106
+
107
+ def _inspect_handler(args: Dict[str, Any], context: Optional[Dict[str, Any]] = None) -> ExecutionResult:
108
+ try:
109
+ from ...browser.preview import PreviewController
110
+ ctrl = PreviewController.instance() if hasattr(PreviewController, "instance") else None
111
+ if ctrl and ctrl.engine and ctrl.engine.available:
112
+ snap = ctrl.engine.snapshot()
113
+ return ExecutionResult(
114
+ success=True,
115
+ output={
116
+ "title": snap.title,
117
+ "url": snap.url,
118
+ "status_code": snap.status_code,
119
+ "headings": snap.headings,
120
+ "links_count": len(snap.links),
121
+ "buttons_count": len(snap.buttons),
122
+ "console_errors": snap.js_errors,
123
+ },
124
+ metadata={"title": snap.title, "url": snap.url},
125
+ )
126
+ return ExecutionResult(success=False, error="Browser engine is not currently running")
127
+ except Exception as e:
128
+ return ExecutionResult(success=False, error=f"Inspect failed: {e}")
129
+
130
+
131
+ def register_browser_capabilities(bus):
132
+ bus.register(Capability(
133
+ spec=CapabilitySpec(
134
+ name="browser.navigate",
135
+ version="1.0.0",
136
+ category=CapabilityCategory.BROWSER,
137
+ description="Navigate to a URL using the real embedded Chromium engine.",
138
+ input_schema={"url": "string"},
139
+ output_schema={"output": "string"},
140
+ permissions=["browser_automation"],
141
+ documentation="Loads a webpage in Chromium and waits for network idle.",
142
+ ),
143
+ handler=_navigate_handler,
144
+ health_checker=_check_browser_health,
145
+ ))
146
+
147
+ bus.register(Capability(
148
+ spec=CapabilitySpec(
149
+ name="browser.click",
150
+ version="1.0.0",
151
+ category=CapabilityCategory.BROWSER,
152
+ description="Click on an interactive element by selector or visible text.",
153
+ input_schema={"target": "string"},
154
+ output_schema={"output": "string"},
155
+ permissions=["browser_automation"],
156
+ documentation="Clicks an element in the active browser page.",
157
+ ),
158
+ handler=_click_handler,
159
+ health_checker=_check_browser_health,
160
+ ))
161
+
162
+ bus.register(Capability(
163
+ spec=CapabilitySpec(
164
+ name="browser.type",
165
+ version="1.0.0",
166
+ category=CapabilityCategory.BROWSER,
167
+ description="Type text into an input field in the active browser page.",
168
+ input_schema={"target": "string", "text": "string"},
169
+ output_schema={"output": "string"},
170
+ permissions=["browser_automation"],
171
+ documentation="Focuses and types into an input/textarea.",
172
+ ),
173
+ handler=_type_handler,
174
+ health_checker=_check_browser_health,
175
+ ))
176
+
177
+ bus.register(Capability(
178
+ spec=CapabilitySpec(
179
+ name="browser.screenshot",
180
+ version="1.0.0",
181
+ category=CapabilityCategory.BROWSER,
182
+ description="Capture a screenshot of the current browser page to disk.",
183
+ input_schema={"destination": "string?"},
184
+ output_schema={"output": "string"},
185
+ permissions=["browser_automation"],
186
+ documentation="Saves a PNG screenshot of the viewport.",
187
+ ),
188
+ handler=_screenshot_handler,
189
+ health_checker=_check_browser_health,
190
+ ))
191
+
192
+ bus.register(Capability(
193
+ spec=CapabilitySpec(
194
+ name="browser.inspect",
195
+ version="1.0.0",
196
+ category=CapabilityCategory.BROWSER,
197
+ description="Inspect the current page DOM structure, interactive elements, and console errors.",
198
+ input_schema={},
199
+ output_schema={"output": "object"},
200
+ permissions=["browser_automation"],
201
+ documentation="Extracts headings, links, buttons, and console output.",
202
+ ),
203
+ handler=_inspect_handler,
204
+ health_checker=_check_browser_health,
205
+ ))