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.
- calc_terminal/__init__.py +14 -0
- calc_terminal/__main__.py +14 -0
- calc_terminal/activity.py +1334 -0
- calc_terminal/agent.py +3387 -0
- calc_terminal/agent_runtime.py +519 -0
- calc_terminal/ai_context.py +447 -0
- calc_terminal/ai_modes.py +752 -0
- calc_terminal/ai_personalization.py +286 -0
- calc_terminal/ai_preview_feedback.py +213 -0
- calc_terminal/aicore.py +2572 -0
- calc_terminal/anim.py +367 -0
- calc_terminal/app.py +3685 -0
- calc_terminal/art.py +639 -0
- calc_terminal/atomsim.py +368 -0
- calc_terminal/attachments.py +743 -0
- calc_terminal/benchmark_system.py +414 -0
- calc_terminal/browser/__init__.py +36 -0
- calc_terminal/browser/browser_state.py +346 -0
- calc_terminal/browser/devserver.py +176 -0
- calc_terminal/browser/engine.py +494 -0
- calc_terminal/browser/navigation.py +84 -0
- calc_terminal/browser/preview.py +429 -0
- calc_terminal/browser/preview_entry.py +95 -0
- calc_terminal/browser/project_detector.py +144 -0
- calc_terminal/browser/server.py +449 -0
- calc_terminal/browser/state.py +75 -0
- calc_terminal/browser/watcher.py +99 -0
- calc_terminal/browser_gui/__init__.py +1 -0
- calc_terminal/browser_gui/__main__.py +3 -0
- calc_terminal/browser_gui/launcher.py +173 -0
- calc_terminal/browser_gui/playwright_browser.py +117 -0
- calc_terminal/browser_gui/qt_browser.py +1501 -0
- calc_terminal/browser_gui/webview_browser.py +57 -0
- calc_terminal/capabilities/__init__.py +35 -0
- calc_terminal/capabilities/adapters/__init__.py +33 -0
- calc_terminal/capabilities/adapters/bioinformatics.py +204 -0
- calc_terminal/capabilities/adapters/browser_adapter.py +205 -0
- calc_terminal/capabilities/adapters/filesystem.py +206 -0
- calc_terminal/capabilities/adapters/git_adapter.py +202 -0
- calc_terminal/capabilities/adapters/jupyter_adapter.py +138 -0
- calc_terminal/capabilities/adapters/ml_frameworks.py +158 -0
- calc_terminal/capabilities/adapters/platforms.py +200 -0
- calc_terminal/capabilities/adapters/python_exec.py +93 -0
- calc_terminal/capabilities/adapters/quantum_adapter.py +150 -0
- calc_terminal/capabilities/adapters/scientific_comp.py +123 -0
- calc_terminal/capabilities/adapters/structural_bio.py +161 -0
- calc_terminal/capabilities/adapters/terminal.py +99 -0
- calc_terminal/capabilities/bus.py +178 -0
- calc_terminal/capabilities/discovery.py +207 -0
- calc_terminal/capabilities/schema.py +221 -0
- calc_terminal/cat.ico +0 -0
- calc_terminal/cat_browser.py +2018 -0
- calc_terminal/chat_store.py +703 -0
- calc_terminal/cli.py +1178 -0
- calc_terminal/code_editor.py +640 -0
- calc_terminal/collaboration.py +723 -0
- calc_terminal/commands_data.py +139 -0
- calc_terminal/compatibility_engine.py +352 -0
- calc_terminal/compute/__init__.py +31 -0
- calc_terminal/compute/fabric.py +350 -0
- calc_terminal/config.py +227 -0
- calc_terminal/core/__init__.py +41 -0
- calc_terminal/core/checkpoint.py +156 -0
- calc_terminal/core/input/__init__.py +45 -0
- calc_terminal/core/mode_registry.py +300 -0
- calc_terminal/core/project_graph.py +172 -0
- calc_terminal/core/recovery.py +129 -0
- calc_terminal/core/security_layer.py +112 -0
- calc_terminal/core/task_graph.py +202 -0
- calc_terminal/core/unified_runtime.py +184 -0
- calc_terminal/core/verification.py +257 -0
- calc_terminal/customization.py +1566 -0
- calc_terminal/derivations.py +153 -0
- calc_terminal/device_control.py +263 -0
- calc_terminal/diagnostics/__init__.py +27 -0
- calc_terminal/diagnostics/doctor_engine.py +382 -0
- calc_terminal/diagnostics/self_test.py +247 -0
- calc_terminal/doctor.py +519 -0
- calc_terminal/easter_eggs.py +274 -0
- calc_terminal/editor/__init__.py +1 -0
- calc_terminal/editor/actions.py +263 -0
- calc_terminal/editor/commands.py +160 -0
- calc_terminal/editor/shortcuts.py +226 -0
- calc_terminal/engine.py +259 -0
- calc_terminal/errors.py +120 -0
- calc_terminal/event_stream.py +146 -0
- calc_terminal/eventbus.py +133 -0
- calc_terminal/extensions.py +733 -0
- calc_terminal/fallback_cli.py +1321 -0
- calc_terminal/first_run.py +265 -0
- calc_terminal/fomoji_auth.py +1043 -0
- calc_terminal/formulas.py +82 -0
- calc_terminal/fs_cache.py +121 -0
- calc_terminal/fs_watcher.py +277 -0
- calc_terminal/game.py +193 -0
- calc_terminal/gen1.py +5 -0
- calc_terminal/generators.py +245 -0
- calc_terminal/gestures/__init__.py +42 -0
- calc_terminal/gestures/bindings.py +175 -0
- calc_terminal/gestures/manager.py +477 -0
- calc_terminal/goodbye.py +363 -0
- calc_terminal/gpu3d.py +290 -0
- calc_terminal/graphs.py +358 -0
- calc_terminal/hardware_analyzer.py +440 -0
- calc_terminal/host/__init__.py +30 -0
- calc_terminal/host/browser_manager.py +187 -0
- calc_terminal/host/desktop.py +1386 -0
- calc_terminal/host/launcher.py +395 -0
- calc_terminal/host/terminal.py +279 -0
- calc_terminal/identity.py +216 -0
- calc_terminal/input/__init__.py +54 -0
- calc_terminal/input/capabilities.py +258 -0
- calc_terminal/input/focus.py +87 -0
- calc_terminal/input/gestures.py +64 -0
- calc_terminal/input/pointer.py +114 -0
- calc_terminal/input/touch.py +345 -0
- calc_terminal/keys.py +84 -0
- calc_terminal/live_automation.py +165 -0
- calc_terminal/mathtext.py +433 -0
- calc_terminal/mcp.py +386 -0
- calc_terminal/memory.py +337 -0
- calc_terminal/memory_v2.py +479 -0
- calc_terminal/metrics.py +333 -0
- calc_terminal/mode_detection.py +146 -0
- calc_terminal/model.py +2431 -0
- calc_terminal/model_router.py +665 -0
- calc_terminal/models/__init__.py +0 -0
- calc_terminal/models/active_state.py +187 -0
- calc_terminal/models/dynamic_registry.py +584 -0
- calc_terminal/models/manager.py +781 -0
- calc_terminal/models/model_metadata.json +3526 -0
- calc_terminal/models/profiles.py +194 -0
- calc_terminal/models/registry.py +265 -0
- calc_terminal/models/schema.py +197 -0
- calc_terminal/models/validator.py +287 -0
- calc_terminal/models/verification_engine.py +368 -0
- calc_terminal/native_picker.py +215 -0
- calc_terminal/ollama_catalog.py +279 -0
- calc_terminal/ollama_download.py +233 -0
- calc_terminal/orchestrator.py +304 -0
- calc_terminal/package_research.py +322 -0
- calc_terminal/packages.py +1024 -0
- calc_terminal/pc_specs.py +116 -0
- calc_terminal/permissions.py +334 -0
- calc_terminal/pet.py +106 -0
- calc_terminal/pipeline.py +505 -0
- calc_terminal/platform/__init__.py +491 -0
- calc_terminal/platform/desktop.py +491 -0
- calc_terminal/platform/web.py +781 -0
- calc_terminal/preview/__init__.py +1 -0
- calc_terminal/preview/dev_server.py +303 -0
- calc_terminal/preview/diagnostics.py +131 -0
- calc_terminal/preview/live_reload.py +66 -0
- calc_terminal/preview/manager.py +129 -0
- calc_terminal/project_stats.py +209 -0
- calc_terminal/projects.py +328 -0
- calc_terminal/providers/__init__.py +0 -0
- calc_terminal/providers/adapters/__init__.py +80 -0
- calc_terminal/providers/adapters/anthropic_adapter.py +127 -0
- calc_terminal/providers/adapters/base.py +106 -0
- calc_terminal/providers/adapters/chinese_adapters.py +420 -0
- calc_terminal/providers/adapters/gemini_adapter.py +101 -0
- calc_terminal/providers/adapters/ollama_adapter.py +83 -0
- calc_terminal/providers/adapters/openai_adapter.py +159 -0
- calc_terminal/providers/adapters/other_adapters.py +246 -0
- calc_terminal/providers/anthropic_provider.py +172 -0
- calc_terminal/providers/auto_update.py +416 -0
- calc_terminal/providers/base_provider.py +105 -0
- calc_terminal/providers/discovery_manager.py +207 -0
- calc_terminal/providers/gemini_provider.py +178 -0
- calc_terminal/providers/lifecycle.py +767 -0
- calc_terminal/providers/ollama_adapter.py +707 -0
- calc_terminal/providers/openai_provider.py +254 -0
- calc_terminal/providers/provider_manager.py +1827 -0
- calc_terminal/providers/providers.json +4075 -0
- calc_terminal/reactionsim.py +279 -0
- calc_terminal/registry.py +337 -0
- calc_terminal/report.py +162 -0
- calc_terminal/research/__init__.py +45 -0
- calc_terminal/research/artifact_intel.py +126 -0
- calc_terminal/research/data_lineage.py +123 -0
- calc_terminal/research/experiment_ledger.py +303 -0
- calc_terminal/research/reproducibility.py +131 -0
- calc_terminal/resilience/__init__.py +47 -0
- calc_terminal/resilience/agent_state.py +121 -0
- calc_terminal/resilience/capability_matcher.py +174 -0
- calc_terminal/resilience/circuit_breaker.py +158 -0
- calc_terminal/resilience/failover_engine.py +230 -0
- calc_terminal/resilience/health_monitor.py +192 -0
- calc_terminal/resilience/ollama_adapter.py +125 -0
- calc_terminal/resilience/orchestrator.py +312 -0
- calc_terminal/resilience/types.py +134 -0
- calc_terminal/sandbox.py +98 -0
- calc_terminal/scires.py +558 -0
- calc_terminal/security_scanner.py +126 -0
- calc_terminal/session.py +294 -0
- calc_terminal/sim3d.py +206 -0
- calc_terminal/solver.py +276 -0
- calc_terminal/sound.py +127 -0
- calc_terminal/task_reports.py +287 -0
- calc_terminal/terminal_host.py +201 -0
- calc_terminal/terminal_identity.py +411 -0
- calc_terminal/test_ai_mode_reliability.py +344 -0
- calc_terminal/test_browser.py +368 -0
- calc_terminal/test_code_editor_upgrade.py +485 -0
- calc_terminal/test_customization.py +1148 -0
- calc_terminal/test_customization_ui.py +612 -0
- calc_terminal/test_dynamic_registry.py +304 -0
- calc_terminal/test_extensions.py +436 -0
- calc_terminal/test_overhaul.py +557 -0
- calc_terminal/test_project_detect.py +255 -0
- calc_terminal/test_root_cause_fix.py +527 -0
- calc_terminal/test_stability.py +532 -0
- calc_terminal/test_terminal_identity.py +132 -0
- calc_terminal/test_v079_speed.py +460 -0
- calc_terminal/theme.py +1107 -0
- calc_terminal/timeline.py +139 -0
- calc_terminal/todos.py +246 -0
- calc_terminal/tool_call_normalizer.py +419 -0
- calc_terminal/tui.py +104 -0
- calc_terminal/ui/__init__.py +8 -0
- calc_terminal/ui/activity_panel.py +231 -0
- calc_terminal/ui/activity_stream_panel.py +238 -0
- calc_terminal/ui/animations.py +122 -0
- calc_terminal/ui/app.py +7271 -0
- calc_terminal/ui/attach_panel.py +597 -0
- calc_terminal/ui/attachments.py +424 -0
- calc_terminal/ui/backup_panel.py +810 -0
- calc_terminal/ui/browser_shell.py +887 -0
- calc_terminal/ui/cat_agent.py +357 -0
- calc_terminal/ui/chats_panel.py +899 -0
- calc_terminal/ui/command_palette.py +125 -0
- calc_terminal/ui/command_palette_modal.py +166 -0
- calc_terminal/ui/composer.py +1141 -0
- calc_terminal/ui/context_menu.py +197 -0
- calc_terminal/ui/conversation.py +1435 -0
- calc_terminal/ui/customization_panel.py +1229 -0
- calc_terminal/ui/dashboard.py +404 -0
- calc_terminal/ui/design_system.py +557 -0
- calc_terminal/ui/diff_panel.py +213 -0
- calc_terminal/ui/editor.py +2102 -0
- calc_terminal/ui/empty_state.py +302 -0
- calc_terminal/ui/events.py +487 -0
- calc_terminal/ui/extensions_panel.py +815 -0
- calc_terminal/ui/footer.py +166 -0
- calc_terminal/ui/gestures_panel.py +383 -0
- calc_terminal/ui/goodbye_screen.py +100 -0
- calc_terminal/ui/header.py +1034 -0
- calc_terminal/ui/help_panel.py +254 -0
- calc_terminal/ui/live_activities.py +914 -0
- calc_terminal/ui/mcp_panel.py +570 -0
- calc_terminal/ui/memory_center.py +524 -0
- calc_terminal/ui/mode_colors_panel.py +525 -0
- calc_terminal/ui/nav_screens.py +747 -0
- calc_terminal/ui/ollama_panel.py +536 -0
- calc_terminal/ui/palette.py +221 -0
- calc_terminal/ui/permission_panel.py +269 -0
- calc_terminal/ui/personalization_panel.py +517 -0
- calc_terminal/ui/personalize_center.py +1568 -0
- calc_terminal/ui/preview_panel.py +441 -0
- calc_terminal/ui/resizers.py +402 -0
- calc_terminal/ui/sidebar.py +1285 -0
- calc_terminal/ui/statusbar.py +168 -0
- calc_terminal/ui/theme_css.py +1396 -0
- calc_terminal/ui/thinking.py +226 -0
- calc_terminal/ui/timeline_panel.py +102 -0
- calc_terminal/ui/todo_panel.py +193 -0
- calc_terminal/ui/viewport.py +136 -0
- calc_terminal/ui/vision_panel.py +489 -0
- calc_terminal/ui/welcome_modal.py +343 -0
- calc_terminal/ui/widgets.py +160 -0
- calc_terminal/ui/workspace.py +831 -0
- calc_terminal/viewers/__init__.py +1 -0
- calc_terminal/viewers/document_viewer.py +252 -0
- calc_terminal/viewers/image_viewer.py +241 -0
- calc_terminal/viewers/pdf_viewer.py +203 -0
- calc_terminal/viewers/presentation_viewer.py +164 -0
- calc_terminal/viewers/registry.py +120 -0
- calc_terminal/viewers/spreadsheet_viewer.py +204 -0
- calc_terminal/vision/__init__.py +89 -0
- calc_terminal/vision/analysis.py +194 -0
- calc_terminal/vision/annotations.py +297 -0
- calc_terminal/vision/capture.py +171 -0
- calc_terminal/vision/context.py +231 -0
- calc_terminal/vision/correlation.py +169 -0
- calc_terminal/vision/cursor.py +258 -0
- calc_terminal/vision/events.py +66 -0
- calc_terminal/vision/frame_pipeline.py +259 -0
- calc_terminal/vision/priority.py +218 -0
- calc_terminal/vision/provider.py +180 -0
- calc_terminal/vision/safety.py +149 -0
- calc_terminal/vision/session.py +281 -0
- calc_terminal/vision/verify.py +162 -0
- calc_terminal/vision.py +514 -0
- calc_terminal/vscode_integration.py +113 -0
- calc_terminal/web/__init__.py +8 -0
- calc_terminal/web/cat_runtime.py +710 -0
- calc_terminal/web/server.py +2891 -0
- calc_terminal/web/static/css/app.css +3152 -0
- calc_terminal/web/static/icons/badge-72.png +0 -0
- calc_terminal/web/static/icons/cat.ico +0 -0
- calc_terminal/web/static/icons/icon-128.png +0 -0
- calc_terminal/web/static/icons/icon-144.png +0 -0
- calc_terminal/web/static/icons/icon-152.png +0 -0
- calc_terminal/web/static/icons/icon-192.png +0 -0
- calc_terminal/web/static/icons/icon-384.png +0 -0
- calc_terminal/web/static/icons/icon-512.png +0 -0
- calc_terminal/web/static/icons/icon-72.png +0 -0
- calc_terminal/web/static/icons/icon-96.png +0 -0
- calc_terminal/web/static/icons/icon.svg +34 -0
- calc_terminal/web/static/icons/new-project.png +0 -0
- calc_terminal/web/static/icons/open-project.png +0 -0
- calc_terminal/web/static/index.html +734 -0
- calc_terminal/web/static/js/app.js +2403 -0
- calc_terminal/web/static/manifest.json +88 -0
- calc_terminal/web/static/sw.js +230 -0
- calc_terminal/workflow_engine.py +769 -0
- calc_terminal/workspace.py +593 -0
- calc_terminal/workspace_index.py +385 -0
- cct_cli-0.7.9.0.dist-info/METADATA +210 -0
- cct_cli-0.7.9.0.dist-info/RECORD +325 -0
- cct_cli-0.7.9.0.dist-info/WHEEL +5 -0
- cct_cli-0.7.9.0.dist-info/entry_points.txt +4 -0
- cct_cli-0.7.9.0.dist-info/licenses/LICENSE +21 -0
- cct_cli-0.7.9.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Scientific Computing Capability Adapter per §43:
|
|
3
|
+
- matlab.execute
|
|
4
|
+
- numpy.compute
|
|
5
|
+
- scipy.optimize
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import shutil
|
|
11
|
+
import subprocess
|
|
12
|
+
from typing import Any, Dict, Optional, Tuple
|
|
13
|
+
|
|
14
|
+
from ..schema import (
|
|
15
|
+
AvailabilityStatus,
|
|
16
|
+
Capability,
|
|
17
|
+
CapabilityCategory,
|
|
18
|
+
CapabilitySpec,
|
|
19
|
+
ExecutionResult,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _check_matlab_health() -> Tuple[AvailabilityStatus, str]:
|
|
24
|
+
if shutil.which("matlab"):
|
|
25
|
+
return AvailabilityStatus.AVAILABLE, "MATLAB CLI executable found"
|
|
26
|
+
try:
|
|
27
|
+
import matlab.engine # noqa: F401
|
|
28
|
+
return AvailabilityStatus.AVAILABLE, "MATLAB Python Engine available"
|
|
29
|
+
except ImportError:
|
|
30
|
+
return AvailabilityStatus.SOFTWARE_NOT_FOUND, "MATLAB is not installed on this system"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _check_scientific_health() -> Tuple[AvailabilityStatus, str]:
|
|
34
|
+
try:
|
|
35
|
+
import numpy # noqa: F401
|
|
36
|
+
import scipy # noqa: F401
|
|
37
|
+
return AvailabilityStatus.AVAILABLE, f"NumPy {numpy.__version__}, SciPy {scipy.__version__} ready"
|
|
38
|
+
except ImportError as e:
|
|
39
|
+
return AvailabilityStatus.SOFTWARE_NOT_FOUND, f"Scientific packages missing: {e}"
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _matlab_execute_handler(args: Dict[str, Any], context: Optional[Dict[str, Any]] = None) -> ExecutionResult:
|
|
43
|
+
script = args.get("script", "")
|
|
44
|
+
code = args.get("code", "")
|
|
45
|
+
if not script and not code:
|
|
46
|
+
return ExecutionResult(success=False, error="script or code argument is required")
|
|
47
|
+
|
|
48
|
+
exe = shutil.which("matlab")
|
|
49
|
+
if not exe:
|
|
50
|
+
return ExecutionResult(
|
|
51
|
+
success=False,
|
|
52
|
+
error="MATLAB executable not found on host machine. Please install and license MATLAB.",
|
|
53
|
+
status=AvailabilityStatus.SOFTWARE_NOT_FOUND,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
matlab_cmd = f"-batch \"{code}\"" if code else f"-batch \"run('{script}')\""
|
|
57
|
+
try:
|
|
58
|
+
proc = subprocess.run([exe, "-nodisplay", "-nosplash", "-batch", code or f"run('{script}')"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, timeout=120)
|
|
59
|
+
return ExecutionResult(
|
|
60
|
+
success=(proc.returncode == 0),
|
|
61
|
+
output=proc.stdout,
|
|
62
|
+
error=proc.stderr if proc.returncode != 0 else None,
|
|
63
|
+
metadata={"exit_code": proc.returncode},
|
|
64
|
+
)
|
|
65
|
+
except Exception as e:
|
|
66
|
+
return ExecutionResult(success=False, error=f"MATLAB execution failed: {e}")
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _numpy_compute_handler(args: Dict[str, Any], context: Optional[Dict[str, Any]] = None) -> ExecutionResult:
|
|
70
|
+
expr = args.get("expression", "")
|
|
71
|
+
if not expr:
|
|
72
|
+
return ExecutionResult(success=False, error="expression argument is required")
|
|
73
|
+
|
|
74
|
+
try:
|
|
75
|
+
import numpy as np
|
|
76
|
+
# Evaluate safely within numpy scope
|
|
77
|
+
scope = {"np": np}
|
|
78
|
+
# Disallow dangerous builtins
|
|
79
|
+
res = eval(expr, {"__builtins__": {}}, scope)
|
|
80
|
+
if isinstance(res, np.ndarray):
|
|
81
|
+
out = {
|
|
82
|
+
"shape": list(res.shape),
|
|
83
|
+
"dtype": str(res.dtype),
|
|
84
|
+
"data": res.tolist() if res.size <= 1000 else "Array too large to serialize; summary displayed",
|
|
85
|
+
"mean": float(np.mean(res)) if res.size > 0 else None,
|
|
86
|
+
}
|
|
87
|
+
else:
|
|
88
|
+
out = res
|
|
89
|
+
return ExecutionResult(success=True, output=out)
|
|
90
|
+
except Exception as e:
|
|
91
|
+
return ExecutionResult(success=False, error=f"NumPy computation error: {e}")
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def register_scientific_capabilities(bus):
|
|
95
|
+
bus.register(Capability(
|
|
96
|
+
spec=CapabilitySpec(
|
|
97
|
+
name="matlab.execute",
|
|
98
|
+
version="1.0.0",
|
|
99
|
+
category=CapabilityCategory.SCIENTIFIC,
|
|
100
|
+
description="Execute MATLAB script or expressions in non-display batch mode.",
|
|
101
|
+
input_schema={"code": "string?", "script": "string?"},
|
|
102
|
+
output_schema={"output": "string"},
|
|
103
|
+
permissions=["execute_terminal"],
|
|
104
|
+
documentation="Executes MATLAB commands where licensed.",
|
|
105
|
+
),
|
|
106
|
+
handler=_matlab_execute_handler,
|
|
107
|
+
health_checker=_check_matlab_health,
|
|
108
|
+
))
|
|
109
|
+
|
|
110
|
+
bus.register(Capability(
|
|
111
|
+
spec=CapabilitySpec(
|
|
112
|
+
name="numpy.compute",
|
|
113
|
+
version="1.0.0",
|
|
114
|
+
category=CapabilityCategory.SCIENTIFIC,
|
|
115
|
+
description="Perform numerical array calculations using NumPy.",
|
|
116
|
+
input_schema={"expression": "string"},
|
|
117
|
+
output_schema={"result": "any"},
|
|
118
|
+
permissions=["read_workspace"],
|
|
119
|
+
documentation="Evaluates mathematical numpy expressions.",
|
|
120
|
+
),
|
|
121
|
+
handler=_numpy_compute_handler,
|
|
122
|
+
health_checker=_check_scientific_health,
|
|
123
|
+
))
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Structural Biology Capability Adapter per §44:
|
|
3
|
+
- pymol.open
|
|
4
|
+
- chimerax.open
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import os
|
|
10
|
+
import shutil
|
|
11
|
+
import subprocess
|
|
12
|
+
from typing import Any, Dict, Optional, Tuple
|
|
13
|
+
|
|
14
|
+
from ..schema import (
|
|
15
|
+
AvailabilityStatus,
|
|
16
|
+
Capability,
|
|
17
|
+
CapabilityCategory,
|
|
18
|
+
CapabilitySpec,
|
|
19
|
+
ExecutionResult,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _find_pymol() -> Optional[str]:
|
|
24
|
+
cmd = shutil.which("pymol")
|
|
25
|
+
if cmd:
|
|
26
|
+
return cmd
|
|
27
|
+
candidates = [
|
|
28
|
+
r"C:\Program Files\PyMOL\PyMOLWin.exe",
|
|
29
|
+
r"C:\Program Files\PyMOL\PyMOL.exe",
|
|
30
|
+
r"C:\Program Files (x86)\Schrodinger\PyMOL\PyMOLWin.exe",
|
|
31
|
+
"/usr/bin/pymol",
|
|
32
|
+
"/usr/local/bin/pymol",
|
|
33
|
+
"/Applications/PyMOL.app/Contents/MacOS/PyMOL",
|
|
34
|
+
]
|
|
35
|
+
for c in candidates:
|
|
36
|
+
if os.path.exists(c):
|
|
37
|
+
return c
|
|
38
|
+
return None
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _find_chimerax() -> Optional[str]:
|
|
42
|
+
cmd = shutil.which("chimerax")
|
|
43
|
+
if cmd:
|
|
44
|
+
return cmd
|
|
45
|
+
candidates = [
|
|
46
|
+
r"C:\Program Files\ChimeraX\bin\ChimeraX.exe",
|
|
47
|
+
r"C:\Program Files\ChimeraX 1.7\bin\ChimeraX.exe",
|
|
48
|
+
"/usr/bin/chimerax",
|
|
49
|
+
"/usr/local/bin/chimerax",
|
|
50
|
+
"/Applications/ChimeraX.app/Contents/MacOS/ChimeraX",
|
|
51
|
+
]
|
|
52
|
+
for c in candidates:
|
|
53
|
+
if os.path.exists(c):
|
|
54
|
+
return c
|
|
55
|
+
return None
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _check_structural_health() -> Tuple[AvailabilityStatus, str]:
|
|
59
|
+
pymol = _find_pymol()
|
|
60
|
+
chimerax = _find_chimerax()
|
|
61
|
+
if pymol or chimerax:
|
|
62
|
+
found = []
|
|
63
|
+
if pymol:
|
|
64
|
+
found.append(f"PyMOL: {pymol}")
|
|
65
|
+
if chimerax:
|
|
66
|
+
found.append(f"ChimeraX: {chimerax}")
|
|
67
|
+
return AvailabilityStatus.AVAILABLE, ", ".join(found)
|
|
68
|
+
return AvailabilityStatus.SOFTWARE_NOT_FOUND, "Neither PyMOL nor ChimeraX executable found on system"
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _pymol_open_handler(args: Dict[str, Any], context: Optional[Dict[str, Any]] = None) -> ExecutionResult:
|
|
72
|
+
structure_path = args.get("path", "")
|
|
73
|
+
if not structure_path:
|
|
74
|
+
return ExecutionResult(success=False, error="path argument (PDB/mmCIF) is required")
|
|
75
|
+
structure_path = os.path.abspath(os.path.expanduser(structure_path))
|
|
76
|
+
if not os.path.exists(structure_path):
|
|
77
|
+
return ExecutionResult(success=False, error=f"Structure file not found: {structure_path}")
|
|
78
|
+
|
|
79
|
+
exe = _find_pymol()
|
|
80
|
+
if not exe:
|
|
81
|
+
return ExecutionResult(
|
|
82
|
+
success=False,
|
|
83
|
+
error="PyMOL executable not found on host machine. Please install PyMOL.",
|
|
84
|
+
status=AvailabilityStatus.SOFTWARE_NOT_FOUND,
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
script = args.get("script", "")
|
|
88
|
+
cmd = [exe, structure_path]
|
|
89
|
+
if script:
|
|
90
|
+
cmd.extend(["-d", script])
|
|
91
|
+
|
|
92
|
+
try:
|
|
93
|
+
# Launch non-blocking
|
|
94
|
+
subprocess.Popen(cmd)
|
|
95
|
+
return ExecutionResult(
|
|
96
|
+
success=True,
|
|
97
|
+
output=f"Opened {structure_path} in PyMOL ({exe})",
|
|
98
|
+
metadata={"executable": exe, "structure": structure_path},
|
|
99
|
+
)
|
|
100
|
+
except Exception as e:
|
|
101
|
+
return ExecutionResult(success=False, error=f"Failed to launch PyMOL: {e}")
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _chimerax_open_handler(args: Dict[str, Any], context: Optional[Dict[str, Any]] = None) -> ExecutionResult:
|
|
105
|
+
structure_path = args.get("path", "")
|
|
106
|
+
if not structure_path:
|
|
107
|
+
return ExecutionResult(success=False, error="path argument (PDB/mmCIF) is required")
|
|
108
|
+
structure_path = os.path.abspath(os.path.expanduser(structure_path))
|
|
109
|
+
if not os.path.exists(structure_path):
|
|
110
|
+
return ExecutionResult(success=False, error=f"Structure file not found: {structure_path}")
|
|
111
|
+
|
|
112
|
+
exe = _find_chimerax()
|
|
113
|
+
if not exe:
|
|
114
|
+
return ExecutionResult(
|
|
115
|
+
success=False,
|
|
116
|
+
error="ChimeraX executable not found on host machine. Please install ChimeraX.",
|
|
117
|
+
status=AvailabilityStatus.SOFTWARE_NOT_FOUND,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
cmd = [exe, structure_path]
|
|
121
|
+
try:
|
|
122
|
+
subprocess.Popen(cmd)
|
|
123
|
+
return ExecutionResult(
|
|
124
|
+
success=True,
|
|
125
|
+
output=f"Opened {structure_path} in ChimeraX ({exe})",
|
|
126
|
+
metadata={"executable": exe, "structure": structure_path},
|
|
127
|
+
)
|
|
128
|
+
except Exception as e:
|
|
129
|
+
return ExecutionResult(success=False, error=f"Failed to launch ChimeraX: {e}")
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def register_structural_bio_capabilities(bus):
|
|
133
|
+
bus.register(Capability(
|
|
134
|
+
spec=CapabilitySpec(
|
|
135
|
+
name="pymol.open",
|
|
136
|
+
version="1.0.0",
|
|
137
|
+
category=CapabilityCategory.BIO,
|
|
138
|
+
description="Open protein/molecular structure in PyMOL for 3D visualization.",
|
|
139
|
+
input_schema={"path": "string", "script": "string?"},
|
|
140
|
+
output_schema={"status": "string"},
|
|
141
|
+
permissions=["execute_terminal"],
|
|
142
|
+
documentation="Launches PyMOL with the target structure.",
|
|
143
|
+
),
|
|
144
|
+
handler=_pymol_open_handler,
|
|
145
|
+
health_checker=_check_structural_health,
|
|
146
|
+
))
|
|
147
|
+
|
|
148
|
+
bus.register(Capability(
|
|
149
|
+
spec=CapabilitySpec(
|
|
150
|
+
name="chimerax.open",
|
|
151
|
+
version="1.0.0",
|
|
152
|
+
category=CapabilityCategory.BIO,
|
|
153
|
+
description="Open biomolecular structure in UCSF ChimeraX for visualization and analysis.",
|
|
154
|
+
input_schema={"path": "string"},
|
|
155
|
+
output_schema={"status": "string"},
|
|
156
|
+
permissions=["execute_terminal"],
|
|
157
|
+
documentation="Launches ChimeraX with the target structure.",
|
|
158
|
+
),
|
|
159
|
+
handler=_chimerax_open_handler,
|
|
160
|
+
health_checker=_check_structural_health,
|
|
161
|
+
))
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Terminal / Process Execution Capability Adapter:
|
|
3
|
+
- terminal.execute
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import os
|
|
9
|
+
import subprocess
|
|
10
|
+
import time
|
|
11
|
+
from typing import Any, Dict, Optional, Tuple
|
|
12
|
+
|
|
13
|
+
from ..schema import (
|
|
14
|
+
AvailabilityStatus,
|
|
15
|
+
Capability,
|
|
16
|
+
CapabilityCategory,
|
|
17
|
+
CapabilitySpec,
|
|
18
|
+
ExecutionResult,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _check_terminal_health() -> Tuple[AvailabilityStatus, str]:
|
|
23
|
+
return AvailabilityStatus.AVAILABLE, "System shell execution available"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _execute_handler(args: Dict[str, Any], context: Optional[Dict[str, Any]] = None) -> ExecutionResult:
|
|
27
|
+
command = args.get("command", "")
|
|
28
|
+
if not command:
|
|
29
|
+
return ExecutionResult(success=False, error="command argument is required")
|
|
30
|
+
|
|
31
|
+
cwd = args.get("cwd", os.getcwd())
|
|
32
|
+
timeout = float(args.get("timeout", 60.0))
|
|
33
|
+
env_overrides = args.get("env", {})
|
|
34
|
+
|
|
35
|
+
env = os.environ.copy()
|
|
36
|
+
if isinstance(env_overrides, dict):
|
|
37
|
+
env.update(env_overrides)
|
|
38
|
+
|
|
39
|
+
t0 = time.perf_counter()
|
|
40
|
+
try:
|
|
41
|
+
proc = subprocess.run(
|
|
42
|
+
command,
|
|
43
|
+
cwd=cwd,
|
|
44
|
+
env=env,
|
|
45
|
+
shell=True,
|
|
46
|
+
stdout=subprocess.PIPE,
|
|
47
|
+
stderr=subprocess.PIPE,
|
|
48
|
+
text=True,
|
|
49
|
+
timeout=timeout,
|
|
50
|
+
)
|
|
51
|
+
duration = round((time.perf_counter() - t0) * 1000.0, 2)
|
|
52
|
+
success = (proc.returncode == 0)
|
|
53
|
+
|
|
54
|
+
return ExecutionResult(
|
|
55
|
+
success=success,
|
|
56
|
+
output=proc.stdout,
|
|
57
|
+
error=proc.stderr if not success else None,
|
|
58
|
+
duration_ms=duration,
|
|
59
|
+
metadata={
|
|
60
|
+
"exit_code": proc.returncode,
|
|
61
|
+
"command": command,
|
|
62
|
+
"cwd": cwd,
|
|
63
|
+
"stdout": proc.stdout,
|
|
64
|
+
"stderr": proc.stderr,
|
|
65
|
+
},
|
|
66
|
+
)
|
|
67
|
+
except subprocess.TimeoutExpired as e:
|
|
68
|
+
duration = round((time.perf_counter() - t0) * 1000.0, 2)
|
|
69
|
+
return ExecutionResult(
|
|
70
|
+
success=False,
|
|
71
|
+
error=f"Command timed out after {timeout} seconds",
|
|
72
|
+
duration_ms=duration,
|
|
73
|
+
metadata={"exit_code": -1, "command": command, "timeout": timeout},
|
|
74
|
+
)
|
|
75
|
+
except Exception as e:
|
|
76
|
+
duration = round((time.perf_counter() - t0) * 1000.0, 2)
|
|
77
|
+
return ExecutionResult(
|
|
78
|
+
success=False,
|
|
79
|
+
error=str(e),
|
|
80
|
+
duration_ms=duration,
|
|
81
|
+
metadata={"exit_code": -1, "command": command},
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def register_terminal_capabilities(bus):
|
|
86
|
+
bus.register(Capability(
|
|
87
|
+
spec=CapabilitySpec(
|
|
88
|
+
name="terminal.execute",
|
|
89
|
+
version="1.0.0",
|
|
90
|
+
category=CapabilityCategory.TERMINAL,
|
|
91
|
+
description="Execute a shell command with stdout, stderr and exit code capture.",
|
|
92
|
+
input_schema={"command": "string", "cwd": "string?", "timeout": "number?", "env": "object?"},
|
|
93
|
+
output_schema={"output": "string", "exit_code": "integer"},
|
|
94
|
+
permissions=["execute_terminal"],
|
|
95
|
+
documentation="Executes a command line string in the host shell.",
|
|
96
|
+
),
|
|
97
|
+
handler=_execute_handler,
|
|
98
|
+
health_checker=_check_terminal_health,
|
|
99
|
+
))
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CAT Capability Bus — Central Architecture per §4, §56, §57.
|
|
3
|
+
Modes request capabilities through this bus; tools and integrations are never
|
|
4
|
+
owned privately by individual modes.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import logging
|
|
10
|
+
import threading
|
|
11
|
+
import time
|
|
12
|
+
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
|
|
13
|
+
|
|
14
|
+
from .schema import (
|
|
15
|
+
AvailabilityStatus,
|
|
16
|
+
Capability,
|
|
17
|
+
CapabilityCategory,
|
|
18
|
+
CapabilitySpec,
|
|
19
|
+
ExecutionError,
|
|
20
|
+
ExecutionResult,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
_LOG = logging.getLogger("cct.capabilities")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class CapabilityBus:
|
|
27
|
+
"""Central registry and execution dispatch for all CAT capabilities."""
|
|
28
|
+
|
|
29
|
+
_instance: Optional[CapabilityBus] = None
|
|
30
|
+
_lock = threading.Lock()
|
|
31
|
+
|
|
32
|
+
def __new__(cls) -> CapabilityBus:
|
|
33
|
+
with cls._lock:
|
|
34
|
+
if cls._instance is None:
|
|
35
|
+
cls._instance = super(CapabilityBus, cls).__new__(cls)
|
|
36
|
+
cls._instance._capabilities: Dict[str, Capability] = {}
|
|
37
|
+
cls._instance._initialized = False
|
|
38
|
+
return cls._instance
|
|
39
|
+
|
|
40
|
+
def register(self, capability: Capability) -> None:
|
|
41
|
+
"""Register a new capability in the bus."""
|
|
42
|
+
name = capability.name.strip().lower()
|
|
43
|
+
self._capabilities[name] = capability
|
|
44
|
+
_LOG.debug("Registered capability: %s (version %s)", name, capability.spec.version)
|
|
45
|
+
|
|
46
|
+
def get(self, name: str) -> Optional[Capability]:
|
|
47
|
+
"""Look up a capability by canonical name."""
|
|
48
|
+
canonical = (name or "").strip().lower()
|
|
49
|
+
return self._capabilities.get(canonical)
|
|
50
|
+
|
|
51
|
+
def get_capability(self, name: str) -> Optional[Capability]:
|
|
52
|
+
"""Look up a capability by canonical name."""
|
|
53
|
+
return self.get(name)
|
|
54
|
+
|
|
55
|
+
def has(self, name: str) -> bool:
|
|
56
|
+
"""Check whether a capability is registered."""
|
|
57
|
+
return name.strip().lower() in self._capabilities
|
|
58
|
+
|
|
59
|
+
def list_capabilities(
|
|
60
|
+
self,
|
|
61
|
+
category: Optional[Union[CapabilityCategory, str]] = None,
|
|
62
|
+
status: Optional[AvailabilityStatus] = None,
|
|
63
|
+
) -> List[Capability]:
|
|
64
|
+
"""Return registered capabilities filtered by category or status."""
|
|
65
|
+
caps = list(self._capabilities.values())
|
|
66
|
+
if category:
|
|
67
|
+
cat_val = category.value if isinstance(category, CapabilityCategory) else str(category).lower()
|
|
68
|
+
caps = [c for c in caps if c.spec.category.value.lower() == cat_val]
|
|
69
|
+
if status:
|
|
70
|
+
caps = [c for c in caps if c.status == status]
|
|
71
|
+
return sorted(caps, key=lambda c: c.name)
|
|
72
|
+
|
|
73
|
+
def execute(
|
|
74
|
+
self,
|
|
75
|
+
name: str,
|
|
76
|
+
args: Optional[Dict[str, Any]] = None,
|
|
77
|
+
context: Optional[Dict[str, Any]] = None,
|
|
78
|
+
task_id: str = "",
|
|
79
|
+
mode: str = "",
|
|
80
|
+
permission_callback: Optional[Callable[[str, str, str, str], str]] = None,
|
|
81
|
+
) -> ExecutionResult:
|
|
82
|
+
"""Execute a capability with permission checking and event emission."""
|
|
83
|
+
canonical = (name or "").strip().lower()
|
|
84
|
+
cap = self.get(canonical)
|
|
85
|
+
if not cap:
|
|
86
|
+
return ExecutionResult(
|
|
87
|
+
success=False,
|
|
88
|
+
error=f"Capability '{canonical}' is not registered in the Capability Bus.",
|
|
89
|
+
status=AvailabilityStatus.UNAVAILABLE,
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
args = args or {}
|
|
93
|
+
context = context or {}
|
|
94
|
+
|
|
95
|
+
# 1. Permission check
|
|
96
|
+
if cap.spec.permissions:
|
|
97
|
+
try:
|
|
98
|
+
from .. import permissions as perm
|
|
99
|
+
for req_perm in cap.spec.permissions:
|
|
100
|
+
if perm.manager.refused_by_always_deny(req_perm):
|
|
101
|
+
return ExecutionResult(
|
|
102
|
+
success=False,
|
|
103
|
+
error=f"Permission denied: {req_perm} is permanently denied for this session.",
|
|
104
|
+
status=AvailabilityStatus.PERMISSION_DENIED,
|
|
105
|
+
)
|
|
106
|
+
if perm.manager.needs_prompt(req_perm):
|
|
107
|
+
decision = "allow_once"
|
|
108
|
+
if permission_callback:
|
|
109
|
+
decision = permission_callback(
|
|
110
|
+
req_perm,
|
|
111
|
+
f"Execute {canonical}",
|
|
112
|
+
str(args.get("path", "") or args.get("target", "")),
|
|
113
|
+
f"Required by capability {canonical}",
|
|
114
|
+
)
|
|
115
|
+
if not perm.manager.decide(req_perm, decision, canonical, "Capability execution"):
|
|
116
|
+
return ExecutionResult(
|
|
117
|
+
success=False,
|
|
118
|
+
error=f"Permission rejected by user for capability '{canonical}'.",
|
|
119
|
+
status=AvailabilityStatus.PERMISSION_DENIED,
|
|
120
|
+
)
|
|
121
|
+
except Exception as e:
|
|
122
|
+
_LOG.warning("Permission validation check failed: %s", e)
|
|
123
|
+
|
|
124
|
+
# 2. Emit ToolStarted event
|
|
125
|
+
try:
|
|
126
|
+
from ..workflow_engine import workflow_engine, EVENT_TOOL_STARTED, ExecutionEvent, STATE_RUNNING
|
|
127
|
+
workflow_engine.record_tool_start(
|
|
128
|
+
tool=canonical,
|
|
129
|
+
args=args,
|
|
130
|
+
turn_id=task_id or context.get("turn_id", ""),
|
|
131
|
+
)
|
|
132
|
+
except Exception:
|
|
133
|
+
pass
|
|
134
|
+
|
|
135
|
+
# 3. Execution
|
|
136
|
+
result = cap.execute(args, context)
|
|
137
|
+
|
|
138
|
+
# 4. Attach Provenance
|
|
139
|
+
result.provenance = {
|
|
140
|
+
"capability": canonical,
|
|
141
|
+
"version": cap.spec.version,
|
|
142
|
+
"task_id": task_id,
|
|
143
|
+
"mode": mode,
|
|
144
|
+
"timestamp": time.time(),
|
|
145
|
+
"duration_ms": result.duration_ms,
|
|
146
|
+
"success": result.success,
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
# 5. Emit ToolFinished event
|
|
150
|
+
try:
|
|
151
|
+
from ..workflow_engine import workflow_engine
|
|
152
|
+
workflow_engine.record_tool_completion(
|
|
153
|
+
tool=canonical,
|
|
154
|
+
result=str(result.output if result.success else result.error)[:200],
|
|
155
|
+
turn_id=task_id or context.get("turn_id", ""),
|
|
156
|
+
duration_ms=int(result.duration_ms),
|
|
157
|
+
)
|
|
158
|
+
except Exception:
|
|
159
|
+
pass
|
|
160
|
+
|
|
161
|
+
return result
|
|
162
|
+
|
|
163
|
+
def health_check_all(self, force: bool = False) -> Dict[str, Dict[str, Any]]:
|
|
164
|
+
"""Run health check across all registered capabilities."""
|
|
165
|
+
report = {}
|
|
166
|
+
for name, cap in self._capabilities.items():
|
|
167
|
+
status, message = cap.check_health(force=force)
|
|
168
|
+
report[name] = {
|
|
169
|
+
"category": cap.spec.category.value,
|
|
170
|
+
"status": status.value,
|
|
171
|
+
"message": message,
|
|
172
|
+
"version": cap.spec.version,
|
|
173
|
+
"auth_required": cap.spec.auth_required,
|
|
174
|
+
}
|
|
175
|
+
return report
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
capability_bus = CapabilityBus()
|