portacode 0.3.19.dev4__py3-none-any.whl → 1.4.11.dev1__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.
Potentially problematic release.
This version of portacode might be problematic. Click here for more details.
- portacode/_version.py +16 -3
- portacode/cli.py +143 -17
- portacode/connection/client.py +149 -10
- portacode/connection/handlers/WEBSOCKET_PROTOCOL.md +824 -21
- portacode/connection/handlers/__init__.py +28 -1
- portacode/connection/handlers/base.py +78 -16
- portacode/connection/handlers/chunked_content.py +244 -0
- portacode/connection/handlers/diff_handlers.py +603 -0
- portacode/connection/handlers/file_handlers.py +902 -17
- portacode/connection/handlers/project_aware_file_handlers.py +226 -0
- portacode/connection/handlers/project_state/README.md +312 -0
- portacode/connection/handlers/project_state/__init__.py +92 -0
- portacode/connection/handlers/project_state/file_system_watcher.py +179 -0
- portacode/connection/handlers/project_state/git_manager.py +1502 -0
- portacode/connection/handlers/project_state/handlers.py +875 -0
- portacode/connection/handlers/project_state/manager.py +1331 -0
- portacode/connection/handlers/project_state/models.py +108 -0
- portacode/connection/handlers/project_state/utils.py +50 -0
- portacode/connection/handlers/project_state_handlers.py +45 -2185
- portacode/connection/handlers/proxmox_infra.py +361 -0
- portacode/connection/handlers/registry.py +15 -4
- portacode/connection/handlers/session.py +483 -32
- portacode/connection/handlers/system_handlers.py +147 -8
- portacode/connection/handlers/tab_factory.py +53 -46
- portacode/connection/handlers/terminal_handlers.py +21 -8
- portacode/connection/handlers/update_handler.py +61 -0
- portacode/connection/multiplex.py +60 -2
- portacode/connection/terminal.py +214 -24
- portacode/keypair.py +63 -1
- portacode/link_capture/__init__.py +38 -0
- portacode/link_capture/__pycache__/__init__.cpython-311.pyc +0 -0
- portacode/link_capture/bin/__pycache__/link_capture_wrapper.cpython-311.pyc +0 -0
- portacode/link_capture/bin/elinks +3 -0
- portacode/link_capture/bin/gio-open +3 -0
- portacode/link_capture/bin/gnome-open +3 -0
- portacode/link_capture/bin/gvfs-open +3 -0
- portacode/link_capture/bin/kde-open +3 -0
- portacode/link_capture/bin/kfmclient +3 -0
- portacode/link_capture/bin/link_capture_exec.sh +11 -0
- portacode/link_capture/bin/link_capture_wrapper.py +75 -0
- portacode/link_capture/bin/links +3 -0
- portacode/link_capture/bin/links2 +3 -0
- portacode/link_capture/bin/lynx +3 -0
- portacode/link_capture/bin/mate-open +3 -0
- portacode/link_capture/bin/netsurf +3 -0
- portacode/link_capture/bin/sensible-browser +3 -0
- portacode/link_capture/bin/w3m +3 -0
- portacode/link_capture/bin/x-www-browser +3 -0
- portacode/link_capture/bin/xdg-open +3 -0
- portacode/logging_categories.py +140 -0
- portacode/pairing.py +103 -0
- portacode/static/js/test-ntp-clock.html +63 -0
- portacode/static/js/utils/ntp-clock.js +232 -0
- portacode/utils/NTP_ARCHITECTURE.md +136 -0
- portacode/utils/__init__.py +1 -0
- portacode/utils/diff_apply.py +456 -0
- portacode/utils/diff_renderer.py +371 -0
- portacode/utils/ntp_clock.py +65 -0
- portacode-1.4.11.dev1.dist-info/METADATA +298 -0
- portacode-1.4.11.dev1.dist-info/RECORD +97 -0
- {portacode-0.3.19.dev4.dist-info → portacode-1.4.11.dev1.dist-info}/WHEEL +1 -1
- portacode-1.4.11.dev1.dist-info/top_level.txt +3 -0
- test_modules/README.md +296 -0
- test_modules/__init__.py +1 -0
- test_modules/test_device_online.py +44 -0
- test_modules/test_file_operations.py +743 -0
- test_modules/test_git_status_ui.py +370 -0
- test_modules/test_login_flow.py +50 -0
- test_modules/test_navigate_testing_folder.py +361 -0
- test_modules/test_play_store_screenshots.py +294 -0
- test_modules/test_terminal_buffer_performance.py +261 -0
- test_modules/test_terminal_interaction.py +80 -0
- test_modules/test_terminal_loading_race_condition.py +95 -0
- test_modules/test_terminal_start.py +56 -0
- testing_framework/.env.example +21 -0
- testing_framework/README.md +334 -0
- testing_framework/__init__.py +17 -0
- testing_framework/cli.py +326 -0
- testing_framework/core/__init__.py +1 -0
- testing_framework/core/base_test.py +336 -0
- testing_framework/core/cli_manager.py +177 -0
- testing_framework/core/hierarchical_runner.py +577 -0
- testing_framework/core/playwright_manager.py +520 -0
- testing_framework/core/runner.py +447 -0
- testing_framework/core/shared_cli_manager.py +234 -0
- testing_framework/core/test_discovery.py +112 -0
- testing_framework/requirements.txt +12 -0
- portacode-0.3.19.dev4.dist-info/METADATA +0 -241
- portacode-0.3.19.dev4.dist-info/RECORD +0 -30
- portacode-0.3.19.dev4.dist-info/top_level.txt +0 -1
- {portacode-0.3.19.dev4.dist-info → portacode-1.4.11.dev1.dist-info}/entry_points.txt +0 -0
- {portacode-0.3.19.dev4.dist-info → portacode-1.4.11.dev1.dist-info/licenses}/LICENSE +0 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"""Data models for project state management.
|
|
2
|
+
|
|
3
|
+
This module contains all the dataclasses and models used throughout the project
|
|
4
|
+
state management system, including TabInfo, FileItem, GitFileChange,
|
|
5
|
+
GitDetailedStatus, ProjectState, and MonitoredFolder.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from dataclasses import dataclass, asdict
|
|
9
|
+
from typing import Any, Dict, List, Optional, Set, Union
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class TabInfo:
|
|
14
|
+
"""Represents an editor tab with content and metadata."""
|
|
15
|
+
tab_id: str # Unique identifier for the tab
|
|
16
|
+
tab_type: str # 'file', 'diff', 'untitled', 'image', 'audio', 'video'
|
|
17
|
+
title: str # Display title for the tab
|
|
18
|
+
file_path: Optional[str] = None # Path for file-based tabs
|
|
19
|
+
content: Optional[str] = None # Text content or base64 for media
|
|
20
|
+
original_content: Optional[str] = None # For diff view
|
|
21
|
+
modified_content: Optional[str] = None # For diff view
|
|
22
|
+
|
|
23
|
+
# Content hash fields for caching optimization
|
|
24
|
+
content_hash: Optional[str] = None # SHA-256 hash of content
|
|
25
|
+
original_content_hash: Optional[str] = None # SHA-256 hash of original_content for diffs
|
|
26
|
+
modified_content_hash: Optional[str] = None # SHA-256 hash of modified_content for diffs
|
|
27
|
+
html_diff_hash: Optional[str] = None # SHA-256 hash of html_diff_versions JSON
|
|
28
|
+
|
|
29
|
+
is_dirty: bool = False # Has unsaved changes
|
|
30
|
+
mime_type: Optional[str] = None # For media files
|
|
31
|
+
encoding: Optional[str] = None # Content encoding (base64, utf-8, etc.)
|
|
32
|
+
metadata: Optional[Dict[str, Any]] = None # Additional metadata
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass
|
|
36
|
+
class MonitoredFolder:
|
|
37
|
+
"""Represents a folder that is being monitored for changes."""
|
|
38
|
+
folder_path: str
|
|
39
|
+
is_expanded: bool = False
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@dataclass
|
|
43
|
+
class FileItem:
|
|
44
|
+
"""Represents a file or directory item with metadata."""
|
|
45
|
+
name: str
|
|
46
|
+
path: str
|
|
47
|
+
is_directory: bool
|
|
48
|
+
parent_path: str
|
|
49
|
+
size: Optional[int] = None
|
|
50
|
+
modified_time: Optional[float] = None
|
|
51
|
+
is_git_tracked: Optional[bool] = None
|
|
52
|
+
git_status: Optional[str] = None
|
|
53
|
+
is_staged: Optional[Union[bool, str]] = None # True, False, or "mixed"
|
|
54
|
+
is_hidden: bool = False
|
|
55
|
+
is_ignored: bool = False
|
|
56
|
+
children: Optional[List['FileItem']] = None
|
|
57
|
+
is_expanded: bool = False
|
|
58
|
+
is_loaded: bool = False
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@dataclass
|
|
62
|
+
class GitFileChange:
|
|
63
|
+
"""Represents a single file change in git."""
|
|
64
|
+
file_repo_path: str # Relative path from repository root
|
|
65
|
+
file_name: str # Just the filename (basename)
|
|
66
|
+
file_abs_path: str # Absolute path to the file
|
|
67
|
+
change_type: str # 'added', 'modified', 'deleted', 'untracked' - follows git's native types
|
|
68
|
+
content_hash: Optional[str] = None # SHA256 hash of current file content
|
|
69
|
+
is_staged: bool = False # Whether this change is staged
|
|
70
|
+
diff_details: Optional[Dict[str, Any]] = None # Per-character diff information using diff-match-patch
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@dataclass
|
|
74
|
+
class GitDetailedStatus:
|
|
75
|
+
"""Represents detailed git status with file hashes."""
|
|
76
|
+
head_commit_hash: Optional[str] = None # Hash of HEAD commit
|
|
77
|
+
staged_changes: List[GitFileChange] = None # Changes in the staging area
|
|
78
|
+
unstaged_changes: List[GitFileChange] = None # Changes in working directory
|
|
79
|
+
untracked_files: List[GitFileChange] = None # Untracked files
|
|
80
|
+
|
|
81
|
+
def __post_init__(self):
|
|
82
|
+
if self.staged_changes is None:
|
|
83
|
+
self.staged_changes = []
|
|
84
|
+
if self.unstaged_changes is None:
|
|
85
|
+
self.unstaged_changes = []
|
|
86
|
+
if self.untracked_files is None:
|
|
87
|
+
self.untracked_files = []
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
@dataclass
|
|
91
|
+
class ProjectState:
|
|
92
|
+
"""Represents the complete state of a project."""
|
|
93
|
+
client_session_id: str # The client session ID - one project per client session
|
|
94
|
+
project_folder_path: str
|
|
95
|
+
items: List[FileItem]
|
|
96
|
+
monitored_folders: List[MonitoredFolder] = None
|
|
97
|
+
is_git_repo: bool = False
|
|
98
|
+
git_branch: Optional[str] = None
|
|
99
|
+
git_status_summary: Optional[Dict[str, int]] = None # Kept for backward compatibility
|
|
100
|
+
git_detailed_status: Optional[GitDetailedStatus] = None # New detailed git state
|
|
101
|
+
open_tabs: Dict[str, 'TabInfo'] = None # Changed from List to Dict with unique keys
|
|
102
|
+
active_tab: Optional['TabInfo'] = None
|
|
103
|
+
|
|
104
|
+
def __post_init__(self):
|
|
105
|
+
if self.open_tabs is None:
|
|
106
|
+
self.open_tabs = {}
|
|
107
|
+
if self.monitored_folders is None:
|
|
108
|
+
self.monitored_folders = []
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""Utility functions for project state management.
|
|
2
|
+
|
|
3
|
+
This module contains shared utility functions used across the project state
|
|
4
|
+
management system, including tab key generation and other helper functions.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import hashlib
|
|
8
|
+
import uuid
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def generate_tab_key(tab_type: str, file_path: str, **kwargs) -> str:
|
|
12
|
+
"""Generate a unique key for a tab.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
tab_type: Type of tab ('file', 'diff', 'untitled', etc.)
|
|
16
|
+
file_path: Path to the file
|
|
17
|
+
**kwargs: Additional parameters for diff tabs (from_ref, to_ref, from_hash, to_hash)
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
Unique string key for the tab
|
|
21
|
+
"""
|
|
22
|
+
if tab_type == 'file':
|
|
23
|
+
return file_path
|
|
24
|
+
elif tab_type == 'diff':
|
|
25
|
+
from_ref = kwargs.get('from_ref', '')
|
|
26
|
+
to_ref = kwargs.get('to_ref', '')
|
|
27
|
+
from_hash = kwargs.get('from_hash', '')
|
|
28
|
+
to_hash = kwargs.get('to_hash', '')
|
|
29
|
+
return f"diff:{file_path}:{from_ref}:{to_ref}:{from_hash}:{to_hash}"
|
|
30
|
+
elif tab_type == 'untitled':
|
|
31
|
+
# For untitled tabs, use the tab_id as the key since they don't have a file path
|
|
32
|
+
return kwargs.get('tab_id', str(uuid.uuid4()))
|
|
33
|
+
else:
|
|
34
|
+
# For other tab types, use file_path if available, otherwise tab_id
|
|
35
|
+
return file_path if file_path else kwargs.get('tab_id', str(uuid.uuid4()))
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def generate_content_hash(content: str) -> str:
|
|
39
|
+
"""Generate SHA-256 hash of content for caching.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
content: The string content to hash
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
SHA-256 hash prefixed with 'sha256:'
|
|
46
|
+
"""
|
|
47
|
+
if content is None:
|
|
48
|
+
return None
|
|
49
|
+
|
|
50
|
+
return "sha256:" + hashlib.sha256(content.encode('utf-8')).hexdigest()
|