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.

Files changed (92) hide show
  1. portacode/_version.py +16 -3
  2. portacode/cli.py +143 -17
  3. portacode/connection/client.py +149 -10
  4. portacode/connection/handlers/WEBSOCKET_PROTOCOL.md +824 -21
  5. portacode/connection/handlers/__init__.py +28 -1
  6. portacode/connection/handlers/base.py +78 -16
  7. portacode/connection/handlers/chunked_content.py +244 -0
  8. portacode/connection/handlers/diff_handlers.py +603 -0
  9. portacode/connection/handlers/file_handlers.py +902 -17
  10. portacode/connection/handlers/project_aware_file_handlers.py +226 -0
  11. portacode/connection/handlers/project_state/README.md +312 -0
  12. portacode/connection/handlers/project_state/__init__.py +92 -0
  13. portacode/connection/handlers/project_state/file_system_watcher.py +179 -0
  14. portacode/connection/handlers/project_state/git_manager.py +1502 -0
  15. portacode/connection/handlers/project_state/handlers.py +875 -0
  16. portacode/connection/handlers/project_state/manager.py +1331 -0
  17. portacode/connection/handlers/project_state/models.py +108 -0
  18. portacode/connection/handlers/project_state/utils.py +50 -0
  19. portacode/connection/handlers/project_state_handlers.py +45 -2185
  20. portacode/connection/handlers/proxmox_infra.py +361 -0
  21. portacode/connection/handlers/registry.py +15 -4
  22. portacode/connection/handlers/session.py +483 -32
  23. portacode/connection/handlers/system_handlers.py +147 -8
  24. portacode/connection/handlers/tab_factory.py +53 -46
  25. portacode/connection/handlers/terminal_handlers.py +21 -8
  26. portacode/connection/handlers/update_handler.py +61 -0
  27. portacode/connection/multiplex.py +60 -2
  28. portacode/connection/terminal.py +214 -24
  29. portacode/keypair.py +63 -1
  30. portacode/link_capture/__init__.py +38 -0
  31. portacode/link_capture/__pycache__/__init__.cpython-311.pyc +0 -0
  32. portacode/link_capture/bin/__pycache__/link_capture_wrapper.cpython-311.pyc +0 -0
  33. portacode/link_capture/bin/elinks +3 -0
  34. portacode/link_capture/bin/gio-open +3 -0
  35. portacode/link_capture/bin/gnome-open +3 -0
  36. portacode/link_capture/bin/gvfs-open +3 -0
  37. portacode/link_capture/bin/kde-open +3 -0
  38. portacode/link_capture/bin/kfmclient +3 -0
  39. portacode/link_capture/bin/link_capture_exec.sh +11 -0
  40. portacode/link_capture/bin/link_capture_wrapper.py +75 -0
  41. portacode/link_capture/bin/links +3 -0
  42. portacode/link_capture/bin/links2 +3 -0
  43. portacode/link_capture/bin/lynx +3 -0
  44. portacode/link_capture/bin/mate-open +3 -0
  45. portacode/link_capture/bin/netsurf +3 -0
  46. portacode/link_capture/bin/sensible-browser +3 -0
  47. portacode/link_capture/bin/w3m +3 -0
  48. portacode/link_capture/bin/x-www-browser +3 -0
  49. portacode/link_capture/bin/xdg-open +3 -0
  50. portacode/logging_categories.py +140 -0
  51. portacode/pairing.py +103 -0
  52. portacode/static/js/test-ntp-clock.html +63 -0
  53. portacode/static/js/utils/ntp-clock.js +232 -0
  54. portacode/utils/NTP_ARCHITECTURE.md +136 -0
  55. portacode/utils/__init__.py +1 -0
  56. portacode/utils/diff_apply.py +456 -0
  57. portacode/utils/diff_renderer.py +371 -0
  58. portacode/utils/ntp_clock.py +65 -0
  59. portacode-1.4.11.dev1.dist-info/METADATA +298 -0
  60. portacode-1.4.11.dev1.dist-info/RECORD +97 -0
  61. {portacode-0.3.19.dev4.dist-info → portacode-1.4.11.dev1.dist-info}/WHEEL +1 -1
  62. portacode-1.4.11.dev1.dist-info/top_level.txt +3 -0
  63. test_modules/README.md +296 -0
  64. test_modules/__init__.py +1 -0
  65. test_modules/test_device_online.py +44 -0
  66. test_modules/test_file_operations.py +743 -0
  67. test_modules/test_git_status_ui.py +370 -0
  68. test_modules/test_login_flow.py +50 -0
  69. test_modules/test_navigate_testing_folder.py +361 -0
  70. test_modules/test_play_store_screenshots.py +294 -0
  71. test_modules/test_terminal_buffer_performance.py +261 -0
  72. test_modules/test_terminal_interaction.py +80 -0
  73. test_modules/test_terminal_loading_race_condition.py +95 -0
  74. test_modules/test_terminal_start.py +56 -0
  75. testing_framework/.env.example +21 -0
  76. testing_framework/README.md +334 -0
  77. testing_framework/__init__.py +17 -0
  78. testing_framework/cli.py +326 -0
  79. testing_framework/core/__init__.py +1 -0
  80. testing_framework/core/base_test.py +336 -0
  81. testing_framework/core/cli_manager.py +177 -0
  82. testing_framework/core/hierarchical_runner.py +577 -0
  83. testing_framework/core/playwright_manager.py +520 -0
  84. testing_framework/core/runner.py +447 -0
  85. testing_framework/core/shared_cli_manager.py +234 -0
  86. testing_framework/core/test_discovery.py +112 -0
  87. testing_framework/requirements.txt +12 -0
  88. portacode-0.3.19.dev4.dist-info/METADATA +0 -241
  89. portacode-0.3.19.dev4.dist-info/RECORD +0 -30
  90. portacode-0.3.19.dev4.dist-info/top_level.txt +0 -1
  91. {portacode-0.3.19.dev4.dist-info → portacode-1.4.11.dev1.dist-info}/entry_points.txt +0 -0
  92. {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()