Revert "chore: Remove dss-claude-plugin directory"
Some checks failed
DSS Project Analysis / dss-context-update (push) Has been cancelled

This reverts commit 72cb7319f5.
This commit is contained in:
2025-12-10 15:54:39 -03:00
parent 72cb7319f5
commit 4de266de61
50 changed files with 10243 additions and 0 deletions

View 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