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,710 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CatRuntime — Unified CAT Core Adapter Layer for Fatty CAT.
|
|
3
|
+
|
|
4
|
+
Single source of truth:
|
|
5
|
+
- Wraps existing CAT core modules (aicore, agent, ai_modes, providers, models,
|
|
6
|
+
permissions, memory, customization, extensions, preview, etc.)
|
|
7
|
+
- Provides a clean, typed internal API consumed by Fatty CAT's web server and UI
|
|
8
|
+
- NEVER rewrites or duplicates business logic — exposes CAT core dynamically.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import asyncio
|
|
14
|
+
import os
|
|
15
|
+
import sys
|
|
16
|
+
import time
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
from typing import Any, AsyncGenerator, Dict, List, Optional, Tuple
|
|
19
|
+
|
|
20
|
+
# Ensure package is on sys.path (source-checkout only; pip-installed CAT
|
|
21
|
+
# already has `calc_terminal` importable).
|
|
22
|
+
try:
|
|
23
|
+
import calc_terminal as _ct_check # noqa: F401
|
|
24
|
+
except Exception:
|
|
25
|
+
_CAT_ROOT = Path(__file__).parent.parent
|
|
26
|
+
if str(_CAT_ROOT) not in sys.path:
|
|
27
|
+
sys.path.insert(0, str(_CAT_ROOT))
|
|
28
|
+
|
|
29
|
+
# CAT Core Imports
|
|
30
|
+
from calc_terminal import aicore, ai_modes, commands_data, config as cct_config, identity
|
|
31
|
+
from calc_terminal import memory, permissions as cct_perms
|
|
32
|
+
from calc_terminal.models import manager as model_manager
|
|
33
|
+
from calc_terminal.providers import provider_manager
|
|
34
|
+
from calc_terminal import ollama_catalog, ollama_download
|
|
35
|
+
from calc_terminal import customization, extensions as cct_ext
|
|
36
|
+
from calc_terminal import workspace as ws_module, projects as proj_module
|
|
37
|
+
from calc_terminal.viewers import registry as viewer_registry
|
|
38
|
+
from calc_terminal import eventbus, event_stream
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class CatRuntime:
|
|
42
|
+
"""Canonical adapter around CAT Core functionality."""
|
|
43
|
+
|
|
44
|
+
def __init__(self):
|
|
45
|
+
self.start_time = time.time()
|
|
46
|
+
self.request_count = 0
|
|
47
|
+
self.error_count = 0
|
|
48
|
+
self.recent_events: List[Dict[str, Any]] = []
|
|
49
|
+
self._wire_events()
|
|
50
|
+
|
|
51
|
+
def _wire_events(self):
|
|
52
|
+
"""Wire into CAT eventbus to track recent activity."""
|
|
53
|
+
def _on_bus_event(topic: str, **kwargs):
|
|
54
|
+
entry = {
|
|
55
|
+
"topic": topic,
|
|
56
|
+
"timestamp": time.time(),
|
|
57
|
+
"data": {k: str(v)[:150] for k, v in kwargs.items()}
|
|
58
|
+
}
|
|
59
|
+
self.recent_events.append(entry)
|
|
60
|
+
if len(self.recent_events) > 100:
|
|
61
|
+
self.recent_events.pop(0)
|
|
62
|
+
|
|
63
|
+
for topic in (
|
|
64
|
+
eventbus.FILE_CREATED, eventbus.FILE_SAVED, eventbus.FILE_DELETED,
|
|
65
|
+
eventbus.AI_RESPONSE_GENERATED, eventbus.TERMINAL_COMMAND,
|
|
66
|
+
eventbus.WORKSPACE_OPENED, eventbus.MODE_SWITCHED,
|
|
67
|
+
eventbus.PACKAGE_INSTALLED, eventbus.TODO_CREATED, eventbus.TODO_COMPLETED
|
|
68
|
+
):
|
|
69
|
+
eventbus.bus.subscribe(topic, lambda t=topic, **kw: _on_bus_event(t, **kw))
|
|
70
|
+
|
|
71
|
+
# ─────────────────────────────────────────────────────────────
|
|
72
|
+
# 1. Capability Discovery & Version Contract
|
|
73
|
+
# ─────────────────────────────────────────────────────────────
|
|
74
|
+
|
|
75
|
+
def get_capabilities(self) -> Dict[str, Any]:
|
|
76
|
+
"""Return the dynamic CAT capability registry and version contract."""
|
|
77
|
+
cfg = aicore.load_config()
|
|
78
|
+
v_reg = viewer_registry.ViewerRegistry()
|
|
79
|
+
|
|
80
|
+
# Tools summary
|
|
81
|
+
tools_list = [
|
|
82
|
+
{"id": "list_formulas", "name": "List Formulas", "category": "math"},
|
|
83
|
+
{"id": "solve_formula", "name": "Solve Formula", "category": "math"},
|
|
84
|
+
{"id": "solve_custom", "name": "Solve Custom", "category": "math"},
|
|
85
|
+
{"id": "calculate", "name": "Safe Calculator", "category": "math"},
|
|
86
|
+
{"id": "generate_numerical", "name": "Generate Numerical", "category": "science"},
|
|
87
|
+
{"id": "plot_preset", "name": "Plot Preset", "category": "graph"},
|
|
88
|
+
{"id": "plot_function", "name": "Plot Function 2D", "category": "graph"},
|
|
89
|
+
{"id": "plot_surface", "name": "Plot Surface 3D", "category": "graph"},
|
|
90
|
+
{"id": "atom_2d", "name": "2D Atom Simulation", "category": "simulation"},
|
|
91
|
+
{"id": "atom_3d", "name": "3D Atom Simulation", "category": "simulation"},
|
|
92
|
+
{"id": "orbital", "name": "Quantum Orbital", "category": "simulation"},
|
|
93
|
+
{"id": "orbital_grid", "name": "Orbital Grid Chart", "category": "simulation"},
|
|
94
|
+
{"id": "bloch", "name": "Bloch Sphere Qubit", "category": "quantum"},
|
|
95
|
+
{"id": "bonding", "name": "Chemical Bonding Map", "category": "science"},
|
|
96
|
+
{"id": "web_search", "name": "Live Web Search", "category": "research"},
|
|
97
|
+
{"id": "deep_research", "name": "Deep Research Synthesis", "category": "research"},
|
|
98
|
+
{"id": "read_attachment", "name": "Read Attachment", "category": "file"},
|
|
99
|
+
{"id": "read_file", "name": "Read File", "category": "file"},
|
|
100
|
+
{"id": "write_file", "name": "Write File", "category": "file"},
|
|
101
|
+
{"id": "create_folder", "name": "Create Folder", "category": "file"},
|
|
102
|
+
{"id": "delete_file", "name": "Delete File", "category": "file"},
|
|
103
|
+
{"id": "rename_file", "name": "Rename File", "category": "file"},
|
|
104
|
+
{"id": "edit_file", "name": "Edit File", "category": "file"},
|
|
105
|
+
{"id": "search_workspace", "name": "Search Workspace", "category": "file"},
|
|
106
|
+
{"id": "list_directory", "name": "List Directory", "category": "file"},
|
|
107
|
+
{"id": "inspect_project", "name": "Inspect Project Structure", "category": "project"},
|
|
108
|
+
{"id": "archive_list", "name": "List Archive Contents", "category": "archive"},
|
|
109
|
+
{"id": "archive_extract", "name": "Extract Archive", "category": "archive"},
|
|
110
|
+
{"id": "archive_add_entries", "name": "Add to Archive", "category": "archive"},
|
|
111
|
+
{"id": "archive_delete_entries", "name": "Delete from Archive", "category": "archive"},
|
|
112
|
+
{"id": "archive_repack", "name": "Repack Archive", "category": "archive"},
|
|
113
|
+
{"id": "archive_validate", "name": "Validate Archive", "category": "archive"},
|
|
114
|
+
{"id": "run_terminal", "name": "Run Terminal Command", "category": "execution"},
|
|
115
|
+
{"id": "run_build", "name": "Run Project Build", "category": "execution"},
|
|
116
|
+
{"id": "run_tests", "name": "Run Test Suite", "category": "execution"},
|
|
117
|
+
{"id": "install_packages", "name": "Install Packages", "category": "execution"},
|
|
118
|
+
{"id": "device_action", "name": "Device Control Action", "category": "system"},
|
|
119
|
+
]
|
|
120
|
+
|
|
121
|
+
# All 103 commands
|
|
122
|
+
cmds = [{"command": c[0], "description": c[1]} for c in commands_data.COMMANDS]
|
|
123
|
+
|
|
124
|
+
# Modes
|
|
125
|
+
modes_dict = {}
|
|
126
|
+
for k in ai_modes.MODE_ORDER:
|
|
127
|
+
m = ai_modes.MODE_META.get(k, {})
|
|
128
|
+
modes_dict[k] = {
|
|
129
|
+
"key": k,
|
|
130
|
+
"label": m.get("label", k.title()),
|
|
131
|
+
"icon": m.get("icon", "●"),
|
|
132
|
+
"accent": ai_modes._rgb_to_hex(m.get("accent", (122, 162, 247))),
|
|
133
|
+
"gradient": [
|
|
134
|
+
ai_modes._rgb_to_hex(m["gradient"][0]),
|
|
135
|
+
ai_modes._rgb_to_hex(m["gradient"][1])
|
|
136
|
+
] if m.get("gradient") else ["#5d84f0", "#91beff"],
|
|
137
|
+
"purpose": m.get("purpose", ""),
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
# Extensions
|
|
141
|
+
ext_list = []
|
|
142
|
+
for ext in cct_ext.BUILTIN_EXTENSIONS.values():
|
|
143
|
+
eid = ext.get("id", "")
|
|
144
|
+
ext_list.append({
|
|
145
|
+
"id": eid,
|
|
146
|
+
"name": ext.get("name", eid),
|
|
147
|
+
"installed": cct_ext.is_installed(eid),
|
|
148
|
+
"enabled": cct_ext.is_enabled(eid),
|
|
149
|
+
"icon": ext.get("icon", "🧩"),
|
|
150
|
+
"description": ext.get("description", ""),
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
return {
|
|
154
|
+
"version_contract": {
|
|
155
|
+
"cat_version": identity.APP_VERSION,
|
|
156
|
+
"api_version": "1.0.0",
|
|
157
|
+
"fatty_cat_version": "1.0.0",
|
|
158
|
+
},
|
|
159
|
+
"active_mode": ai_modes.current_mode(),
|
|
160
|
+
"modes": modes_dict,
|
|
161
|
+
"mode_order": list(ai_modes.MODE_ORDER),
|
|
162
|
+
"active_provider": cfg.get("provider", "openai"),
|
|
163
|
+
"active_model": cfg.get("model", "gpt-4o"),
|
|
164
|
+
"has_api_key": bool(cfg.get("api_key")),
|
|
165
|
+
"providers_count": len(model_manager.load_providers()),
|
|
166
|
+
"commands": cmds,
|
|
167
|
+
"native_ui_commands": list(commands_data.NATIVE_UI_COMMANDS),
|
|
168
|
+
"tools": tools_list,
|
|
169
|
+
"viewers": list(v_reg._providers.keys()),
|
|
170
|
+
"extensions": ext_list,
|
|
171
|
+
"features": [
|
|
172
|
+
"chat", "agent", "build", "plan", "research", "debugger", "notebook",
|
|
173
|
+
"files", "code_editor", "terminal", "browser", "live_preview",
|
|
174
|
+
"models", "providers", "ollama", "permissions", "memory",
|
|
175
|
+
"customization", "extensions", "vision", "diagnostics", "fomoji_auth"
|
|
176
|
+
],
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
# ─────────────────────────────────────────────────────────────
|
|
180
|
+
# 2. AI Execution & Streaming
|
|
181
|
+
# ─────────────────────────────────────────────────────────────
|
|
182
|
+
|
|
183
|
+
async def stream_chat(
|
|
184
|
+
self,
|
|
185
|
+
message: str,
|
|
186
|
+
mode: str = "notebook",
|
|
187
|
+
history: Optional[List[Dict[str, str]]] = None,
|
|
188
|
+
project_path: Optional[str] = None
|
|
189
|
+
) -> AsyncGenerator[Dict[str, Any], None]:
|
|
190
|
+
"""Stream chat tokens directly from aicore.stream_ai()."""
|
|
191
|
+
self.request_count += 1
|
|
192
|
+
if project_path:
|
|
193
|
+
ws_module.set_active_project(project_path)
|
|
194
|
+
|
|
195
|
+
cfg = aicore.load_config()
|
|
196
|
+
system_prompt = ai_modes.system_prompt_for(mode)
|
|
197
|
+
|
|
198
|
+
loop = asyncio.get_event_loop()
|
|
199
|
+
queue: asyncio.Queue[Tuple[str, Any]] = asyncio.Queue()
|
|
200
|
+
|
|
201
|
+
def _worker():
|
|
202
|
+
try:
|
|
203
|
+
for chunk in aicore.stream_ai(
|
|
204
|
+
prompt=message,
|
|
205
|
+
system_prompt=system_prompt,
|
|
206
|
+
history=history,
|
|
207
|
+
config=cfg,
|
|
208
|
+
on_failover=lambda msg: loop.call_soon_threadsafe(
|
|
209
|
+
queue.put_nowait, ("failover", msg)
|
|
210
|
+
),
|
|
211
|
+
size_class="normal"
|
|
212
|
+
):
|
|
213
|
+
loop.call_soon_threadsafe(queue.put_nowait, ("token", chunk))
|
|
214
|
+
loop.call_soon_threadsafe(queue.put_nowait, ("done", None))
|
|
215
|
+
except Exception as e:
|
|
216
|
+
self.error_count += 1
|
|
217
|
+
loop.call_soon_threadsafe(queue.put_nowait, ("error", str(e)))
|
|
218
|
+
|
|
219
|
+
thread = asyncio.to_thread(_worker)
|
|
220
|
+
task = asyncio.create_task(thread)
|
|
221
|
+
|
|
222
|
+
try:
|
|
223
|
+
while True:
|
|
224
|
+
kind, data = await queue.get()
|
|
225
|
+
if kind == "token":
|
|
226
|
+
yield {"type": "token", "text": data}
|
|
227
|
+
elif kind == "failover":
|
|
228
|
+
yield {"type": "thinking", "text": f"Failover: {data}"}
|
|
229
|
+
elif kind == "error":
|
|
230
|
+
yield {"type": "error", "error": data}
|
|
231
|
+
break
|
|
232
|
+
elif kind == "done":
|
|
233
|
+
yield {"type": "done"}
|
|
234
|
+
break
|
|
235
|
+
finally:
|
|
236
|
+
await task
|
|
237
|
+
|
|
238
|
+
def run_agent_turn(
|
|
239
|
+
self,
|
|
240
|
+
message: str,
|
|
241
|
+
mode: str = "agent",
|
|
242
|
+
max_steps: int = 15,
|
|
243
|
+
project_path: Optional[str] = None
|
|
244
|
+
) -> Dict[str, Any]:
|
|
245
|
+
"""Run a full step-by-step agent loop using agent.run_agent()."""
|
|
246
|
+
self.request_count += 1
|
|
247
|
+
if project_path:
|
|
248
|
+
ws_module.set_active_project(project_path)
|
|
249
|
+
|
|
250
|
+
from calc_terminal.agent import run_agent as _cct_run_agent
|
|
251
|
+
|
|
252
|
+
try:
|
|
253
|
+
agent_mode = "agent" if mode in ("agent", "build", "plan") else "ai"
|
|
254
|
+
text, steps, meta = _cct_run_agent(
|
|
255
|
+
message,
|
|
256
|
+
max_steps=max_steps,
|
|
257
|
+
verbose=False,
|
|
258
|
+
mode=agent_mode
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
formatted_steps = []
|
|
262
|
+
for s in steps:
|
|
263
|
+
if len(s) >= 3:
|
|
264
|
+
formatted_steps.append({
|
|
265
|
+
"tool": s[0],
|
|
266
|
+
"args": s[1] if isinstance(s[1], dict) else {},
|
|
267
|
+
"result": str(s[2])[:800],
|
|
268
|
+
})
|
|
269
|
+
|
|
270
|
+
return {
|
|
271
|
+
"success": True,
|
|
272
|
+
"response": text or "",
|
|
273
|
+
"steps": formatted_steps,
|
|
274
|
+
"meta": meta or {},
|
|
275
|
+
}
|
|
276
|
+
except Exception as e:
|
|
277
|
+
self.error_count += 1
|
|
278
|
+
return {
|
|
279
|
+
"success": False,
|
|
280
|
+
"response": f"Agent execution error: {e}",
|
|
281
|
+
"steps": [],
|
|
282
|
+
"meta": {},
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
# ─────────────────────────────────────────────────────────────
|
|
286
|
+
# 3. AI Modes Management
|
|
287
|
+
# ─────────────────────────────────────────────────────────────
|
|
288
|
+
|
|
289
|
+
def get_modes(self) -> Dict[str, Any]:
|
|
290
|
+
"""Get snapshot of current modes and active mode."""
|
|
291
|
+
return {
|
|
292
|
+
"current": ai_modes.current_mode(),
|
|
293
|
+
"order": list(ai_modes.MODE_ORDER),
|
|
294
|
+
"modes": {
|
|
295
|
+
k: {
|
|
296
|
+
"key": k,
|
|
297
|
+
"label": v.get("label", k.title()),
|
|
298
|
+
"icon": v.get("icon", "●"),
|
|
299
|
+
"accent": ai_modes._rgb_to_hex(v.get("accent", (122, 162, 247))),
|
|
300
|
+
"gradient": [
|
|
301
|
+
ai_modes._rgb_to_hex(v["gradient"][0]),
|
|
302
|
+
ai_modes._rgb_to_hex(v["gradient"][1])
|
|
303
|
+
] if v.get("gradient") else ["#5d84f0", "#91beff"],
|
|
304
|
+
"purpose": v.get("purpose", ""),
|
|
305
|
+
"system_prompt": v.get("system_prompt", ""),
|
|
306
|
+
}
|
|
307
|
+
for k, v in ai_modes.MODE_META.items()
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
def set_mode(self, mode_key: str) -> str:
|
|
312
|
+
"""Switch active AI mode."""
|
|
313
|
+
res = ai_modes.set_mode(mode_key)
|
|
314
|
+
eventbus.bus.publish(eventbus.MODE_SWITCHED, mode=res)
|
|
315
|
+
return res
|
|
316
|
+
|
|
317
|
+
def create_custom_mode(
|
|
318
|
+
self,
|
|
319
|
+
key: str,
|
|
320
|
+
label: str,
|
|
321
|
+
icon: str,
|
|
322
|
+
accent_hex: str,
|
|
323
|
+
purpose: str = "",
|
|
324
|
+
system_prompt: str = ""
|
|
325
|
+
) -> Tuple[bool, str]:
|
|
326
|
+
"""Create user-defined mode via ai_modes.create_mode()."""
|
|
327
|
+
return ai_modes.create_mode(key, label, icon, accent_hex, purpose, system_prompt)
|
|
328
|
+
|
|
329
|
+
def delete_custom_mode(self, key: str) -> Tuple[bool, str]:
|
|
330
|
+
"""Delete custom mode."""
|
|
331
|
+
return ai_modes.delete_mode(key)
|
|
332
|
+
|
|
333
|
+
# ─────────────────────────────────────────────────────────────
|
|
334
|
+
# 4. Providers & Models Management
|
|
335
|
+
# ─────────────────────────────────────────────────────────────
|
|
336
|
+
|
|
337
|
+
def list_providers(self) -> List[Dict[str, Any]]:
|
|
338
|
+
"""List all providers from providers.json."""
|
|
339
|
+
all_provs = model_manager.load_providers()
|
|
340
|
+
cfg = aicore.load_config()
|
|
341
|
+
active_id = cfg.get("provider", "openai")
|
|
342
|
+
|
|
343
|
+
out = []
|
|
344
|
+
for p in all_provs:
|
|
345
|
+
item = dict(p)
|
|
346
|
+
item["is_active"] = (p.get("id") == active_id)
|
|
347
|
+
out.append(item)
|
|
348
|
+
return out
|
|
349
|
+
|
|
350
|
+
def get_models_for_provider(self, provider_id: str, force_refresh: bool = False) -> List[Dict[str, Any]]:
|
|
351
|
+
"""Get models for provider using CAT model manager with caching."""
|
|
352
|
+
cfg = aicore.load_config()
|
|
353
|
+
models = model_manager.get_models(provider_id, config=cfg, force_refresh=force_refresh)
|
|
354
|
+
active_model = cfg.get("model", "")
|
|
355
|
+
|
|
356
|
+
out = []
|
|
357
|
+
for m_id in models:
|
|
358
|
+
meta = model_manager.get_model_metadata(m_id) or {}
|
|
359
|
+
out.append({
|
|
360
|
+
"id": m_id,
|
|
361
|
+
"name": meta.get("name", m_id),
|
|
362
|
+
"is_active": (m_id == active_model),
|
|
363
|
+
"family": meta.get("family", ""),
|
|
364
|
+
"description": meta.get("desc", meta.get("description", "")),
|
|
365
|
+
"context_window": meta.get("context", meta.get("context_window", 4096)),
|
|
366
|
+
"capabilities": meta.get("caps", meta.get("capabilities", [])),
|
|
367
|
+
"free": meta.get("free", False),
|
|
368
|
+
})
|
|
369
|
+
return out
|
|
370
|
+
|
|
371
|
+
def switch_model(self, provider_id: str, model_id: str, api_key: Optional[str] = None) -> Dict[str, Any]:
|
|
372
|
+
"""Switch active provider and model in CAT config."""
|
|
373
|
+
cfg = aicore.load_config()
|
|
374
|
+
cfg["provider"] = provider_id
|
|
375
|
+
cfg["model"] = model_id
|
|
376
|
+
if api_key is not None:
|
|
377
|
+
cfg["api_key"] = api_key
|
|
378
|
+
aicore.save_config(cfg)
|
|
379
|
+
provider_manager.save_config(cfg)
|
|
380
|
+
return {"success": True, "provider": provider_id, "model": model_id}
|
|
381
|
+
|
|
382
|
+
# ─────────────────────────────────────────────────────────────
|
|
383
|
+
# 5. Ollama Support
|
|
384
|
+
# ─────────────────────────────────────────────────────────────
|
|
385
|
+
|
|
386
|
+
def get_ollama_status(self) -> Dict[str, Any]:
|
|
387
|
+
"""Check Ollama installation and connection status."""
|
|
388
|
+
base_url = ollama_download.ollama_base_url()
|
|
389
|
+
installed = ollama_download.is_ollama_installed()
|
|
390
|
+
running = ollama_download.is_ollama_running(base_url=base_url)
|
|
391
|
+
installed_models = []
|
|
392
|
+
if running:
|
|
393
|
+
try:
|
|
394
|
+
installed_models = provider_manager.refresh_ollama_models(base_url=base_url)
|
|
395
|
+
except Exception:
|
|
396
|
+
pass
|
|
397
|
+
return {
|
|
398
|
+
"installed": installed,
|
|
399
|
+
"running": running,
|
|
400
|
+
"base_url": base_url,
|
|
401
|
+
"installed_models": installed_models,
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
def get_ollama_catalog(self, query: str = "") -> List[Dict[str, Any]]:
|
|
405
|
+
"""Return curated Ollama models from ollama_catalog.py."""
|
|
406
|
+
if query:
|
|
407
|
+
return ollama_catalog.search_models(query)
|
|
408
|
+
return ollama_catalog.all_models()
|
|
409
|
+
|
|
410
|
+
def ensure_ollama_started(self) -> Tuple[bool, str]:
|
|
411
|
+
"""Start local Ollama serve process if installed and not running."""
|
|
412
|
+
return ollama_download.ensure_ollama_running(auto_start=True)
|
|
413
|
+
|
|
414
|
+
# ─────────────────────────────────────────────────────────────
|
|
415
|
+
# 6. Command Dispatcher
|
|
416
|
+
# ─────────────────────────────────────────────────────────────
|
|
417
|
+
|
|
418
|
+
def get_commands(self) -> List[Dict[str, str]]:
|
|
419
|
+
"""Return list of all 103 slash commands."""
|
|
420
|
+
return [{"command": c[0], "description": c[1]} for c in commands_data.COMMANDS]
|
|
421
|
+
|
|
422
|
+
def dispatch_command(self, cmd_line: str) -> Dict[str, Any]:
|
|
423
|
+
"""Dispatch a slash command to CAT handlers."""
|
|
424
|
+
cmd = cmd_line.strip()
|
|
425
|
+
parts = cmd.split(maxsplit=1)
|
|
426
|
+
base = parts[0].lower()
|
|
427
|
+
arg = parts[1].strip() if len(parts) > 1 else ""
|
|
428
|
+
|
|
429
|
+
if base in ("/about", "about"):
|
|
430
|
+
return {"command": base, "output": f"FATTY CAT v{identity.APP_VERSION} (Source: {identity.APP_NAME})"}
|
|
431
|
+
|
|
432
|
+
if base in ("/browser", "/browse", "/cat", "browser", "browse", "cat"):
|
|
433
|
+
try:
|
|
434
|
+
from calc_terminal.host.launcher import launch_cat_host, can_launch_host
|
|
435
|
+
if can_launch_host():
|
|
436
|
+
target = arg or "about:home"
|
|
437
|
+
launch_cat_host(start_browser_url=target, start_mode="browser", block=False)
|
|
438
|
+
return {"command": base, "output": f"Launched CAT Browser: {target}"}
|
|
439
|
+
return {"command": base, "output": "CAT Browser requires PySide6 on host."}
|
|
440
|
+
except Exception as e:
|
|
441
|
+
return {"command": base, "output": f"Error launching CAT Browser: {e}"}
|
|
442
|
+
|
|
443
|
+
if base in ("/help", "help"):
|
|
444
|
+
return {
|
|
445
|
+
"command": base,
|
|
446
|
+
"output": "Available commands:\n" + "\n".join(f" {c[0]:<20} {c[1]}" for c in commands_data.COMMANDS[:30]) + "\n ...and 70+ more. Use Command Palette (Ctrl+K) to browse all."
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
if base in ("/tokens", "tokens"):
|
|
450
|
+
usage = aicore.get_session_usage()
|
|
451
|
+
return {"command": base, "output": f"Tokens: {usage.get('total_tokens', 0)} used | Requests: {usage.get('requests', 0)}"}
|
|
452
|
+
|
|
453
|
+
if base in ("/calculator", "/calc", "calc"):
|
|
454
|
+
if not arg:
|
|
455
|
+
return {"command": base, "output": "Usage: /calc <expression> e.g. /calc 2.5 * log10(100)"}
|
|
456
|
+
from calc_terminal.agent import _tool_calculate
|
|
457
|
+
res = _tool_calculate({"expression": arg})
|
|
458
|
+
return {"command": base, "output": res}
|
|
459
|
+
|
|
460
|
+
if base in ("/formulas", "formulas"):
|
|
461
|
+
from calc_terminal.agent import _tool_list_formulas
|
|
462
|
+
return {"command": base, "output": _tool_list_formulas({})}
|
|
463
|
+
|
|
464
|
+
if base in ("/solve", "solve"):
|
|
465
|
+
if not arg:
|
|
466
|
+
return {"command": base, "output": "Usage: /solve <formula_key> values={...} or /solve formula=... values={...} solve_for=..."}
|
|
467
|
+
from calc_terminal.solver import FORMULA_LIBRARY
|
|
468
|
+
matches = [k for k in FORMULA_LIBRARY if arg.lower() in k.lower()]
|
|
469
|
+
if matches:
|
|
470
|
+
info = FORMULA_LIBRARY[matches[0]]
|
|
471
|
+
return {"command": base, "output": f"Formula: {info[0]}\nEquation: {info[2]}\nCategory: {info[1]}"}
|
|
472
|
+
return {"command": base, "output": f"No formula found matching '{arg}'."}
|
|
473
|
+
|
|
474
|
+
if base in ("/workspace", "workspace"):
|
|
475
|
+
root = ws_module.active_project() or ws_module.root_dir()
|
|
476
|
+
return {"command": base, "output": f"Active workspace: {root}"}
|
|
477
|
+
|
|
478
|
+
if base in ("/mode", "mode"):
|
|
479
|
+
if arg and arg in ai_modes.MODE_META:
|
|
480
|
+
new_m = self.set_mode(arg)
|
|
481
|
+
return {"command": base, "output": f"Switched AI mode to {new_m.upper()}."}
|
|
482
|
+
return {"command": base, "output": f"Current mode: {ai_modes.current_mode().upper()}\nAvailable: {', '.join(ai_modes.MODE_ORDER)}"}
|
|
483
|
+
|
|
484
|
+
if base in ("/memory", "/m", "memory"):
|
|
485
|
+
from calc_terminal import memory as _cct_mem
|
|
486
|
+
if arg.lower() == "clear":
|
|
487
|
+
_cct_mem.clear()
|
|
488
|
+
return {"command": base, "output": "Persistent memory cleared."}
|
|
489
|
+
facts = _cct_mem.get_facts() if hasattr(_cct_mem, "get_facts") else []
|
|
490
|
+
topics = _cct_mem.get_topics() if hasattr(_cct_mem, "get_topics") else []
|
|
491
|
+
out = f"Persistent Facts ({len(facts)}):\n"
|
|
492
|
+
for f in facts:
|
|
493
|
+
txt = f if isinstance(f, str) else f.get("fact", str(f))
|
|
494
|
+
out += f" • {txt}\n"
|
|
495
|
+
if topics:
|
|
496
|
+
out += f"\nTop Topics: {', '.join(topics[:5])}"
|
|
497
|
+
return {"command": base, "output": out or "No facts remembered yet."}
|
|
498
|
+
|
|
499
|
+
if base in ("/extensions", "extensions"):
|
|
500
|
+
exts = self.list_extensions()
|
|
501
|
+
out = "CAT Extensions:\n"
|
|
502
|
+
for e in exts:
|
|
503
|
+
status = "✓ Enabled" if e.get("enabled") else "○ Disabled"
|
|
504
|
+
out += f" {e.get('name', e.get('id')):<30} {status}\n"
|
|
505
|
+
return {"command": base, "output": out}
|
|
506
|
+
|
|
507
|
+
if base in ("/atomsim", "/atom", "atomsim"):
|
|
508
|
+
from calc_terminal.agent import _tool_simulate_atom_2d
|
|
509
|
+
return {"command": base, "output": _tool_simulate_atom_2d({"element": arg or "H"})}
|
|
510
|
+
|
|
511
|
+
if base in ("/orbitals", "orbitals"):
|
|
512
|
+
from calc_terminal.agent import _tool_simulate_orbital
|
|
513
|
+
return {"command": base, "output": _tool_simulate_orbital({"n": 2, "l": 1, "m": 0})}
|
|
514
|
+
|
|
515
|
+
if base in ("/graph", "graph"):
|
|
516
|
+
from calc_terminal.agent import _tool_plot_preset
|
|
517
|
+
preset_name = arg or "first_order_kinetics"
|
|
518
|
+
return {"command": base, "output": _tool_plot_preset({"preset": preset_name})}
|
|
519
|
+
|
|
520
|
+
if base in ("/model", "model"):
|
|
521
|
+
cfg = aicore.load_config()
|
|
522
|
+
if arg:
|
|
523
|
+
self.switch_model(cfg.get("provider", "ollama"), arg)
|
|
524
|
+
return {"command": base, "output": f"Switched model to {arg}."}
|
|
525
|
+
return {"command": base, "output": f"Active provider: {cfg.get('provider', 'ollama').upper()} · Model: {cfg.get('model', 'deepseek-r1')}"}
|
|
526
|
+
|
|
527
|
+
if base in ("/ai-verify", "/verify", "verify"):
|
|
528
|
+
cfg = aicore.load_config()
|
|
529
|
+
prov = cfg.get("provider", "ollama")
|
|
530
|
+
mod = cfg.get("model", "deepseek-r1")
|
|
531
|
+
try:
|
|
532
|
+
ok = aicore.verify_connection(prov, config=cfg)
|
|
533
|
+
status = "✓ Connection Successful" if ok else "⚠ Connection Failed"
|
|
534
|
+
except Exception as e:
|
|
535
|
+
status = f"⚠ Verification Error: {e}"
|
|
536
|
+
return {"command": base, "output": f"AI Connectivity Test for {prov.upper()} ({mod}): {status}"}
|
|
537
|
+
|
|
538
|
+
return {
|
|
539
|
+
"command": base,
|
|
540
|
+
"output": f"Command {base} dispatched. Type /help or press Ctrl+K for command details."
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
# ─────────────────────────────────────────────────────────────
|
|
544
|
+
# 7. Permissions
|
|
545
|
+
# ─────────────────────────────────────────────────────────────
|
|
546
|
+
|
|
547
|
+
def get_permissions(self) -> Dict[str, Any]:
|
|
548
|
+
"""Get current permission manager snapshot."""
|
|
549
|
+
return {
|
|
550
|
+
"mode": cct_perms.perm_manager.mode,
|
|
551
|
+
"mode_label": cct_perms.perm_manager.mode_label(),
|
|
552
|
+
"permissions": [
|
|
553
|
+
{"key": k, "label": l, "enabled": v}
|
|
554
|
+
for k, l, v in cct_perms.perm_manager.snapshot()
|
|
555
|
+
],
|
|
556
|
+
"log": [
|
|
557
|
+
{"time": t, "key": k, "action": a, "decision": d}
|
|
558
|
+
for t, k, a, d in cct_perms.perm_manager.log[-30:]
|
|
559
|
+
],
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
def set_permission_mode(self, mode: str) -> Dict[str, Any]:
|
|
563
|
+
cct_perms.perm_manager.set_mode(mode)
|
|
564
|
+
return {"mode": cct_perms.perm_manager.mode, "label": cct_perms.perm_manager.mode_label()}
|
|
565
|
+
|
|
566
|
+
def toggle_permission(self, key: str, enabled: bool) -> bool:
|
|
567
|
+
return cct_perms.perm_manager.set(key, enabled)
|
|
568
|
+
|
|
569
|
+
def decide_permission(self, key: str, decision: str, action_text: str = "") -> bool:
|
|
570
|
+
return cct_perms.perm_manager.decide(key, decision, action_text)
|
|
571
|
+
|
|
572
|
+
# ─────────────────────────────────────────────────────────────
|
|
573
|
+
# 8. Memory
|
|
574
|
+
# ─────────────────────────────────────────────────────────────
|
|
575
|
+
|
|
576
|
+
def get_memory_facts(self) -> List[str]:
|
|
577
|
+
data = memory.load()
|
|
578
|
+
return data.get("facts", [])
|
|
579
|
+
|
|
580
|
+
def add_memory_fact(self, fact: str):
|
|
581
|
+
memory.add_fact(fact)
|
|
582
|
+
|
|
583
|
+
def remove_memory_fact(self, index: int):
|
|
584
|
+
data = memory.load()
|
|
585
|
+
facts = data.get("facts", [])
|
|
586
|
+
if 0 <= index < len(facts):
|
|
587
|
+
facts.pop(index)
|
|
588
|
+
data["facts"] = facts
|
|
589
|
+
memory.save(data)
|
|
590
|
+
|
|
591
|
+
def get_memory_activity(self) -> List[Dict[str, Any]]:
|
|
592
|
+
data = memory.load()
|
|
593
|
+
return data.get("activity", [])
|
|
594
|
+
|
|
595
|
+
def get_memory_topics(self) -> Dict[str, int]:
|
|
596
|
+
data = memory.load()
|
|
597
|
+
return data.get("topics", {})
|
|
598
|
+
|
|
599
|
+
# ─────────────────────────────────────────────────────────────
|
|
600
|
+
# 9. Customization & UI Layout
|
|
601
|
+
# ─────────────────────────────────────────────────────────────
|
|
602
|
+
|
|
603
|
+
def get_customization(self) -> Dict[str, Any]:
|
|
604
|
+
"""Get entire layout and style customization state."""
|
|
605
|
+
return customization.get_customization().to_dict()
|
|
606
|
+
|
|
607
|
+
def update_layout_component(self, comp_id: str, updates: Dict[str, Any]) -> bool:
|
|
608
|
+
"""Update layout positioning or size for a UI panel."""
|
|
609
|
+
cust = customization.get_customization()
|
|
610
|
+
return cust.layout.update_component(comp_id, updates)
|
|
611
|
+
|
|
612
|
+
def reset_customization(self) -> bool:
|
|
613
|
+
"""Reset layout and styling to factory defaults."""
|
|
614
|
+
return customization.get_customization().reset_to_defaults()
|
|
615
|
+
|
|
616
|
+
# ─────────────────────────────────────────────────────────────
|
|
617
|
+
# 10. Extensions
|
|
618
|
+
# ─────────────────────────────────────────────────────────────
|
|
619
|
+
|
|
620
|
+
def list_extensions(self) -> List[Dict[str, Any]]:
|
|
621
|
+
return cct_ext.list_extensions()
|
|
622
|
+
|
|
623
|
+
def toggle_extension(self, ext_id: str, enable: bool) -> bool:
|
|
624
|
+
if enable:
|
|
625
|
+
return cct_ext.enable(ext_id)
|
|
626
|
+
return cct_ext.disable(ext_id)
|
|
627
|
+
|
|
628
|
+
def create_extension(self, **kwargs) -> Tuple[bool, Dict[str, Any]]:
|
|
629
|
+
return cct_ext.create_extension(**kwargs)
|
|
630
|
+
|
|
631
|
+
# ─────────────────────────────────────────────────────────────
|
|
632
|
+
# 11. Science & Notebook
|
|
633
|
+
# ─────────────────────────────────────────────────────────────
|
|
634
|
+
|
|
635
|
+
def solve_formula(self, key: str, values: Dict[str, float]) -> Dict[str, Any]:
|
|
636
|
+
from calc_terminal import solver
|
|
637
|
+
try:
|
|
638
|
+
solve_for, result, name, cat, eq = solver.solve_library_formula(key, values)
|
|
639
|
+
return {
|
|
640
|
+
"success": True,
|
|
641
|
+
"name": name,
|
|
642
|
+
"category": cat,
|
|
643
|
+
"equation": eq,
|
|
644
|
+
"solve_for": solve_for,
|
|
645
|
+
"result": result,
|
|
646
|
+
}
|
|
647
|
+
except Exception as e:
|
|
648
|
+
return {"success": False, "error": str(e)}
|
|
649
|
+
|
|
650
|
+
def get_derivation(self, keyword: str) -> Dict[str, Any]:
|
|
651
|
+
from calc_terminal import derivations
|
|
652
|
+
text = derivations.get_derivation(keyword)
|
|
653
|
+
if text:
|
|
654
|
+
return {"success": True, "derivation": text}
|
|
655
|
+
return {"success": False, "error": f"No derivation found for '{keyword}'"}
|
|
656
|
+
|
|
657
|
+
def get_atom_sim(self, element: str) -> Dict[str, Any]:
|
|
658
|
+
from calc_terminal import atomsim
|
|
659
|
+
z = atomsim.resolve_element(element)
|
|
660
|
+
if z is None:
|
|
661
|
+
return {"success": False, "error": f"Unknown element '{element}'"}
|
|
662
|
+
sym, name, shells, mass = atomsim.ELEMENTS[z]
|
|
663
|
+
return {
|
|
664
|
+
"success": True,
|
|
665
|
+
"z": z,
|
|
666
|
+
"symbol": sym,
|
|
667
|
+
"name": name,
|
|
668
|
+
"shells": shells,
|
|
669
|
+
"mass": mass,
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
# ─────────────────────────────────────────────────────────────
|
|
673
|
+
# 12. Diagnostics
|
|
674
|
+
# ─────────────────────────────────────────────────────────────
|
|
675
|
+
|
|
676
|
+
def get_diagnostics(self) -> Dict[str, Any]:
|
|
677
|
+
"""System diagnostics, version contract, runtime statistics."""
|
|
678
|
+
cfg = aicore.load_config()
|
|
679
|
+
uptime_sec = int(time.time() - self.start_time)
|
|
680
|
+
|
|
681
|
+
# Hardware metrics
|
|
682
|
+
specs = {}
|
|
683
|
+
try:
|
|
684
|
+
from calc_terminal import pc_specs
|
|
685
|
+
specs = pc_specs.get_specs()
|
|
686
|
+
except Exception:
|
|
687
|
+
pass
|
|
688
|
+
|
|
689
|
+
return {
|
|
690
|
+
"contract": {
|
|
691
|
+
"cat_version": identity.APP_VERSION,
|
|
692
|
+
"api_version": "1.0.0",
|
|
693
|
+
"fatty_cat_version": "1.0.0",
|
|
694
|
+
},
|
|
695
|
+
"runtime": {
|
|
696
|
+
"uptime_seconds": uptime_sec,
|
|
697
|
+
"request_count": self.request_count,
|
|
698
|
+
"error_count": self.error_count,
|
|
699
|
+
"active_workspace": ws_module.active_project() or ws_module.root_dir(),
|
|
700
|
+
"active_provider": cfg.get("provider", "openai"),
|
|
701
|
+
"active_model": cfg.get("model", "gpt-4o"),
|
|
702
|
+
"active_mode": ai_modes.current_mode(),
|
|
703
|
+
},
|
|
704
|
+
"hardware": specs,
|
|
705
|
+
"recent_events": self.recent_events[-25:],
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
|
|
709
|
+
# Singleton runtime adapter instance
|
|
710
|
+
runtime = CatRuntime()
|