portacode 0.3.20.dev3__tar.gz → 0.3.20.dev4__tar.gz
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.
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/PKG-INFO +1 -1
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/portacode/_version.py +2 -2
- portacode-0.3.20.dev4/portacode/connection/handlers/project_state/__init__.py +145 -0
- portacode-0.3.20.dev4/portacode/connection/handlers/project_state/centralized_handlers.py +299 -0
- portacode-0.3.20.dev4/portacode/connection/handlers/project_state/centralized_manager.py +410 -0
- portacode-0.3.20.dev4/portacode/connection/handlers/project_state/centralized_state.py +441 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/portacode/connection/handlers/project_state/git_manager.py +6 -1
- portacode-0.3.20.dev4/portacode/connection/handlers/project_state/simplified_file_watcher.py +138 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/portacode/connection/handlers/project_state/utils.py +27 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/portacode.egg-info/PKG-INFO +1 -1
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/portacode.egg-info/SOURCES.txt +4 -0
- portacode-0.3.20.dev3/portacode/connection/handlers/project_state/__init__.py +0 -90
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/.claude/agents/communication-manager.md +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/.claude/settings.local.json +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/.gitignore +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/.gitmodules +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/LICENSE +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/MANIFEST.in +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/Makefile +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/README.md +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/backup.sh +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/docker-compose.yaml +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/portacode/README.md +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/portacode/__init__.py +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/portacode/__main__.py +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/portacode/cli.py +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/portacode/connection/README.md +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/portacode/connection/__init__.py +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/portacode/connection/client.py +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/portacode/connection/handlers/README.md +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/portacode/connection/handlers/WEBSOCKET_PROTOCOL.md +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/portacode/connection/handlers/__init__.py +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/portacode/connection/handlers/base.py +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/portacode/connection/handlers/file_handlers.py +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/portacode/connection/handlers/project_state/README.md +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/portacode/connection/handlers/project_state/file_system_watcher.py +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/portacode/connection/handlers/project_state/handlers.py +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/portacode/connection/handlers/project_state/manager.py +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/portacode/connection/handlers/project_state/models.py +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/portacode/connection/handlers/project_state_handlers.py +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/portacode/connection/handlers/registry.py +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/portacode/connection/handlers/session.py +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/portacode/connection/handlers/system_handlers.py +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/portacode/connection/handlers/tab_factory.py +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/portacode/connection/handlers/terminal_handlers.py +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/portacode/connection/multiplex.py +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/portacode/connection/terminal.py +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/portacode/data.py +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/portacode/keypair.py +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/portacode/service.py +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/portacode.egg-info/dependency_links.txt +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/portacode.egg-info/entry_points.txt +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/portacode.egg-info/requires.txt +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/portacode.egg-info/top_level.txt +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/pyproject.toml +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/restore.sh +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/run_tests.py +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/setup.cfg +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/setup.py +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/test.sh +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/test_modules/README.md +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/test_modules/__init__.py +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/test_modules/test_device_online.py +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/test_modules/test_file_operations.py +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/test_modules/test_login_flow.py +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/test_modules/test_navigate_testing_folder.py +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/test_modules/test_terminal_interaction.py +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/test_modules/test_terminal_start.py +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/testing_framework/.env.example +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/testing_framework/README.md +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/testing_framework/__init__.py +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/testing_framework/cli.py +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/testing_framework/core/__init__.py +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/testing_framework/core/base_test.py +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/testing_framework/core/cli_manager.py +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/testing_framework/core/hierarchical_runner.py +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/testing_framework/core/playwright_manager.py +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/testing_framework/core/runner.py +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/testing_framework/core/shared_cli_manager.py +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/testing_framework/core/test_discovery.py +0 -0
- {portacode-0.3.20.dev3 → portacode-0.3.20.dev4}/testing_framework/requirements.txt +0 -0
|
@@ -17,5 +17,5 @@ __version__: str
|
|
|
17
17
|
__version_tuple__: VERSION_TUPLE
|
|
18
18
|
version_tuple: VERSION_TUPLE
|
|
19
19
|
|
|
20
|
-
__version__ = version = '0.3.20.
|
|
21
|
-
__version_tuple__ = version_tuple = (0, 3, 20, '
|
|
20
|
+
__version__ = version = '0.3.20.dev4'
|
|
21
|
+
__version_tuple__ = version_tuple = (0, 3, 20, 'dev4')
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"""Project State Management Package
|
|
2
|
+
|
|
3
|
+
This package provides a modular architecture for managing project state in the
|
|
4
|
+
Portacode application, including file system monitoring, git integration,
|
|
5
|
+
tab management, and real-time state synchronization.
|
|
6
|
+
|
|
7
|
+
The package is organized into the following modules:
|
|
8
|
+
|
|
9
|
+
- models: Data structures and models (ProjectState, FileItem, TabInfo, etc.)
|
|
10
|
+
- git_manager: Git operations and repository management
|
|
11
|
+
- file_system_watcher: File system change monitoring
|
|
12
|
+
- manager: Central project state coordinator (legacy)
|
|
13
|
+
- centralized_manager: New centralized state manager with single source of truth
|
|
14
|
+
- handlers: Request handlers for various operations (legacy)
|
|
15
|
+
- centralized_handlers: New handlers using centralized state management
|
|
16
|
+
- utils: Utility functions and helpers
|
|
17
|
+
|
|
18
|
+
Usage (New Centralized System):
|
|
19
|
+
from project_state.centralized_manager import get_or_create_centralized_manager
|
|
20
|
+
from project_state.centralized_handlers import CentralizedProjectStateFolderExpandHandler
|
|
21
|
+
from project_state.centralized_state import CentralizedProjectState
|
|
22
|
+
|
|
23
|
+
Usage (Legacy System):
|
|
24
|
+
from project_state.manager import get_or_create_project_state_manager
|
|
25
|
+
from project_state.handlers import ProjectStateFolderExpandHandler
|
|
26
|
+
from project_state.models import ProjectState, FileItem
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
# Public API exports
|
|
30
|
+
from .models import (
|
|
31
|
+
ProjectState,
|
|
32
|
+
FileItem,
|
|
33
|
+
TabInfo,
|
|
34
|
+
MonitoredFolder,
|
|
35
|
+
GitFileChange,
|
|
36
|
+
GitDetailedStatus
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
# Legacy manager (for backwards compatibility)
|
|
40
|
+
from .manager import (
|
|
41
|
+
ProjectStateManager,
|
|
42
|
+
get_or_create_project_state_manager,
|
|
43
|
+
reset_global_project_state_manager,
|
|
44
|
+
debug_global_manager_state
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
# New centralized system
|
|
48
|
+
from .centralized_manager import (
|
|
49
|
+
CentralizedProjectStateManager,
|
|
50
|
+
get_or_create_centralized_manager
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
from .centralized_state import (
|
|
54
|
+
CentralizedProjectState,
|
|
55
|
+
GitStateSnapshot,
|
|
56
|
+
StateUpdateManager,
|
|
57
|
+
StateNotificationManager,
|
|
58
|
+
PeriodicGitMonitor
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
from .git_manager import GitManager
|
|
62
|
+
from .file_system_watcher import FileSystemWatcher
|
|
63
|
+
|
|
64
|
+
# Legacy handlers (for backwards compatibility)
|
|
65
|
+
from .handlers import (
|
|
66
|
+
ProjectStateFolderExpandHandler,
|
|
67
|
+
ProjectStateFolderCollapseHandler,
|
|
68
|
+
ProjectStateFileOpenHandler,
|
|
69
|
+
ProjectStateTabCloseHandler,
|
|
70
|
+
ProjectStateSetActiveTabHandler,
|
|
71
|
+
ProjectStateDiffOpenHandler,
|
|
72
|
+
ProjectStateGitStageHandler,
|
|
73
|
+
ProjectStateGitUnstageHandler,
|
|
74
|
+
ProjectStateGitRevertHandler,
|
|
75
|
+
handle_client_session_cleanup
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
# New centralized handlers
|
|
79
|
+
from .centralized_handlers import (
|
|
80
|
+
CentralizedProjectStateFolderExpandHandler,
|
|
81
|
+
CentralizedProjectStateFolderCollapseHandler,
|
|
82
|
+
CentralizedProjectStateFileOpenHandler,
|
|
83
|
+
CentralizedProjectStateTabCloseHandler,
|
|
84
|
+
CentralizedProjectStateGitStageHandler,
|
|
85
|
+
CentralizedProjectStateGitUnstageHandler,
|
|
86
|
+
CentralizedProjectStateGitRevertHandler,
|
|
87
|
+
handle_centralized_client_session_cleanup
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
from .utils import generate_tab_key, generate_tab_id
|
|
91
|
+
|
|
92
|
+
__all__ = [
|
|
93
|
+
# Models
|
|
94
|
+
'ProjectState',
|
|
95
|
+
'FileItem',
|
|
96
|
+
'TabInfo',
|
|
97
|
+
'MonitoredFolder',
|
|
98
|
+
'GitFileChange',
|
|
99
|
+
'GitDetailedStatus',
|
|
100
|
+
|
|
101
|
+
# Core classes
|
|
102
|
+
'ProjectStateManager', # Legacy
|
|
103
|
+
'CentralizedProjectStateManager', # New
|
|
104
|
+
'CentralizedProjectState', # New
|
|
105
|
+
'GitStateSnapshot', # New
|
|
106
|
+
'StateUpdateManager', # New
|
|
107
|
+
'StateNotificationManager', # New
|
|
108
|
+
'PeriodicGitMonitor', # New
|
|
109
|
+
'GitManager',
|
|
110
|
+
'FileSystemWatcher',
|
|
111
|
+
|
|
112
|
+
# Manager functions (Legacy)
|
|
113
|
+
'get_or_create_project_state_manager',
|
|
114
|
+
'reset_global_project_state_manager',
|
|
115
|
+
'debug_global_manager_state',
|
|
116
|
+
|
|
117
|
+
# Manager functions (New)
|
|
118
|
+
'get_or_create_centralized_manager',
|
|
119
|
+
|
|
120
|
+
# Legacy Handlers
|
|
121
|
+
'ProjectStateFolderExpandHandler',
|
|
122
|
+
'ProjectStateFolderCollapseHandler',
|
|
123
|
+
'ProjectStateFileOpenHandler',
|
|
124
|
+
'ProjectStateTabCloseHandler',
|
|
125
|
+
'ProjectStateSetActiveTabHandler',
|
|
126
|
+
'ProjectStateDiffOpenHandler',
|
|
127
|
+
'ProjectStateGitStageHandler',
|
|
128
|
+
'ProjectStateGitUnstageHandler',
|
|
129
|
+
'ProjectStateGitRevertHandler',
|
|
130
|
+
'handle_client_session_cleanup',
|
|
131
|
+
|
|
132
|
+
# New Centralized Handlers
|
|
133
|
+
'CentralizedProjectStateFolderExpandHandler',
|
|
134
|
+
'CentralizedProjectStateFolderCollapseHandler',
|
|
135
|
+
'CentralizedProjectStateFileOpenHandler',
|
|
136
|
+
'CentralizedProjectStateTabCloseHandler',
|
|
137
|
+
'CentralizedProjectStateGitStageHandler',
|
|
138
|
+
'CentralizedProjectStateGitUnstageHandler',
|
|
139
|
+
'CentralizedProjectStateGitRevertHandler',
|
|
140
|
+
'handle_centralized_client_session_cleanup',
|
|
141
|
+
|
|
142
|
+
# Utils
|
|
143
|
+
'generate_tab_key',
|
|
144
|
+
'generate_tab_id'
|
|
145
|
+
]
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Updated handlers that use the centralized project state manager.
|
|
3
|
+
|
|
4
|
+
These handlers replace the old handlers with clean interfaces to the
|
|
5
|
+
centralized state management system.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
from typing import Any, Dict
|
|
10
|
+
|
|
11
|
+
from ..base import AsyncHandler
|
|
12
|
+
from .centralized_manager import get_or_create_centralized_manager
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class CentralizedProjectStateFolderExpandHandler(AsyncHandler):
|
|
18
|
+
"""Handler for expanding project folders using centralized state."""
|
|
19
|
+
|
|
20
|
+
@property
|
|
21
|
+
def command_name(self) -> str:
|
|
22
|
+
return "project_state_folder_expand"
|
|
23
|
+
|
|
24
|
+
async def execute(self, message: Dict[str, Any]) -> Dict[str, Any]:
|
|
25
|
+
"""Expand a folder in project state."""
|
|
26
|
+
server_project_id = message.get("project_id")
|
|
27
|
+
folder_path = message.get("folder_path")
|
|
28
|
+
source_client_session = message.get("source_client_session")
|
|
29
|
+
|
|
30
|
+
if not server_project_id:
|
|
31
|
+
raise ValueError("project_id is required")
|
|
32
|
+
if not folder_path:
|
|
33
|
+
raise ValueError("folder_path is required")
|
|
34
|
+
if not source_client_session:
|
|
35
|
+
raise ValueError("source_client_session is required")
|
|
36
|
+
|
|
37
|
+
logger.info("Expanding folder %s for session %s", folder_path, source_client_session)
|
|
38
|
+
|
|
39
|
+
# Get centralized manager
|
|
40
|
+
manager = get_or_create_centralized_manager(self.context, self.control_channel)
|
|
41
|
+
|
|
42
|
+
# Expand folder
|
|
43
|
+
success = await manager.expand_folder(source_client_session, folder_path)
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
"event": "project_state_folder_expand_response",
|
|
47
|
+
"project_id": server_project_id,
|
|
48
|
+
"folder_path": folder_path,
|
|
49
|
+
"success": success
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class CentralizedProjectStateFolderCollapseHandler(AsyncHandler):
|
|
54
|
+
"""Handler for collapsing project folders using centralized state."""
|
|
55
|
+
|
|
56
|
+
@property
|
|
57
|
+
def command_name(self) -> str:
|
|
58
|
+
return "project_state_folder_collapse"
|
|
59
|
+
|
|
60
|
+
async def execute(self, message: Dict[str, Any]) -> Dict[str, Any]:
|
|
61
|
+
"""Collapse a folder in project state."""
|
|
62
|
+
server_project_id = message.get("project_id")
|
|
63
|
+
folder_path = message.get("folder_path")
|
|
64
|
+
source_client_session = message.get("source_client_session")
|
|
65
|
+
|
|
66
|
+
if not server_project_id:
|
|
67
|
+
raise ValueError("project_id is required")
|
|
68
|
+
if not folder_path:
|
|
69
|
+
raise ValueError("folder_path is required")
|
|
70
|
+
if not source_client_session:
|
|
71
|
+
raise ValueError("source_client_session is required")
|
|
72
|
+
|
|
73
|
+
logger.info("Collapsing folder %s for session %s", folder_path, source_client_session)
|
|
74
|
+
|
|
75
|
+
# Get centralized manager
|
|
76
|
+
manager = get_or_create_centralized_manager(self.context, self.control_channel)
|
|
77
|
+
|
|
78
|
+
# Collapse folder
|
|
79
|
+
success = await manager.collapse_folder(source_client_session, folder_path)
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
"event": "project_state_folder_collapse_response",
|
|
83
|
+
"project_id": server_project_id,
|
|
84
|
+
"folder_path": folder_path,
|
|
85
|
+
"success": success
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class CentralizedProjectStateFileOpenHandler(AsyncHandler):
|
|
90
|
+
"""Handler for opening files using centralized state."""
|
|
91
|
+
|
|
92
|
+
@property
|
|
93
|
+
def command_name(self) -> str:
|
|
94
|
+
return "project_state_file_open"
|
|
95
|
+
|
|
96
|
+
async def execute(self, message: Dict[str, Any]) -> Dict[str, Any]:
|
|
97
|
+
"""Open a file in project state."""
|
|
98
|
+
server_project_id = message.get("project_id")
|
|
99
|
+
file_path = message.get("file_path")
|
|
100
|
+
source_client_session = message.get("source_client_session")
|
|
101
|
+
set_active = message.get("set_active", True)
|
|
102
|
+
|
|
103
|
+
if not server_project_id:
|
|
104
|
+
raise ValueError("project_id is required")
|
|
105
|
+
if not file_path:
|
|
106
|
+
raise ValueError("file_path is required")
|
|
107
|
+
if not source_client_session:
|
|
108
|
+
raise ValueError("source_client_session is required")
|
|
109
|
+
|
|
110
|
+
logger.info("Opening file %s for session %s", file_path, source_client_session)
|
|
111
|
+
|
|
112
|
+
# Get centralized manager
|
|
113
|
+
manager = get_or_create_centralized_manager(self.context, self.control_channel)
|
|
114
|
+
|
|
115
|
+
# Open file
|
|
116
|
+
success = await manager.open_file_tab(source_client_session, file_path, set_active)
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
"event": "project_state_file_open_response",
|
|
120
|
+
"project_id": server_project_id,
|
|
121
|
+
"file_path": file_path,
|
|
122
|
+
"success": success,
|
|
123
|
+
"set_active": set_active
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class CentralizedProjectStateTabCloseHandler(AsyncHandler):
|
|
128
|
+
"""Handler for closing tabs using centralized state."""
|
|
129
|
+
|
|
130
|
+
@property
|
|
131
|
+
def command_name(self) -> str:
|
|
132
|
+
return "project_state_tab_close"
|
|
133
|
+
|
|
134
|
+
async def execute(self, message: Dict[str, Any]) -> Dict[str, Any]:
|
|
135
|
+
"""Close a tab in project state."""
|
|
136
|
+
server_project_id = message.get("project_id")
|
|
137
|
+
tab_id = message.get("tab_id")
|
|
138
|
+
source_client_session = message.get("source_client_session")
|
|
139
|
+
|
|
140
|
+
if not server_project_id:
|
|
141
|
+
raise ValueError("project_id is required")
|
|
142
|
+
if not tab_id:
|
|
143
|
+
raise ValueError("tab_id is required")
|
|
144
|
+
if not source_client_session:
|
|
145
|
+
raise ValueError("source_client_session is required")
|
|
146
|
+
|
|
147
|
+
logger.info("Closing tab %s for session %s", tab_id, source_client_session)
|
|
148
|
+
|
|
149
|
+
# Get centralized manager
|
|
150
|
+
manager = get_or_create_centralized_manager(self.context, self.control_channel)
|
|
151
|
+
|
|
152
|
+
# Close tab
|
|
153
|
+
success = await manager.close_tab(source_client_session, tab_id)
|
|
154
|
+
|
|
155
|
+
return {
|
|
156
|
+
"event": "project_state_tab_close_response",
|
|
157
|
+
"project_id": server_project_id,
|
|
158
|
+
"tab_id": tab_id,
|
|
159
|
+
"success": success
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
class CentralizedProjectStateGitStageHandler(AsyncHandler):
|
|
164
|
+
"""Handler for staging files using centralized state."""
|
|
165
|
+
|
|
166
|
+
@property
|
|
167
|
+
def command_name(self) -> str:
|
|
168
|
+
return "project_state_git_stage"
|
|
169
|
+
|
|
170
|
+
async def execute(self, message: Dict[str, Any]) -> Dict[str, Any]:
|
|
171
|
+
"""Stage a file in git."""
|
|
172
|
+
server_project_id = message.get("project_id")
|
|
173
|
+
file_path = message.get("file_path")
|
|
174
|
+
source_client_session = message.get("source_client_session")
|
|
175
|
+
|
|
176
|
+
if not server_project_id:
|
|
177
|
+
raise ValueError("project_id is required")
|
|
178
|
+
if not file_path:
|
|
179
|
+
raise ValueError("file_path is required")
|
|
180
|
+
if not source_client_session:
|
|
181
|
+
raise ValueError("source_client_session is required")
|
|
182
|
+
|
|
183
|
+
logger.info("Staging file %s for session %s", file_path, source_client_session)
|
|
184
|
+
|
|
185
|
+
# Get centralized manager
|
|
186
|
+
manager = get_or_create_centralized_manager(self.context, self.control_channel)
|
|
187
|
+
|
|
188
|
+
# Stage file
|
|
189
|
+
success = await manager.stage_file(source_client_session, file_path)
|
|
190
|
+
|
|
191
|
+
return {
|
|
192
|
+
"event": "project_state_git_stage_response",
|
|
193
|
+
"project_id": server_project_id,
|
|
194
|
+
"file_path": file_path,
|
|
195
|
+
"success": success
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
class CentralizedProjectStateGitUnstageHandler(AsyncHandler):
|
|
200
|
+
"""Handler for unstaging files using centralized state."""
|
|
201
|
+
|
|
202
|
+
@property
|
|
203
|
+
def command_name(self) -> str:
|
|
204
|
+
return "project_state_git_unstage"
|
|
205
|
+
|
|
206
|
+
async def execute(self, message: Dict[str, Any]) -> Dict[str, Any]:
|
|
207
|
+
"""Unstage a file in git."""
|
|
208
|
+
server_project_id = message.get("project_id")
|
|
209
|
+
file_path = message.get("file_path")
|
|
210
|
+
source_client_session = message.get("source_client_session")
|
|
211
|
+
|
|
212
|
+
if not server_project_id:
|
|
213
|
+
raise ValueError("project_id is required")
|
|
214
|
+
if not file_path:
|
|
215
|
+
raise ValueError("file_path is required")
|
|
216
|
+
if not source_client_session:
|
|
217
|
+
raise ValueError("source_client_session is required")
|
|
218
|
+
|
|
219
|
+
logger.info("Unstaging file %s for session %s", file_path, source_client_session)
|
|
220
|
+
|
|
221
|
+
# Get centralized manager
|
|
222
|
+
manager = get_or_create_centralized_manager(self.context, self.control_channel)
|
|
223
|
+
|
|
224
|
+
# Unstage file
|
|
225
|
+
success = await manager.unstage_file(source_client_session, file_path)
|
|
226
|
+
|
|
227
|
+
return {
|
|
228
|
+
"event": "project_state_git_unstage_response",
|
|
229
|
+
"project_id": server_project_id,
|
|
230
|
+
"file_path": file_path,
|
|
231
|
+
"success": success
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
class CentralizedProjectStateGitRevertHandler(AsyncHandler):
|
|
236
|
+
"""Handler for reverting files using centralized state."""
|
|
237
|
+
|
|
238
|
+
@property
|
|
239
|
+
def command_name(self) -> str:
|
|
240
|
+
return "project_state_git_revert"
|
|
241
|
+
|
|
242
|
+
async def execute(self, message: Dict[str, Any]) -> Dict[str, Any]:
|
|
243
|
+
"""Revert a file in git."""
|
|
244
|
+
server_project_id = message.get("project_id")
|
|
245
|
+
file_path = message.get("file_path")
|
|
246
|
+
source_client_session = message.get("source_client_session")
|
|
247
|
+
|
|
248
|
+
if not server_project_id:
|
|
249
|
+
raise ValueError("project_id is required")
|
|
250
|
+
if not file_path:
|
|
251
|
+
raise ValueError("file_path is required")
|
|
252
|
+
if not source_client_session:
|
|
253
|
+
raise ValueError("source_client_session is required")
|
|
254
|
+
|
|
255
|
+
logger.info("Reverting file %s for session %s", file_path, source_client_session)
|
|
256
|
+
|
|
257
|
+
# Get centralized manager
|
|
258
|
+
manager = get_or_create_centralized_manager(self.context, self.control_channel)
|
|
259
|
+
|
|
260
|
+
# Revert file
|
|
261
|
+
success = await manager.revert_file(source_client_session, file_path)
|
|
262
|
+
|
|
263
|
+
return {
|
|
264
|
+
"event": "project_state_git_revert_response",
|
|
265
|
+
"project_id": server_project_id,
|
|
266
|
+
"file_path": file_path,
|
|
267
|
+
"success": success
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
# Handler for explicit client session cleanup
|
|
272
|
+
async def handle_centralized_client_session_cleanup(handler, payload: Dict[str, Any],
|
|
273
|
+
source_client_session: str) -> Dict[str, Any]:
|
|
274
|
+
"""Handle cleanup of a client session using centralized manager."""
|
|
275
|
+
client_session_id = payload.get('client_session_id')
|
|
276
|
+
|
|
277
|
+
if not client_session_id:
|
|
278
|
+
logger.error("client_session_id is required for client session cleanup")
|
|
279
|
+
return {
|
|
280
|
+
"event": "client_session_cleanup_response",
|
|
281
|
+
"success": False,
|
|
282
|
+
"error": "client_session_id is required"
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
logger.info("Handling centralized cleanup for client session: %s", client_session_id)
|
|
286
|
+
|
|
287
|
+
# Get centralized manager
|
|
288
|
+
manager = get_or_create_centralized_manager(handler.context, handler.control_channel)
|
|
289
|
+
|
|
290
|
+
# Clean up the client session
|
|
291
|
+
await manager.cleanup_project_state(client_session_id)
|
|
292
|
+
|
|
293
|
+
logger.info("Centralized client session cleanup completed: %s", client_session_id)
|
|
294
|
+
|
|
295
|
+
return {
|
|
296
|
+
"event": "client_session_cleanup_response",
|
|
297
|
+
"client_session_id": client_session_id,
|
|
298
|
+
"success": True
|
|
299
|
+
}
|