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,440 @@
1
+ """
2
+ calc_terminal/hardware_analyzer.py
3
+ ==================================
4
+ CAT Hardware Analyzer — Comprehensive cross-platform device capability detection.
5
+
6
+ Part of CAT (Created by Kazi Zillani).
7
+ Detects:
8
+ - CPU: Model, physical cores, logical threads, architecture.
9
+ - System RAM: Total, available, used, percent.
10
+ - GPU: Model name, total VRAM, free VRAM, vendor, compute platform (CUDA, ROCm, Metal, DirectML, CPU).
11
+ - Storage: Available disk capacity on the user's primary/models storage drive.
12
+ - Platform / OS / Python environment.
13
+ - Local AI Capability Rating: EXCELLENT / GOOD / MODERATE / LIMITED.
14
+
15
+ Designed to be lightweight, fail-safe (never raises unhandled exceptions), and
16
+ runs with standard library fallbacks if third-party modules are unavailable.
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import os
22
+ import platform
23
+ import shutil
24
+ import subprocess
25
+ import sys
26
+ from dataclasses import dataclass, field
27
+ from typing import Any, Dict, Optional, Tuple
28
+
29
+
30
+ @dataclass
31
+ class CPUInfo:
32
+ model: str = "Unknown CPU"
33
+ physical_cores: int = 1
34
+ logical_cores: int = 1
35
+ arch: str = ""
36
+ features: list[str] = field(default_factory=list)
37
+
38
+
39
+ @dataclass
40
+ class RAMInfo:
41
+ total_gb: float = 8.0
42
+ available_gb: float = 4.0
43
+ used_gb: float = 4.0
44
+ percent_used: float = 50.0
45
+
46
+
47
+ @dataclass
48
+ class GPUInfo:
49
+ detected: bool = False
50
+ name: str = "Integrated / CPU Only"
51
+ vendor: str = "Unknown" # NVIDIA, AMD, Apple, Intel, Unknown
52
+ vram_total_gb: float = 0.0
53
+ vram_free_gb: float = 0.0
54
+ backend: str = "CPU" # CUDA, ROCm, Metal, DirectML, CPU
55
+ driver_version: str = ""
56
+
57
+
58
+ @dataclass
59
+ class StorageInfo:
60
+ total_gb: float = 0.0
61
+ free_gb: float = 0.0
62
+ used_gb: float = 0.0
63
+ path: str = ""
64
+
65
+
66
+ @dataclass
67
+ class HardwareProfile:
68
+ cpu: CPUInfo = field(default_factory=CPUInfo)
69
+ ram: RAMInfo = field(default_factory=RAMInfo)
70
+ gpu: GPUInfo = field(default_factory=GPUInfo)
71
+ storage: StorageInfo = field(default_factory=StorageInfo)
72
+ os_name: str = ""
73
+ os_version: str = ""
74
+ python_version: str = ""
75
+ ai_capability: str = "MODERATE" # EXCELLENT, GOOD, MODERATE, LIMITED
76
+ ai_capability_notes: list[str] = field(default_factory=list)
77
+
78
+ @property
79
+ def cpu_model(self) -> str:
80
+ return self.cpu.model
81
+
82
+ @property
83
+ def cpu_threads(self) -> int:
84
+ return self.cpu.logical_cores
85
+
86
+ @property
87
+ def ram_total_gb(self) -> float:
88
+ return self.ram.total_gb
89
+
90
+ @property
91
+ def ram_available_gb(self) -> float:
92
+ return self.ram.available_gb
93
+
94
+ @property
95
+ def has_gpu(self) -> bool:
96
+ return self.gpu.detected
97
+
98
+ @property
99
+ def gpu_name(self) -> str:
100
+ return self.gpu.name
101
+
102
+ @property
103
+ def vram_gb(self) -> float:
104
+ return self.gpu.vram_total_gb
105
+
106
+ @property
107
+ def gpu_backend(self) -> str:
108
+ return self.gpu.backend
109
+
110
+ @property
111
+ def local_ai_tier(self):
112
+ class _Tier:
113
+ def __init__(self, val):
114
+ self.value = val
115
+ def __str__(self):
116
+ return self.value
117
+ return _Tier(self.ai_capability)
118
+
119
+ def to_dict(self) -> Dict[str, Any]:
120
+ return {
121
+ "cpu": {
122
+ "model": self.cpu.model,
123
+ "physical_cores": self.cpu.physical_cores,
124
+ "logical_cores": self.cpu.logical_cores,
125
+ "arch": self.cpu.arch,
126
+ },
127
+ "ram": {
128
+ "total_gb": self.ram.total_gb,
129
+ "available_gb": self.ram.available_gb,
130
+ "used_gb": self.ram.used_gb,
131
+ "percent_used": self.ram.percent_used,
132
+ },
133
+ "gpu": {
134
+ "detected": self.gpu.detected,
135
+ "name": self.gpu.name,
136
+ "vendor": self.gpu.vendor,
137
+ "vram_total_gb": self.gpu.vram_total_gb,
138
+ "vram_free_gb": self.gpu.vram_free_gb,
139
+ "backend": self.gpu.backend,
140
+ "driver_version": self.gpu.driver_version,
141
+ },
142
+ "storage": {
143
+ "total_gb": self.storage.total_gb,
144
+ "free_gb": self.storage.free_gb,
145
+ "used_gb": self.storage.used_gb,
146
+ "path": self.storage.path,
147
+ },
148
+ "os_name": self.os_name,
149
+ "os_version": self.os_version,
150
+ "python_version": self.python_version,
151
+ "ai_capability": self.ai_capability,
152
+ "ai_capability_notes": self.ai_capability_notes,
153
+ }
154
+
155
+
156
+ class HardwareAnalyzer:
157
+ """Analyzes the local environment and grades local inference capability."""
158
+
159
+ _cached_profile: Optional[HardwareProfile] = None
160
+
161
+ @classmethod
162
+ def analyze(cls, force_refresh: bool = False) -> HardwareProfile:
163
+ """Run full hardware analysis, returning a structured HardwareProfile."""
164
+ if cls._cached_profile is not None and not force_refresh:
165
+ return cls._cached_profile
166
+
167
+ cpu_info = cls._detect_cpu()
168
+ ram_info = cls._detect_ram()
169
+ gpu_info = cls._detect_gpu()
170
+ storage_info = cls._detect_storage()
171
+ os_name = platform.system()
172
+ os_version = platform.platform()
173
+ python_ver = platform.python_version()
174
+
175
+ capability, notes = cls._evaluate_capability(ram_info, gpu_info, cpu_info)
176
+
177
+ profile = HardwareProfile(
178
+ cpu=cpu_info,
179
+ ram=ram_info,
180
+ gpu=gpu_info,
181
+ storage=storage_info,
182
+ os_name=os_name,
183
+ os_version=os_version,
184
+ python_version=python_ver,
185
+ ai_capability=capability,
186
+ ai_capability_notes=notes,
187
+ )
188
+
189
+ cls._cached_profile = profile
190
+ return profile
191
+
192
+ @classmethod
193
+ def detect(cls, force_refresh: bool = False) -> HardwareProfile:
194
+ return cls.analyze(force_refresh=force_refresh)
195
+
196
+ @classmethod
197
+ def _detect_cpu(cls) -> CPUInfo:
198
+ model = "Unknown CPU"
199
+ logical = os.cpu_count() or 4
200
+ physical = logical
201
+ arch = platform.machine() or "x86_64"
202
+
203
+ # Try psutil for physical cores
204
+ try:
205
+ import psutil
206
+ p = psutil.cpu_count(logical=False)
207
+ if p:
208
+ physical = p
209
+ l = psutil.cpu_count(logical=True)
210
+ if l:
211
+ logical = l
212
+ except Exception:
213
+ pass
214
+
215
+ # Try OS specific name lookups
216
+ try:
217
+ if sys.platform == "win32":
218
+ import winreg
219
+ key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r"HARDWARE\DESCRIPTION\System\CentralProcessor\0")
220
+ val, _ = winreg.QueryValueEx(key, "ProcessorNameString")
221
+ if val and str(val).strip():
222
+ model = str(val).strip()
223
+ elif sys.platform == "darwin":
224
+ out = subprocess.check_output(["sysctl", "-n", "machdep.cpu.brand_string"], text=True, timeout=2)
225
+ if out.strip():
226
+ model = out.strip()
227
+ elif sys.platform.startswith("linux"):
228
+ with open("/proc/cpuinfo", "r", encoding="utf-8", errors="ignore") as f:
229
+ for line in f:
230
+ if "model name" in line:
231
+ model = line.split(":", 1)[1].strip()
232
+ break
233
+ except Exception:
234
+ pass
235
+
236
+ if model == "Unknown CPU":
237
+ model = f"{arch} Processor ({logical} threads)"
238
+
239
+ return CPUInfo(model=model, physical_cores=physical, logical_cores=logical, arch=arch)
240
+
241
+ @classmethod
242
+ def _detect_ram(cls) -> RAMInfo:
243
+ total_gb = 8.0
244
+ avail_gb = 4.0
245
+ used_gb = 4.0
246
+ percent = 50.0
247
+
248
+ try:
249
+ import psutil
250
+ vm = psutil.virtual_memory()
251
+ total_gb = round(vm.total / (1024**3), 1)
252
+ avail_gb = round(vm.available / (1024**3), 1)
253
+ used_gb = round(vm.used / (1024**3), 1)
254
+ percent = round(vm.percent, 1)
255
+ return RAMInfo(total_gb=total_gb, available_gb=avail_gb, used_gb=used_gb, percent_used=percent)
256
+ except Exception:
257
+ pass
258
+
259
+ # Windows ctypes fallback
260
+ if sys.platform == "win32":
261
+ try:
262
+ import ctypes
263
+ class MEMORYSTATUSEX(ctypes.Structure):
264
+ _fields_ = [
265
+ ("dwLength", ctypes.c_ulong),
266
+ ("dwMemoryLoad", ctypes.c_ulong),
267
+ ("ullTotalPhys", ctypes.c_ulonglong),
268
+ ("ullAvailPhys", ctypes.c_ulonglong),
269
+ ("ullTotalPageFile", ctypes.c_ulonglong),
270
+ ("ullAvailPageFile", ctypes.c_ulonglong),
271
+ ("ullTotalVirtual", ctypes.c_ulonglong),
272
+ ("ullAvailVirtual", ctypes.c_ulonglong),
273
+ ("ullAvailExtendedVirtual", ctypes.c_ulonglong),
274
+ ]
275
+ stat = MEMORYSTATUSEX(dwLength=ctypes.sizeof(MEMORYSTATUSEX))
276
+ if ctypes.windll.kernel32.GlobalMemoryStatusEx(ctypes.byref(stat)):
277
+ total_gb = round(stat.ullTotalPhys / (1024**3), 1)
278
+ avail_gb = round(stat.ullAvailPhys / (1024**3), 1)
279
+ used_gb = round((stat.ullTotalPhys - stat.ullAvailPhys) / (1024**3), 1)
280
+ percent = float(stat.dwMemoryLoad)
281
+ except Exception:
282
+ pass
283
+
284
+ return RAMInfo(total_gb=total_gb, available_gb=avail_gb, used_gb=used_gb, percent_used=percent)
285
+
286
+ @classmethod
287
+ def _detect_gpu(cls) -> GPUInfo:
288
+ # 1. Check NVIDIA via nvidia-smi
289
+ if shutil.which("nvidia-smi"):
290
+ try:
291
+ cmd = ["nvidia-smi", "--query-gpu=name,memory.total,memory.free,driver_version", "--format=csv,noheader"]
292
+ out = subprocess.check_output(cmd, timeout=3, text=True)
293
+ lines = [l.strip() for l in out.splitlines() if l.strip()]
294
+ if lines:
295
+ parts = [p.strip() for p in lines[0].split(",")]
296
+ name = parts[0]
297
+ vram_total = 0.0
298
+ vram_free = 0.0
299
+ driver = ""
300
+ if len(parts) > 1:
301
+ try:
302
+ vram_total = round(float(parts[1].replace("MiB", "").strip()) / 1024, 1)
303
+ except Exception:
304
+ pass
305
+ if len(parts) > 2:
306
+ try:
307
+ vram_free = round(float(parts[2].replace("MiB", "").strip()) / 1024, 1)
308
+ except Exception:
309
+ pass
310
+ if len(parts) > 3:
311
+ driver = parts[3]
312
+ return GPUInfo(
313
+ detected=True,
314
+ name=name,
315
+ vendor="NVIDIA",
316
+ vram_total_gb=vram_total,
317
+ vram_free_gb=vram_free,
318
+ backend="CUDA",
319
+ driver_version=driver,
320
+ )
321
+ except Exception:
322
+ pass
323
+
324
+ # 2. Check Apple Silicon via sysctl
325
+ if sys.platform == "darwin":
326
+ try:
327
+ out = subprocess.check_output(["sysctl", "-n", "machdep.cpu.brand_string"], text=True, timeout=2)
328
+ if "Apple" in out:
329
+ # Apple unified memory shares system RAM
330
+ import psutil
331
+ total_ram = round(psutil.virtual_memory().total / (1024**3), 1)
332
+ return GPUInfo(
333
+ detected=True,
334
+ name=out.strip() + " GPU (Unified)",
335
+ vendor="Apple",
336
+ vram_total_gb=total_ram,
337
+ vram_free_gb=round(total_ram * 0.7, 1),
338
+ backend="Metal",
339
+ driver_version="Metal 3",
340
+ )
341
+ except Exception:
342
+ pass
343
+
344
+ # 3. Check AMD ROCm / Linux
345
+ if shutil.which("rocm-smi"):
346
+ try:
347
+ out = subprocess.check_output(["rocm-smi", "--showmeminfo", "vram"], text=True, timeout=3)
348
+ return GPUInfo(detected=True, name="AMD Radeon (ROCm)", vendor="AMD", backend="ROCm")
349
+ except Exception:
350
+ pass
351
+
352
+ # 4. Windows WMIC / DirectX VideoController fallback
353
+ if sys.platform == "win32":
354
+ try:
355
+ out = subprocess.check_output("wmic path win32_VideoController get name,adapterram", shell=True, timeout=3, text=True)
356
+ lines = [l.strip() for l in out.splitlines() if l.strip() and "Name" not in l and "AdapterRAM" not in l]
357
+ for line in lines:
358
+ parts = line.split()
359
+ if parts:
360
+ name = " ".join(parts[:-1]) if len(parts) > 1 else parts[0]
361
+ vram_bytes = parts[-1] if len(parts) > 1 and parts[-1].isdigit() else "0"
362
+ vram_gb = round(int(vram_bytes) / (1024**3), 1) if vram_bytes.isdigit() else 0.0
363
+ vendor = "NVIDIA" if "NVIDIA" in name.upper() else ("AMD" if "AMD" in name.upper() or "RADEON" in name.upper() else ("Intel" if "INTEL" in name.upper() else "Unknown"))
364
+ backend = "CUDA" if vendor == "NVIDIA" else "DirectML"
365
+ if vram_gb > 0.5 or "GeForce" in name or "Radeon" in name or "RTX" in name:
366
+ return GPUInfo(
367
+ detected=True,
368
+ name=name[:40],
369
+ vendor=vendor,
370
+ vram_total_gb=vram_gb,
371
+ vram_free_gb=vram_gb,
372
+ backend=backend,
373
+ )
374
+ except Exception:
375
+ pass
376
+
377
+ return GPUInfo()
378
+
379
+ @classmethod
380
+ def _detect_storage(cls) -> StorageInfo:
381
+ path = os.path.expanduser("~")
382
+ total_gb = 0.0
383
+ free_gb = 0.0
384
+ used_gb = 0.0
385
+ try:
386
+ du = shutil.disk_usage(path)
387
+ total_gb = round(du.total / (1024**3), 1)
388
+ free_gb = round(du.free / (1024**3), 1)
389
+ used_gb = round(du.used / (1024**3), 1)
390
+ except Exception:
391
+ pass
392
+ return StorageInfo(total_gb=total_gb, free_gb=free_gb, used_gb=used_gb, path=path)
393
+
394
+ @classmethod
395
+ def _evaluate_capability(cls, ram: RAMInfo, gpu: GPUInfo, cpu: CPUInfo) -> Tuple[str, list[str]]:
396
+ notes = []
397
+ effective_vram = gpu.vram_total_gb if gpu.detected else 0.0
398
+ total_ram = ram.total_gb
399
+
400
+ # High-end GPU or high unified memory
401
+ if (gpu.detected and effective_vram >= 12.0) or (gpu.backend == "Metal" and total_ram >= 32.0):
402
+ capability = "EXCELLENT"
403
+ notes.append(f"High-speed {gpu.backend} acceleration with {effective_vram}GB memory.")
404
+ notes.append("Can comfortably run 8B-14B models with zero offloading, and 32B-70B with partial quantization.")
405
+ elif (gpu.detected and effective_vram >= 4.0) or total_ram >= 24.0:
406
+ capability = "GOOD"
407
+ notes.append(f"Solid local AI foundation: {gpu.name} ({effective_vram}GB VRAM) + {total_ram}GB RAM.")
408
+ notes.append("Ideal for 3B-8B parameter models with near-instant interactive token generation.")
409
+ if total_ram >= 32.0 and effective_vram < 8.0:
410
+ notes.append("Ample System RAM enables running 14B-32B models with hybrid CPU/GPU offloading.")
411
+ elif total_ram >= 12.0:
412
+ capability = "MODERATE"
413
+ notes.append(f"Capable of running compact 1B-4B models locally via CPU/RAM.")
414
+ notes.append("Cloud models recommended for multi-file coding or complex reasoning tasks.")
415
+ else:
416
+ capability = "LIMITED"
417
+ notes.append("Hardware is resource-constrained for large local inference.")
418
+ notes.append("Recommended to use lightweight 1B models or cloud AI providers.")
419
+
420
+ return capability, notes
421
+
422
+ @classmethod
423
+ def summary_text(cls) -> str:
424
+ """Returns a concise, beautifully formatted human-readable hardware card."""
425
+ p = cls.analyze()
426
+ gpu_str = f"{p.gpu.name} ({p.gpu.vram_total_gb} GB {p.gpu.backend})" if p.gpu.detected else "None (CPU inference)"
427
+ lines = [
428
+ "CAT HARDWARE & ACCELERATION ANALYSIS",
429
+ "----------------------------------------------",
430
+ f"CPU : {p.cpu.model} ({p.cpu.logical_cores} threads)",
431
+ f"System RAM : {p.ram.total_gb} GB ({p.ram.available_gb} GB available)",
432
+ f"GPU : {gpu_str}",
433
+ f"Model Storage : {p.storage.free_gb} GB free on {p.storage.path}",
434
+ f"Operating Sys : {p.os_name} ({p.cpu.arch})",
435
+ "----------------------------------------------",
436
+ f"Local AI Tier : {p.ai_capability}",
437
+ ]
438
+ for note in p.ai_capability_notes:
439
+ lines.append(f" * {note}")
440
+ return "\n".join(lines)
@@ -0,0 +1,30 @@
1
+ """
2
+ CAT Browser subsystem — embedded QWebEngineView renderer owned by CAT.
3
+
4
+ When CAT needs to render a real webpage (HTML/CSS/JavaScript), it uses
5
+ this subsystem to create a browser window with QWebEngineView (Chromium).
6
+
7
+ The browser is NOT a separate application. It is created by CAT on demand
8
+ and destroyed when closed.
9
+
10
+ Usage:
11
+ from calc_terminal.host import launch_cat_host, can_launch_host
12
+ if can_launch_host():
13
+ launch_cat_host(start_browser_url="http://localhost:3000/connector.html")
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ def launch_cat_host(*args, **kwargs):
19
+ from .launcher import launch_cat_host as _impl
20
+ return _impl(*args, **kwargs)
21
+
22
+ def can_launch_host(*args, **kwargs):
23
+ from .launcher import can_launch_host as _impl
24
+ return _impl(*args, **kwargs)
25
+
26
+ def get_host_status(*args, **kwargs):
27
+ from .launcher import get_host_status as _impl
28
+ return _impl(*args, **kwargs)
29
+
30
+ __all__ = ["launch_cat_host", "can_launch_host", "get_host_status"]
@@ -0,0 +1,187 @@
1
+ """
2
+ CAT BrowserManager — internal browser controller owned by CAT.
3
+
4
+ Lifecycle belongs to CAT (no browser.exe, no separate app):
5
+
6
+ CAT (calc_terminal/host/desktop.py CATDesktopWindow)
7
+ └── BrowserManager
8
+ ├── EmbeddedBrowserPane (QWebEngineView child)
9
+ ├── BrowserState (tabs/history/bookmarks persistence)
10
+ └── QWebEngineProfile (cookies/storage/WebAuthn)
11
+
12
+ API:
13
+
14
+ createTab(), closeTab(), navigate(url), goBack(), goForward(),
15
+ reload(), executeScript(js), setViewport(w,h), destroy()
16
+
17
+ The browser renderer (QWebEngineView) is the working Chromium-based
18
+ engine that renders real HTML/CSS/JavaScript. It is integrated directly
19
+ into CAT's browser window — not launched as a separate application.
20
+
21
+ All rendering is LIVE (Chromium via Qt WebEngine / WebView2), not
22
+ screenshots, not text extraction.
23
+ """
24
+
25
+ from __future__ import annotations
26
+
27
+ from typing import Optional
28
+
29
+ try:
30
+ from PySide6.QtCore import QSize, QUrl
31
+ from PySide6.QtWebEngineCore import QWebEngineProfile
32
+ _QT = True
33
+ except ImportError:
34
+ try:
35
+ from PyQt6.QtCore import QSize, QUrl # type: ignore
36
+ from PyQt6.QtWebEngineCore import QWebEngineProfile # type: ignore
37
+ _QT = True
38
+ except ImportError:
39
+ _QT = False
40
+
41
+
42
+ class BrowserManager:
43
+ """
44
+ CAT-owned browser controller. One instance per CAT process/host window.
45
+
46
+ Wraps an EmbeddedBrowserPane (the graphical QWebEngineView surface) and
47
+ exposes a stable API for CAT CLI, AI Agent, and Fomoji auth to drive.
48
+
49
+ Example:
50
+ mgr = BrowserManager(browser_pane)
51
+ mgr.navigate("http://localhost:3000/connector.html")
52
+ mgr.goBack()
53
+ mgr.executeScript("document.title")
54
+ """
55
+
56
+ def __init__(self, browser_pane):
57
+ """
58
+ browser_pane: EmbeddedBrowserPane instance from desktop.py
59
+ """
60
+ self.pane = browser_pane
61
+
62
+ # ------------------------------------------------------------------ tabs --
63
+
64
+ def createTab(self, url: str = "about:home", switch: bool = True):
65
+ """Create a new tab. Returns tab index."""
66
+ return self.pane.new_tab(url)
67
+
68
+ def closeTab(self, index: Optional[int] = None):
69
+ """Close tab at index, or current tab if None."""
70
+ if index is None:
71
+ index = self.pane.tab_bar.currentIndex()
72
+ self.pane._close_tab(index)
73
+
74
+ def switchTab(self, index: int):
75
+ self.pane.tab_bar.setCurrentIndex(index)
76
+ self.pane.viewport_stack.setCurrentIndex(index)
77
+ self.pane._sync_chrome()
78
+
79
+ def tabCount(self) -> int:
80
+ return self.pane.tab_bar.count()
81
+
82
+ # --------------------------------------------------------------- navigation --
83
+
84
+ def navigate(self, url: str):
85
+ """Navigate current tab to url (real HTML/CSS/JS, not text)."""
86
+ self.pane.navigate(url)
87
+
88
+ def goBack(self):
89
+ self.pane.go_back()
90
+
91
+ def goForward(self):
92
+ self.pane.go_forward()
93
+
94
+ def reload(self):
95
+ self.pane.reload()
96
+
97
+ def home(self):
98
+ self.pane.navigate("about:home")
99
+
100
+ def currentUrl(self) -> str:
101
+ try:
102
+ idx = self.pane.tab_bar.currentIndex()
103
+ w = self.pane._views[idx]
104
+ if self.pane._is_newtab(w):
105
+ return "about:home"
106
+ return w.url().toString() # type: ignore
107
+ except Exception:
108
+ return ""
109
+
110
+ def currentTitle(self) -> str:
111
+ try:
112
+ idx = self.pane.tab_bar.currentIndex()
113
+ w = self.pane._views[idx]
114
+ if self.pane._is_newtab(w):
115
+ return "New Tab"
116
+ return w.title() # type: ignore
117
+ except Exception:
118
+ return ""
119
+
120
+ # -------------------------------------------------------------- scripting --
121
+
122
+ def executeScript(self, js: str, callback=None):
123
+ """
124
+ Execute JavaScript in the current page's context (live DOM).
125
+ callback receives the result (if any).
126
+ """
127
+ try:
128
+ idx = self.pane.tab_bar.currentIndex()
129
+ w = self.pane._views[idx]
130
+ if not self.pane._is_newtab(w):
131
+ page = w.page() # type: ignore
132
+ if callback:
133
+ page.runJavaScript(js, callback)
134
+ else:
135
+ page.runJavaScript(js)
136
+ return True
137
+ except Exception:
138
+ pass
139
+ return False
140
+
141
+ def setViewport(self, width: int, height: int):
142
+ """Resize the web viewport — CSS sees real dimensions (vw/vh/media queries)."""
143
+ try:
144
+ idx = self.pane.tab_bar.currentIndex()
145
+ w = self.pane._views[idx]
146
+ if not self.pane._is_newtab(w):
147
+ w.page().setViewportSize(QSize(int(width), int(height))) # type: ignore
148
+ return True
149
+ except Exception:
150
+ pass
151
+ return False
152
+
153
+ # -------------------------------------------------------------- lifecycle --
154
+
155
+ def destroy(self):
156
+ """Tear down browser with CAT (no orphan process)."""
157
+ try:
158
+ # Close all WebEngineViews; profile will be destroyed with parent
159
+ for w in list(getattr(self.pane, "_views", [])):
160
+ try:
161
+ w.deleteLater()
162
+ except Exception:
163
+ pass
164
+ self.pane._views.clear() # type: ignore
165
+ except Exception:
166
+ pass
167
+
168
+ # -------------------------------------------------------------- devtools / AI hooks --
169
+
170
+ def getConsoleLogs(self):
171
+ """Return recent console logs for AI Agent inspection (future)."""
172
+ # EmbeddedBrowserPane could expose page console via QWebEnginePage.javaScriptConsoleMessage
173
+ # For now return empty; hook point for AI integration.
174
+ return []
175
+
176
+ def screenshot(self, path: str) -> bool:
177
+ """For AI analysis — capture viewport to file (not used for rendering)."""
178
+ try:
179
+ idx = self.pane.tab_bar.currentIndex()
180
+ w = self.pane._views[idx]
181
+ if not self.pane._is_newtab(w):
182
+ # QWebEngineView grab
183
+ pixmap = w.grab() # type: ignore
184
+ return pixmap.save(path)
185
+ except Exception:
186
+ pass
187
+ return False