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,491 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CAT Platform Abstraction Layer
|
|
3
|
+
|
|
4
|
+
Provides a unified interface for platform-specific operations:
|
|
5
|
+
- Desktop (native Python)
|
|
6
|
+
- Web/PWA (browser via WebAssembly or server-bridged)
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import os
|
|
12
|
+
import sys
|
|
13
|
+
from abc import ABC, abstractmethod
|
|
14
|
+
from dataclasses import dataclass
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from typing import Any, BinaryIO, List, Optional, Union
|
|
17
|
+
from enum import Enum
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class PlatformType(Enum):
|
|
21
|
+
DESKTOP = "desktop"
|
|
22
|
+
WEB = "web"
|
|
23
|
+
PWA = "pwa"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass
|
|
27
|
+
class PlatformInfo:
|
|
28
|
+
type: PlatformType
|
|
29
|
+
name: str
|
|
30
|
+
version: str
|
|
31
|
+
is_mobile: bool
|
|
32
|
+
supports_file_system_access: bool
|
|
33
|
+
supports_notifications: bool
|
|
34
|
+
supports_background_sync: bool
|
|
35
|
+
user_agent: str = ""
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class FileHandle(ABC):
|
|
39
|
+
"""Abstract file handle for platform-agnostic file operations."""
|
|
40
|
+
|
|
41
|
+
@abstractmethod
|
|
42
|
+
async def read(self, size: int = -1) -> bytes:
|
|
43
|
+
pass
|
|
44
|
+
|
|
45
|
+
@abstractmethod
|
|
46
|
+
async def write(self, data: Union[bytes, str]) -> int:
|
|
47
|
+
pass
|
|
48
|
+
|
|
49
|
+
@abstractmethod
|
|
50
|
+
async def close(self) -> None:
|
|
51
|
+
pass
|
|
52
|
+
|
|
53
|
+
@abstractmethod
|
|
54
|
+
async def seek(self, offset: int, whence: int = 0) -> int:
|
|
55
|
+
pass
|
|
56
|
+
|
|
57
|
+
@abstractmethod
|
|
58
|
+
def tell(self) -> int:
|
|
59
|
+
pass
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class DirectoryHandle(ABC):
|
|
63
|
+
"""Abstract directory handle for platform-agnostic directory operations."""
|
|
64
|
+
|
|
65
|
+
@abstractmethod
|
|
66
|
+
async def get_file_handle(self, name: str, create: bool = False) -> FileHandle:
|
|
67
|
+
pass
|
|
68
|
+
|
|
69
|
+
@abstractmethod
|
|
70
|
+
async def get_directory_handle(self, name: str, create: bool = False) -> DirectoryHandle:
|
|
71
|
+
pass
|
|
72
|
+
|
|
73
|
+
@abstractmethod
|
|
74
|
+
async def remove_entry(self, name: str, recursive: bool = False) -> None:
|
|
75
|
+
pass
|
|
76
|
+
|
|
77
|
+
@abstractmethod
|
|
78
|
+
async def list_entries(self) -> List[str]:
|
|
79
|
+
pass
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class PlatformBase(ABC):
|
|
83
|
+
"""Abstract base class for platform implementations."""
|
|
84
|
+
|
|
85
|
+
@property
|
|
86
|
+
@abstractmethod
|
|
87
|
+
def info(self) -> PlatformInfo:
|
|
88
|
+
pass
|
|
89
|
+
|
|
90
|
+
# --- Filesystem ---
|
|
91
|
+
|
|
92
|
+
@abstractmethod
|
|
93
|
+
async def get_root_directory(self) -> DirectoryHandle:
|
|
94
|
+
"""Get the root directory handle (workspace root for web, home for desktop)."""
|
|
95
|
+
pass
|
|
96
|
+
|
|
97
|
+
@abstractmethod
|
|
98
|
+
async def pick_directory(self, title: str = "Select folder") -> Optional[DirectoryHandle]:
|
|
99
|
+
"""Show directory picker dialog."""
|
|
100
|
+
pass
|
|
101
|
+
|
|
102
|
+
@abstractmethod
|
|
103
|
+
async def pick_file(self, title: str = "Select file", accept: Optional[dict] = None) -> Optional[FileHandle]:
|
|
104
|
+
"""Show file picker dialog."""
|
|
105
|
+
pass
|
|
106
|
+
|
|
107
|
+
@abstractmethod
|
|
108
|
+
async def resolve_path(self, path: str) -> str:
|
|
109
|
+
"""Resolve a path to absolute form."""
|
|
110
|
+
pass
|
|
111
|
+
|
|
112
|
+
@abstractmethod
|
|
113
|
+
async def path_exists(self, path: str) -> bool:
|
|
114
|
+
"""Check if path exists."""
|
|
115
|
+
pass
|
|
116
|
+
|
|
117
|
+
@abstractmethod
|
|
118
|
+
async def is_directory(self, path: str) -> bool:
|
|
119
|
+
"""Check if path is a directory."""
|
|
120
|
+
pass
|
|
121
|
+
|
|
122
|
+
@abstractmethod
|
|
123
|
+
async def is_file(self, path: str) -> bool:
|
|
124
|
+
"""Check if path is a file."""
|
|
125
|
+
pass
|
|
126
|
+
|
|
127
|
+
@abstractmethod
|
|
128
|
+
async def list_directory(self, path: str) -> List[str]:
|
|
129
|
+
"""List directory contents."""
|
|
130
|
+
pass
|
|
131
|
+
|
|
132
|
+
@abstractmethod
|
|
133
|
+
async def read_file(self, path: str, encoding: Optional[str] = None) -> Union[str, bytes]:
|
|
134
|
+
"""Read file contents."""
|
|
135
|
+
pass
|
|
136
|
+
|
|
137
|
+
@abstractmethod
|
|
138
|
+
async def write_file(self, path: str, data: Union[str, bytes], encoding: Optional[str] = None) -> int:
|
|
139
|
+
"""Write file contents."""
|
|
140
|
+
pass
|
|
141
|
+
|
|
142
|
+
@abstractmethod
|
|
143
|
+
async def delete_file(self, path: str) -> None:
|
|
144
|
+
"""Delete a file."""
|
|
145
|
+
pass
|
|
146
|
+
|
|
147
|
+
@abstractmethod
|
|
148
|
+
async def delete_directory(self, path: str, recursive: bool = False) -> None:
|
|
149
|
+
"""Delete a directory."""
|
|
150
|
+
pass
|
|
151
|
+
|
|
152
|
+
@abstractmethod
|
|
153
|
+
async def create_directory(self, path: str, parents: bool = True) -> None:
|
|
154
|
+
"""Create a directory."""
|
|
155
|
+
pass
|
|
156
|
+
|
|
157
|
+
@abstractmethod
|
|
158
|
+
async def copy_file(self, src: str, dst: str) -> None:
|
|
159
|
+
"""Copy a file."""
|
|
160
|
+
pass
|
|
161
|
+
|
|
162
|
+
@abstractmethod
|
|
163
|
+
async def move_file(self, src: str, dst: str) -> None:
|
|
164
|
+
"""Move/rename a file."""
|
|
165
|
+
pass
|
|
166
|
+
|
|
167
|
+
@abstractmethod
|
|
168
|
+
async def get_file_size(self, path: str) -> int:
|
|
169
|
+
"""Get file size in bytes."""
|
|
170
|
+
pass
|
|
171
|
+
|
|
172
|
+
@abstractmethod
|
|
173
|
+
async def get_file_mtime(self, path: str) -> float:
|
|
174
|
+
"""Get file modification time."""
|
|
175
|
+
pass
|
|
176
|
+
|
|
177
|
+
# --- Process Execution ---
|
|
178
|
+
|
|
179
|
+
@abstractmethod
|
|
180
|
+
async def run_command(
|
|
181
|
+
self,
|
|
182
|
+
cmd: Union[str, List[str]],
|
|
183
|
+
cwd: Optional[str] = None,
|
|
184
|
+
env: Optional[dict] = None,
|
|
185
|
+
capture_output: bool = True,
|
|
186
|
+
timeout: Optional[float] = None,
|
|
187
|
+
) -> "CommandResult":
|
|
188
|
+
"""Run a command and return result."""
|
|
189
|
+
pass
|
|
190
|
+
|
|
191
|
+
@abstractmethod
|
|
192
|
+
async def start_process(
|
|
193
|
+
self,
|
|
194
|
+
cmd: Union[str, List[str]],
|
|
195
|
+
cwd: Optional[str] = None,
|
|
196
|
+
env: Optional[dict] = None,
|
|
197
|
+
) -> "ProcessHandle":
|
|
198
|
+
"""Start a background process."""
|
|
199
|
+
pass
|
|
200
|
+
|
|
201
|
+
# --- Clipboard ---
|
|
202
|
+
|
|
203
|
+
@abstractmethod
|
|
204
|
+
async def read_clipboard(self) -> str:
|
|
205
|
+
"""Read text from clipboard."""
|
|
206
|
+
pass
|
|
207
|
+
|
|
208
|
+
@abstractmethod
|
|
209
|
+
async def write_clipboard(self, text: str) -> None:
|
|
210
|
+
"""Write text to clipboard."""
|
|
211
|
+
pass
|
|
212
|
+
|
|
213
|
+
# --- Notifications ---
|
|
214
|
+
|
|
215
|
+
@abstractmethod
|
|
216
|
+
async def show_notification(
|
|
217
|
+
self,
|
|
218
|
+
title: str,
|
|
219
|
+
body: str,
|
|
220
|
+
icon: Optional[str] = None,
|
|
221
|
+
) -> None:
|
|
222
|
+
"""Show a system notification."""
|
|
223
|
+
pass
|
|
224
|
+
|
|
225
|
+
@abstractmethod
|
|
226
|
+
async def request_notification_permission(self) -> bool:
|
|
227
|
+
"""Request permission for notifications."""
|
|
228
|
+
pass
|
|
229
|
+
|
|
230
|
+
# --- Browser/URL ---
|
|
231
|
+
|
|
232
|
+
@abstractmethod
|
|
233
|
+
async def open_url(self, url: str) -> bool:
|
|
234
|
+
"""Open a URL in the default browser."""
|
|
235
|
+
pass
|
|
236
|
+
|
|
237
|
+
# --- Keyboard/Input ---
|
|
238
|
+
|
|
239
|
+
@abstractmethod
|
|
240
|
+
async def show_keyboard(self) -> None:
|
|
241
|
+
"""Show virtual keyboard (mobile)."""
|
|
242
|
+
pass
|
|
243
|
+
|
|
244
|
+
@abstractmethod
|
|
245
|
+
async def hide_keyboard(self) -> None:
|
|
246
|
+
"""Hide virtual keyboard (mobile)."""
|
|
247
|
+
pass
|
|
248
|
+
|
|
249
|
+
# --- Storage ---
|
|
250
|
+
|
|
251
|
+
@abstractmethod
|
|
252
|
+
async def get_storage_estimate(self) -> dict:
|
|
253
|
+
"""Get storage usage estimate."""
|
|
254
|
+
pass
|
|
255
|
+
|
|
256
|
+
@abstractmethod
|
|
257
|
+
async def request_persistent_storage(self) -> bool:
|
|
258
|
+
"""Request persistent storage (web)."""
|
|
259
|
+
pass
|
|
260
|
+
|
|
261
|
+
# --- App Lifecycle ---
|
|
262
|
+
|
|
263
|
+
@abstractmethod
|
|
264
|
+
async def minimize_app(self) -> None:
|
|
265
|
+
"""Minimize the app."""
|
|
266
|
+
pass
|
|
267
|
+
|
|
268
|
+
@abstractmethod
|
|
269
|
+
async def close_app(self) -> None:
|
|
270
|
+
"""Close the app."""
|
|
271
|
+
pass
|
|
272
|
+
|
|
273
|
+
# --- Platform-specific ---
|
|
274
|
+
|
|
275
|
+
@abstractmethod
|
|
276
|
+
def get_env(self, key: str, default: Optional[str] = None) -> Optional[str]:
|
|
277
|
+
"""Get environment variable."""
|
|
278
|
+
pass
|
|
279
|
+
|
|
280
|
+
@abstractmethod
|
|
281
|
+
def set_env(self, key: str, value: str) -> None:
|
|
282
|
+
"""Set environment variable."""
|
|
283
|
+
pass
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
@dataclass
|
|
287
|
+
class CommandResult:
|
|
288
|
+
returncode: int
|
|
289
|
+
stdout: str
|
|
290
|
+
stderr: str
|
|
291
|
+
success: bool
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
class ProcessHandle(ABC):
|
|
295
|
+
"""Handle for a running background process."""
|
|
296
|
+
|
|
297
|
+
@abstractmethod
|
|
298
|
+
async def wait(self, timeout: Optional[float] = None) -> CommandResult:
|
|
299
|
+
pass
|
|
300
|
+
|
|
301
|
+
@abstractmethod
|
|
302
|
+
async def terminate(self) -> None:
|
|
303
|
+
pass
|
|
304
|
+
|
|
305
|
+
@abstractmethod
|
|
306
|
+
async def kill(self) -> None:
|
|
307
|
+
pass
|
|
308
|
+
|
|
309
|
+
@property
|
|
310
|
+
@abstractmethod
|
|
311
|
+
def pid(self) -> Optional[int]:
|
|
312
|
+
pass
|
|
313
|
+
|
|
314
|
+
@property
|
|
315
|
+
@abstractmethod
|
|
316
|
+
def returncode(self) -> Optional[int]:
|
|
317
|
+
pass
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
# Global platform instance
|
|
321
|
+
_platform: Optional[PlatformBase] = None
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
def get_platform() -> PlatformBase:
|
|
325
|
+
"""Get the current platform implementation."""
|
|
326
|
+
global _platform
|
|
327
|
+
if _platform is None:
|
|
328
|
+
_platform = _detect_platform()
|
|
329
|
+
return _platform
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
def set_platform(platform: PlatformBase) -> None:
|
|
333
|
+
"""Set the platform implementation (for testing/override)."""
|
|
334
|
+
global _platform
|
|
335
|
+
_platform = platform
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
def _detect_platform() -> PlatformBase:
|
|
339
|
+
"""Auto-detect the platform."""
|
|
340
|
+
# Check if running in browser (Pyodide/WASM)
|
|
341
|
+
if "pyodide" in sys.modules or hasattr(sys, "implementation") and sys.implementation.name == "pyodide":
|
|
342
|
+
from .web import WebPlatform
|
|
343
|
+
return WebPlatform()
|
|
344
|
+
|
|
345
|
+
# Check if running in a PWA context (served via HTTP)
|
|
346
|
+
if os.environ.get("CAT_PLATFORM") == "web":
|
|
347
|
+
from .web import WebPlatform
|
|
348
|
+
return WebPlatform()
|
|
349
|
+
|
|
350
|
+
# Default to desktop
|
|
351
|
+
from .desktop import DesktopPlatform
|
|
352
|
+
return DesktopPlatform()
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
# Convenience functions that delegate to the platform
|
|
356
|
+
async def get_root_directory() -> DirectoryHandle:
|
|
357
|
+
return await get_platform().get_root_directory()
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
async def pick_directory(title: str = "Select folder") -> Optional[DirectoryHandle]:
|
|
361
|
+
return await get_platform().pick_directory(title)
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
async def pick_file(title: str = "Select file", accept: Optional[dict] = None) -> Optional[FileHandle]:
|
|
365
|
+
return await get_platform().pick_file(title, accept)
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
async def resolve_path(path: str) -> str:
|
|
369
|
+
return await get_platform().resolve_path(path)
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
async def path_exists(path: str) -> bool:
|
|
373
|
+
return await get_platform().path_exists(path)
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
async def is_directory(path: str) -> bool:
|
|
377
|
+
return await get_platform().is_directory(path)
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
async def is_file(path: str) -> bool:
|
|
381
|
+
return await get_platform().is_file(path)
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
async def list_directory(path: str) -> List[str]:
|
|
385
|
+
return await get_platform().list_directory(path)
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
async def read_file(path: str, encoding: Optional[str] = None) -> Union[str, bytes]:
|
|
389
|
+
return await get_platform().read_file(path, encoding)
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
async def write_file(path: str, data: Union[str, bytes], encoding: Optional[str] = None) -> int:
|
|
393
|
+
return await get_platform().write_file(path, data, encoding)
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
async def delete_file(path: str) -> None:
|
|
397
|
+
return await get_platform().delete_file(path)
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
async def delete_directory(path: str, recursive: bool = False) -> None:
|
|
401
|
+
return await get_platform().delete_directory(path, recursive)
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
async def create_directory(path: str, parents: bool = True) -> None:
|
|
405
|
+
return await get_platform().create_directory(path, parents)
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
async def copy_file(src: str, dst: str) -> None:
|
|
409
|
+
return await get_platform().copy_file(src, dst)
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
async def move_file(src: str, dst: str) -> None:
|
|
413
|
+
return await get_platform().move_file(src, dst)
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
async def get_file_size(path: str) -> int:
|
|
417
|
+
return await get_platform().get_file_size(path)
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
async def get_file_mtime(path: str) -> float:
|
|
421
|
+
return await get_platform().get_file_mtime(path)
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
async def run_command(
|
|
425
|
+
cmd: Union[str, List[str]],
|
|
426
|
+
cwd: Optional[str] = None,
|
|
427
|
+
env: Optional[dict] = None,
|
|
428
|
+
capture_output: bool = True,
|
|
429
|
+
timeout: Optional[float] = None,
|
|
430
|
+
) -> CommandResult:
|
|
431
|
+
return await get_platform().run_command(cmd, cwd, env, capture_output, timeout)
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
async def start_process(
|
|
435
|
+
cmd: Union[str, List[str]],
|
|
436
|
+
cwd: Optional[str] = None,
|
|
437
|
+
env: Optional[dict] = None,
|
|
438
|
+
) -> ProcessHandle:
|
|
439
|
+
return await get_platform().start_process(cmd, cwd, env)
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
async def read_clipboard() -> str:
|
|
443
|
+
return await get_platform().read_clipboard()
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
async def write_clipboard(text: str) -> None:
|
|
447
|
+
return await get_platform().write_clipboard(text)
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
async def show_notification(title: str, body: str, icon: Optional[str] = None) -> None:
|
|
451
|
+
return await get_platform().show_notification(title, body, icon)
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
async def request_notification_permission() -> bool:
|
|
455
|
+
return await get_platform().request_notification_permission()
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
async def open_url(url: str) -> bool:
|
|
459
|
+
return await get_platform().open_url(url)
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
async def show_keyboard() -> None:
|
|
463
|
+
return await get_platform().show_keyboard()
|
|
464
|
+
|
|
465
|
+
|
|
466
|
+
async def hide_keyboard() -> None:
|
|
467
|
+
return await get_platform().hide_keyboard()
|
|
468
|
+
|
|
469
|
+
|
|
470
|
+
async def get_storage_estimate() -> dict:
|
|
471
|
+
return await get_platform().get_storage_estimate()
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
async def request_persistent_storage() -> bool:
|
|
475
|
+
return await get_platform().request_persistent_storage()
|
|
476
|
+
|
|
477
|
+
|
|
478
|
+
async def minimize_app() -> None:
|
|
479
|
+
return await get_platform().minimize_app()
|
|
480
|
+
|
|
481
|
+
|
|
482
|
+
async def close_app() -> None:
|
|
483
|
+
return await get_platform().close_app()
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
def get_env(key: str, default: Optional[str] = None) -> Optional[str]:
|
|
487
|
+
return get_platform().get_env(key, default)
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
def set_env(key: str, value: str) -> None:
|
|
491
|
+
return get_platform().set_env(key, value)
|