Revert "chore: Remove dss-claude-plugin directory"
Some checks failed
DSS Project Analysis / dss-context-update (push) Has been cancelled
Some checks failed
DSS Project Analysis / dss-context-update (push) Has been cancelled
This reverts commit 72cb7319f5.
This commit is contained in:
181
dss-claude-plugin/core/context.py
Normal file
181
dss-claude-plugin/core/context.py
Normal file
@@ -0,0 +1,181 @@
|
||||
"""
|
||||
DSS Context Module
|
||||
==================
|
||||
|
||||
Singleton context manager for the DSS Plugin.
|
||||
Handles configuration loading, mode detection, and strategy instantiation.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
from typing import Optional, Dict, Any
|
||||
|
||||
from .config import DSSConfig, DSSMode
|
||||
|
||||
# Logger setup
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Protocol/Type placeholder for Strategies (to be replaced by base class in next steps)
|
||||
Strategy = Any
|
||||
|
||||
|
||||
class DSSContext:
|
||||
"""
|
||||
Singleton context manager for the DSS Plugin.
|
||||
|
||||
Handles configuration loading, mode detection (Local/Remote),
|
||||
and strategy instantiation.
|
||||
"""
|
||||
_instance: Optional['DSSContext'] = None
|
||||
_lock: asyncio.Lock = asyncio.Lock()
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""
|
||||
Private initializer. Use get_instance() instead.
|
||||
"""
|
||||
if DSSContext._instance is not None:
|
||||
raise RuntimeError("DSSContext is a singleton. Use get_instance() to access it.")
|
||||
|
||||
self.config: Optional[DSSConfig] = None
|
||||
self.active_mode: DSSMode = DSSMode.REMOTE # Default safe fallback
|
||||
self._capabilities: Dict[str, bool] = {}
|
||||
self._strategy_cache: Dict[str, Strategy] = {}
|
||||
self.session_id: Optional[str] = None
|
||||
|
||||
@classmethod
|
||||
async def get_instance(cls) -> 'DSSContext':
|
||||
"""
|
||||
Async factory method to get the singleton instance.
|
||||
Ensures config is loaded and mode is detected before returning.
|
||||
"""
|
||||
if not cls._instance:
|
||||
async with cls._lock:
|
||||
# Double-check locking pattern
|
||||
if not cls._instance:
|
||||
instance = cls()
|
||||
await instance._initialize()
|
||||
cls._instance = instance
|
||||
|
||||
return cls._instance
|
||||
|
||||
@classmethod
|
||||
def reset(cls) -> None:
|
||||
"""
|
||||
Resets the singleton instance. Useful for testing.
|
||||
"""
|
||||
cls._instance = None
|
||||
|
||||
async def _initialize(self) -> None:
|
||||
"""
|
||||
Internal initialization logic:
|
||||
1. Load Config
|
||||
2. Detect Mode
|
||||
3. Cache Capabilities
|
||||
"""
|
||||
try:
|
||||
# 1. Load Configuration
|
||||
self.config = DSSConfig.load()
|
||||
self.session_id = self.config.session_id
|
||||
|
||||
# 2. Detect Mode (Async check)
|
||||
self.active_mode = await self.config.get_active_mode()
|
||||
|
||||
logger.info(f"DSSContext initialized. Mode: {self.active_mode.value}, Session: {self.session_id}")
|
||||
|
||||
# 3. Cache Capabilities
|
||||
self._cache_capabilities()
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to initialize DSSContext: {e}")
|
||||
# Fallback to defaults if initialization fails
|
||||
self.active_mode = DSSMode.REMOTE
|
||||
self._capabilities = {"limited": True}
|
||||
|
||||
def _cache_capabilities(self) -> None:
|
||||
"""
|
||||
Determines what the plugin can do based on the active mode.
|
||||
"""
|
||||
# Base capabilities
|
||||
caps = {
|
||||
"can_read_files": False,
|
||||
"can_execute_browser": False,
|
||||
"can_screenshot": False,
|
||||
"can_connect_remote": True
|
||||
}
|
||||
|
||||
if self.active_mode == DSSMode.LOCAL:
|
||||
# Local mode allows direct filesystem access and local browser control
|
||||
caps["can_read_files"] = True
|
||||
caps["can_execute_browser"] = True
|
||||
caps["can_screenshot"] = True
|
||||
elif self.active_mode == DSSMode.REMOTE:
|
||||
# Remote mode relies on API capabilities
|
||||
# Depending on remote configuration, these might differ
|
||||
caps["can_execute_browser"] = False # Typically restricted in pure remote unless via API
|
||||
caps["can_read_files"] = False # Security restriction
|
||||
|
||||
self._capabilities = caps
|
||||
|
||||
def get_capability(self, key: str) -> bool:
|
||||
"""Check if a specific capability is active."""
|
||||
return self._capabilities.get(key, False)
|
||||
|
||||
def get_api_url(self) -> str:
|
||||
"""Get the correct API URL for the current mode."""
|
||||
if self.config is None:
|
||||
return "https://dss.overbits.luz.uy" # Default fallback
|
||||
return self.config.get_api_url(self.active_mode)
|
||||
|
||||
def get_strategy(self, strategy_type: str) -> Any:
|
||||
"""
|
||||
Factory method to retrieve operational strategies.
|
||||
|
||||
Args:
|
||||
strategy_type: One of 'browser', 'filesystem', 'screenshot'
|
||||
|
||||
Returns:
|
||||
An instance of the requested strategy.
|
||||
"""
|
||||
# Return cached strategy if available
|
||||
if strategy_type in self._strategy_cache:
|
||||
return self._strategy_cache[strategy_type]
|
||||
|
||||
strategy_instance = None
|
||||
|
||||
# NOTE: Strategy classes will be implemented in the next step.
|
||||
# We use local imports here to avoid circular dependency issues
|
||||
# if strategies define their own types using DSSContext.
|
||||
|
||||
try:
|
||||
if strategy_type == "browser":
|
||||
# Will be implemented in Phase 2 & 3
|
||||
if self.active_mode == DSSMode.LOCAL:
|
||||
from ..strategies.local.browser import LocalBrowserStrategy
|
||||
strategy_instance = LocalBrowserStrategy(self)
|
||||
else:
|
||||
from ..strategies.remote.browser import RemoteBrowserStrategy
|
||||
strategy_instance = RemoteBrowserStrategy(self)
|
||||
|
||||
elif strategy_type == "filesystem":
|
||||
# Will be implemented in Phase 2
|
||||
if self.active_mode == DSSMode.LOCAL:
|
||||
from ..strategies.local.filesystem import LocalFilesystemStrategy
|
||||
strategy_instance = LocalFilesystemStrategy(self)
|
||||
else:
|
||||
from ..strategies.remote.filesystem import RemoteFilesystemStrategy
|
||||
strategy_instance = RemoteFilesystemStrategy(self)
|
||||
|
||||
elif strategy_type == "screenshot":
|
||||
# Screenshot is part of browser strategy
|
||||
return self.get_strategy("browser")
|
||||
|
||||
else:
|
||||
raise ValueError(f"Unknown strategy type: {strategy_type}")
|
||||
|
||||
except ImportError as e:
|
||||
logger.error(f"Failed to import strategy {strategy_type}: {e}")
|
||||
raise NotImplementedError(f"Strategy {strategy_type} not yet implemented") from e
|
||||
|
||||
# Cache and return
|
||||
self._strategy_cache[strategy_type] = strategy_instance
|
||||
return strategy_instance
|
||||
Reference in New Issue
Block a user