grasp-sdk 0.1.0__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 grasp-sdk might be problematic. Click here for more details.

grasp_sdk/__init__.py ADDED
@@ -0,0 +1,262 @@
1
+ """Grasp E2B Python SDK
2
+
3
+ A Python SDK for E2B platform providing secure command execution
4
+ and browser automation in isolated cloud environments.
5
+ """
6
+
7
+ import asyncio
8
+ import signal
9
+ import sys
10
+ from typing import Dict, Optional, Any, Literal
11
+
12
+ # Import utilities
13
+ from .utils.logger import init_logger, get_logger
14
+ from .utils.config import get_config
15
+ from .services.browser import BrowserService
16
+ from .services.sandbox import SandboxService
17
+
18
+ # Import models and types
19
+ from .models import (
20
+ ISandboxConfig,
21
+ IBrowserConfig,
22
+ ICommandOptions,
23
+ IScriptOptions,
24
+ SandboxStatus,
25
+ )
26
+
27
+ __version__ = "0.1.0"
28
+ __author__ = "Grasp Team"
29
+ __email__ = "team@grasp.dev"
30
+
31
+
32
+ class GraspServer:
33
+ """Main Grasp E2B class for browser automation."""
34
+
35
+ def __init__(self, sandbox_config: Optional[Dict[str, Any]] = None):
36
+ """Initialize GraspServer with configuration.
37
+
38
+ Args:
39
+ sandbox_config: Optional sandbox configuration overrides
40
+ """
41
+ if sandbox_config is None:
42
+ sandbox_config = {}
43
+
44
+ config = get_config()
45
+ config['sandbox'].update(sandbox_config)
46
+ self.config = config
47
+
48
+ # Initialize logger first
49
+ init_logger(config['logger'])
50
+ self.logger = get_logger().child('GraspE2B')
51
+
52
+ self.browser_service: Optional[BrowserService] = None
53
+
54
+ self.logger.info(
55
+ f'GraspE2B initialized (templateId: {config["sandbox"]["templateId"]})'
56
+ )
57
+
58
+ @property
59
+ def sandbox(self) -> Optional[SandboxService]:
60
+ """Get the underlying sandbox service.
61
+
62
+ Returns:
63
+ SandboxService instance or None
64
+ """
65
+ return self.browser_service.get_sandbox() if self.browser_service else None
66
+
67
+ def get_status(self) -> Optional[SandboxStatus]:
68
+ """Get current sandbox status.
69
+
70
+ Returns:
71
+ Sandbox status or None
72
+ """
73
+ return self.sandbox.get_status() if self.sandbox else None
74
+
75
+ def get_sandbox_id(self) -> Optional[str]:
76
+ """Get sandbox ID.
77
+
78
+ Returns:
79
+ Sandbox ID or None
80
+ """
81
+ return self.sandbox.get_sandbox_id() if self.sandbox else None
82
+
83
+ async def create_browser_task(
84
+ self,
85
+ browser_type: Literal['chrome-stable', 'chromium'] = 'chromium',
86
+ config: Optional[Dict[str, Any]] = None
87
+ ) -> Dict[str, Any]:
88
+ """Create and launch a browser task.
89
+
90
+ Args:
91
+ browser_type: Type of browser to launch
92
+ config: Browser configuration overrides
93
+
94
+ Returns:
95
+ Dictionary containing browser connection info
96
+
97
+ Raises:
98
+ RuntimeError: If browser service is already initialized
99
+ """
100
+ if self.browser_service:
101
+ raise RuntimeError('Browser service can only be initialized once')
102
+
103
+ if config is None:
104
+ config = {}
105
+
106
+ # Create base browser config
107
+ browser_config: IBrowserConfig = {
108
+ 'cdpPort': 9222,
109
+ 'headless': True,
110
+ 'launchTimeout': 30000,
111
+ 'args': [
112
+ '--disable-web-security',
113
+ '--disable-features=VizDisplayCompositor',
114
+ ],
115
+ 'envs': {},
116
+ }
117
+
118
+ # Apply user config overrides with type safety
119
+ if 'cdpPort' in config:
120
+ browser_config['cdpPort'] = config['cdpPort']
121
+ if 'headless' in config:
122
+ browser_config['headless'] = config['headless']
123
+ if 'launchTimeout' in config:
124
+ browser_config['launchTimeout'] = config['launchTimeout']
125
+ if 'args' in config:
126
+ browser_config['args'] = config['args']
127
+ if 'envs' in config:
128
+ browser_config['envs'] = config['envs']
129
+
130
+ self.browser_service = BrowserService(
131
+ self.config['sandbox'],
132
+ browser_config
133
+ )
134
+ await self.browser_service.initialize()
135
+
136
+ self.logger.info('🌐 Launching Chromium browser with CDP...')
137
+ cdp_connection = await self.browser_service.launch_browser(browser_type)
138
+
139
+ self.logger.info('✅ Browser launched successfully!')
140
+ self.logger.debug(
141
+ f'CDP Connection Info (wsUrl: {cdp_connection.ws_url}, httpUrl: {cdp_connection.http_url})'
142
+ )
143
+
144
+ return {
145
+ 'id': self.browser_service.id,
146
+ 'ws_url': cdp_connection.ws_url,
147
+ 'http_url': cdp_connection.http_url
148
+ }
149
+
150
+ async def cleanup(self) -> None:
151
+ """Cleanup resources.
152
+
153
+ Returns:
154
+ Promise that resolves when cleanup is complete
155
+ """
156
+ self.logger.info('Starting cleanup process')
157
+
158
+ try:
159
+ # Cleanup browser service
160
+ if self.browser_service:
161
+ await self.browser_service.cleanup()
162
+
163
+ self.logger.info('Cleanup completed successfully')
164
+ except Exception as error:
165
+ self.logger.error(f'Cleanup failed: {error}')
166
+ raise
167
+
168
+
169
+ # Global server registry
170
+ _servers: Dict[str, GraspServer] = {}
171
+
172
+
173
+ async def launch_browser(
174
+ options: Optional[Dict[str, Any]] = None
175
+ ) -> Dict[str, Any]:
176
+ """Launch a browser instance.
177
+
178
+ Args:
179
+ options: Launch options including type, headless, adblock settings
180
+
181
+ Returns:
182
+ Dictionary containing connection information
183
+ """
184
+ if options is None:
185
+ options = {}
186
+
187
+ # Extract browser-specific options
188
+ browser_type = options.pop('type', 'chromium')
189
+ headless = options.pop('headless', True)
190
+ adblock = options.pop('adblock', False)
191
+
192
+ # Create server instance
193
+ server = GraspServer(options)
194
+
195
+ # Create browser task
196
+ browser_config = {
197
+ 'headless': headless,
198
+ 'envs': {'ADBLOCK': 'true' if adblock else 'false'}
199
+ }
200
+
201
+ connection = await server.create_browser_task(browser_type, browser_config)
202
+
203
+ # Register server
204
+ if connection['id']:
205
+ _servers[connection['id']] = server
206
+
207
+ return connection
208
+
209
+
210
+ async def _graceful_shutdown(signal_name: str) -> None:
211
+ """Handle graceful shutdown.
212
+
213
+ Args:
214
+ signal_name: Name of the signal received
215
+ """
216
+ print(f'Received {signal_name}, starting cleanup...')
217
+
218
+ # Cleanup all GraspServer instances
219
+ for server_id in list(_servers.keys()):
220
+ await _servers[server_id].cleanup()
221
+ del _servers[server_id]
222
+
223
+ print('All servers cleaned up, exiting...')
224
+ sys.exit(0)
225
+
226
+
227
+ def _setup_signal_handlers() -> None:
228
+ """Setup signal handlers for graceful shutdown."""
229
+ def signal_handler(signum, frame):
230
+ signal_name = signal.Signals(signum).name
231
+ asyncio.create_task(_graceful_shutdown(signal_name))
232
+
233
+ # Register signal handlers
234
+ signal.signal(signal.SIGINT, signal_handler) # Ctrl+C
235
+ signal.signal(signal.SIGTERM, signal_handler) # Termination signal
236
+
237
+
238
+ # Setup signal handlers on import
239
+ _setup_signal_handlers()
240
+
241
+
242
+ # Export all public APIs
243
+ __all__ = [
244
+ 'GraspServer',
245
+ 'launch_browser',
246
+ 'ISandboxConfig',
247
+ 'IBrowserConfig',
248
+ 'ICommandOptions',
249
+ 'IScriptOptions',
250
+ 'SandboxStatus',
251
+ 'get_config',
252
+ 'init_logger',
253
+ 'get_logger',
254
+ 'BrowserService',
255
+ 'SandboxService',
256
+ ]
257
+
258
+
259
+ # Default export equivalent
260
+ default = {
261
+ 'launch_browser': launch_browser,
262
+ }
@@ -0,0 +1,78 @@
1
+ """Type definitions for Grasp SDK Python implementation.
2
+
3
+ This module contains TypedDict classes and enums that correspond to
4
+ the TypeScript interfaces in the Node.js version.
5
+ """
6
+
7
+ from typing import TypedDict, Optional, Dict, Any, List, Union
8
+ from typing_extensions import NotRequired
9
+ from enum import Enum
10
+
11
+
12
+ class SandboxStatus(Enum):
13
+ """Sandbox status enumeration."""
14
+ CREATING = "creating"
15
+ RUNNING = "running"
16
+ STOPPED = "stopped"
17
+ ERROR = "error"
18
+
19
+
20
+ class ISandboxConfig(TypedDict):
21
+ """Sandbox configuration interface."""
22
+ key: str # Required: Grasp API key
23
+ templateId: str # Required: Sandbox template ID
24
+ timeout: int # Required: Default timeout in milliseconds
25
+ workspace: NotRequired[str] # Optional: Grasp workspace ID
26
+ debug: NotRequired[bool] # Optional: Enable debug mode for detailed logging
27
+
28
+
29
+ class IBrowserConfig(TypedDict):
30
+ """Browser service configuration interface."""
31
+ cdpPort: int # Required: Port for CDP server (default: 9222)
32
+ args: List[str] # Required: Chromium launch arguments
33
+ headless: bool # Required: Headless mode (default: true)
34
+ launchTimeout: int # Required: Timeout for browser launch (default: 30000ms)
35
+ envs: Dict[str, str] # Required: The environment variables
36
+
37
+
38
+ class ICommandOptions(TypedDict):
39
+ """Command execution options interface."""
40
+ inBackground: NotRequired[bool] # Whether to run command in background
41
+ timeout: NotRequired[int] # Timeout in milliseconds
42
+ cwd: NotRequired[str] # Working directory
43
+ nohup: NotRequired[bool] # Whether to use nohup for command execution
44
+
45
+
46
+ class IScriptOptions(TypedDict):
47
+ """Script execution options interface."""
48
+ type: str # Required: Script type: 'cjs' for CommonJS, 'esm' for ES Modules
49
+ cwd: NotRequired[str] # Working directory
50
+ timeoutMs: NotRequired[int] # Timeout in milliseconds
51
+ background: NotRequired[bool] # Run in background
52
+ nohup: NotRequired[bool] # Use nohup for background execution
53
+ envs: NotRequired[Dict[str, str]] # The environment variables
54
+ preCommand: NotRequired[str] # Pre command
55
+
56
+
57
+ class ILoggerConfig(TypedDict):
58
+ """Logger configuration interface."""
59
+ level: str # Required: Log level ('debug' | 'info' | 'warn' | 'error')
60
+ console: bool # Required: Enable console output
61
+ file: NotRequired[str] # Optional: Log file path
62
+
63
+
64
+ class IAppConfig(TypedDict):
65
+ """Application configuration interface."""
66
+ sandbox: ISandboxConfig # Required: E2B configuration
67
+ logger: ILoggerConfig # Required: Logger configuration
68
+
69
+
70
+ __all__ = [
71
+ 'SandboxStatus',
72
+ 'ISandboxConfig',
73
+ 'IBrowserConfig',
74
+ 'ICommandOptions',
75
+ 'IScriptOptions',
76
+ 'ILoggerConfig',
77
+ 'IAppConfig',
78
+ ]