grasp-sdk 0.1.9__py3-none-any.whl → 0.2.0a1__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.

@@ -25,20 +25,20 @@ class CDPConnection:
25
25
  ws_url: str,
26
26
  http_url: str,
27
27
  port: int,
28
- pid: Optional[int] = None
28
+ id: str,
29
29
  ):
30
30
  self.ws_url = ws_url
31
31
  self.http_url = http_url
32
32
  self.port = port
33
- self.pid = pid
33
+ self.id = id
34
34
 
35
35
  def to_dict(self) -> Dict[str, Any]:
36
36
  """Convert to dictionary representation."""
37
37
  return {
38
+ 'id': self.id,
38
39
  'wsUrl': self.ws_url,
39
40
  'httpUrl': self.http_url,
40
- 'port': self.port,
41
- 'pid': self.pid
41
+ 'port': self.port
42
42
  }
43
43
 
44
44
 
@@ -77,6 +77,7 @@ class BrowserService:
77
77
  self.cdp_connection: Optional[CDPConnection] = None
78
78
  self.browser_process: Optional[CommandEventEmitter] = None
79
79
  self._health_check_task: Optional[asyncio.Task] = None
80
+ self.config['envs']['APIKEY'] = sandbox_config['key']
80
81
 
81
82
  def _get_default_logger(self):
82
83
  """Gets or creates a default logger instance."""
@@ -99,7 +100,6 @@ class BrowserService:
99
100
  """
100
101
  self.logger.info('Initializing Browser service')
101
102
  envs = {
102
- 'CDP_PORT': '9222',
103
103
  'BROWSER_ARGS': json.dumps(self.config['args']),
104
104
  'LAUNCH_TIMEOUT': str(self.config['launchTimeout']),
105
105
  'SANDBOX_TIMEOUT': str(self.sandbox_service.timeout),
@@ -107,16 +107,46 @@ class BrowserService:
107
107
  'NODE_ENV': 'production',
108
108
  # 'SANDBOX_ID': self.sandbox_service.id,
109
109
  'WORKSPACE': self.sandbox_service.workspace,
110
- 'BS_SOURCE_TOKEN': 'Qth8JGboEKVersqr1PSsUFMW',
111
- 'BS_INGESTING_HOST': 's1363065.eu-nbg-2.betterstackdata.com',
112
- 'SENTRY_DSN': 'https://21fa729ceb72d7f0adef06b4f786c067@o4509574910509056.ingest.us.sentry.io/4509574913720320',
110
+ 'BROWSER_TYPE': browser_type,
113
111
  **self.config['envs']
114
112
  }
115
- await self.sandbox_service.create_sandbox(f'grasp-run-{browser_type}', envs)
116
- if(self.sandbox_service.sandbox is not None):
117
- await self.sandbox_service.sandbox.files.write('/home/user/.sandbox_id', self.id)
113
+ await self.sandbox_service.create_sandbox(f'grasp-run-{browser_type}-v2', envs)
114
+ # if(self.sandbox_service.sandbox is not None):
115
+ # await self.sandbox_service.sandbox.files.write('/home/user/.sandbox_id', self.id)
118
116
  self.logger.info('Grasp sandbox initialized successfully')
119
117
 
118
+ async def connect(self, sandbox_id: str) -> CDPConnection:
119
+ """Connect to an existing sandbox.
120
+
121
+ Args:
122
+ sandbox_id: ID of the sandbox to connect to
123
+
124
+ Returns:
125
+ CDP connection information
126
+ """
127
+ self.logger.info('Initializing Browser service')
128
+ await self.sandbox_service.connect_sandbox(sandbox_id)
129
+ self.logger.info('Browser service initialized successfully')
130
+
131
+ # Read CDP connection info from file
132
+ if not self.sandbox_service.sandbox:
133
+ raise RuntimeError('Sandbox is not available')
134
+
135
+ cdp_content = await self.sandbox_service.sandbox.files.read(
136
+ '/home/user/.grasp-cdp.json'
137
+ )
138
+ cdp_data = json.loads(cdp_content)
139
+
140
+ # Create CDPConnection object
141
+ self.cdp_connection = CDPConnection(
142
+ ws_url=cdp_data['wsUrl'],
143
+ http_url=cdp_data['httpUrl'],
144
+ port=cdp_data['port'],
145
+ id=cdp_data['id']
146
+ )
147
+
148
+ return self.cdp_connection
149
+
120
150
  async def launch_browser(
121
151
  self,
122
152
  ) -> CDPConnection:
@@ -174,15 +204,22 @@ class BrowserService:
174
204
  # Create CDP connection info
175
205
  self.cdp_connection = result
176
206
 
207
+ # Write CDP connection info to file
208
+ if not self.sandbox_service.sandbox:
209
+ raise RuntimeError('Sandbox is not available')
210
+
211
+ await self.sandbox_service.sandbox.files.write(
212
+ '/home/user/.grasp-cdp.json',
213
+ json.dumps(self.cdp_connection.to_dict())
214
+ )
215
+
177
216
  self.logger.info(
178
217
  f'Chromium browser launched successfully (cdpPort: 9222, wsUrl: {self.cdp_connection.ws_url})'
179
218
  )
180
219
 
181
- # Start health check if not in debug mode
182
- if not self.sandbox_service.is_debug:
183
- self._health_check_task = asyncio.create_task(
184
- self._start_health_check()
185
- )
220
+ self._health_check_task = asyncio.create_task(
221
+ self._start_health_check()
222
+ )
186
223
 
187
224
  return self.cdp_connection
188
225
 
@@ -201,11 +238,11 @@ class BrowserService:
201
238
  def on_stderr(data: str) -> None:
202
239
  self.logger.info(f'Browser stderr: {data}')
203
240
 
204
- def on_exit(exit_code: int) -> None:
205
- self.logger.info(f'Browser process exited (exitCode: {exit_code})')
206
- self.cdp_connection = None
207
- self.browser_process = None
208
- asyncio.create_task(self.sandbox_service.destroy())
241
+ # def on_exit(exit_code: int) -> None:
242
+ # self.logger.info(f'Browser process exited (exitCode: {exit_code})')
243
+ # self.cdp_connection = None
244
+ # self.browser_process = None
245
+ # asyncio.create_task(self.sandbox_service.destroy())
209
246
 
210
247
  def on_error(error: Exception) -> None:
211
248
  self.logger.error(f'Browser process error: {error}')
@@ -214,7 +251,7 @@ class BrowserService:
214
251
  if hasattr(self.browser_process, 'on'):
215
252
  self.browser_process.on('stdout', on_stdout)
216
253
  self.browser_process.on('stderr', on_stderr)
217
- self.browser_process.on('exit', on_exit)
254
+ # self.browser_process.on('exit', on_exit)
218
255
  self.browser_process.on('error', on_error)
219
256
 
220
257
  async def _wait_for_cdp_ready(self) -> CDPConnection:
@@ -252,7 +289,7 @@ class BrowserService:
252
289
  ws_url = metadata['webSocketDebuggerUrl'].replace(
253
290
  'ws://', 'wss://'
254
291
  ).replace(
255
- f'localhost:9222', host
292
+ '127.0.0.1:9222', host
256
293
  )
257
294
 
258
295
  http_url = f'https://{host}'
@@ -260,7 +297,8 @@ class BrowserService:
260
297
  connection = CDPConnection(
261
298
  ws_url=ws_url,
262
299
  http_url=http_url,
263
- port=9222
300
+ port=9222,
301
+ id=str(self.sandbox_service.id)
264
302
  )
265
303
 
266
304
  self.logger.info(f'CDP server is ready (metadata: {metadata})')
@@ -338,8 +376,8 @@ class BrowserService:
338
376
  self._health_check_task = None
339
377
 
340
378
  # Kill the browser process
341
- if hasattr(self.browser_process, 'kill'):
342
- await self.browser_process.kill()
379
+ # if hasattr(self.browser_process, 'kill'):
380
+ # await self.browser_process.kill()
343
381
 
344
382
  self.browser_process = None
345
383
  self.cdp_connection = None
@@ -347,8 +385,8 @@ class BrowserService:
347
385
  self.logger.info('Chromium browser stopped successfully')
348
386
 
349
387
  except Exception as error:
350
- self.logger.error(f'Error stopping browser: {error}')
351
- raise
388
+ self.logger.debug(f'Error stopping browser: {error}')
389
+ # raise
352
390
 
353
391
  async def cleanup(self) -> None:
354
392
  """Cleanup all resources including Grasp sandbox.
@@ -0,0 +1,94 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Filesystem service for managing file operations in sandbox.
4
+
5
+ This module provides Python implementation of the filesystem service,
6
+ equivalent to the TypeScript version in src/services/filesystem.service.ts
7
+ """
8
+
9
+ import asyncio
10
+ from typing import Optional, Dict, Union
11
+ from ..utils.logger import get_logger, Logger
12
+ from .sandbox import SandboxService
13
+ from .browser import CDPConnection
14
+
15
+
16
+ class FileSystemService:
17
+ """
18
+ Filesystem service for managing file operations in sandbox.
19
+ """
20
+
21
+ def __init__(self, sandbox: SandboxService, connection: CDPConnection):
22
+ """Initialize filesystem service.
23
+
24
+ Args:
25
+ sandbox: The sandbox service instance
26
+ connection: The CDP connection for browser integration
27
+ """
28
+ self.sandbox = sandbox
29
+ self.connection = connection
30
+ self.logger = self._get_default_logger()
31
+
32
+ def _get_default_logger(self) -> Logger:
33
+ """Gets or creates a default logger instance.
34
+
35
+ Returns:
36
+ Logger instance
37
+ """
38
+ try:
39
+ return get_logger().child('FileSystemService')
40
+ except Exception:
41
+ # If logger is not initialized, create a default one
42
+ from ..utils.logger import Logger
43
+ default_logger = Logger({
44
+ 'level': 'debug' if self.sandbox.is_debug else 'info',
45
+ 'console': True
46
+ })
47
+ return default_logger.child('FileSystemService')
48
+
49
+ async def upload_file(self, local_path: str, remote_path: str) -> None:
50
+ """Upload file from local to sandbox.
51
+
52
+ Args:
53
+ local_path: Local file path
54
+ remote_path: Remote file path in sandbox
55
+ """
56
+ upload_result = await self.sandbox.upload_file_to_sandbox(local_path, remote_path)
57
+ return upload_result
58
+
59
+ async def download_file(self, remote_path: str, local_path: str) -> None:
60
+ """Download file from sandbox to local.
61
+
62
+ Args:
63
+ remote_path: Remote file path in sandbox
64
+ local_path: Local file path
65
+ """
66
+ download_result = await self.sandbox.copy_file_from_sandbox(remote_path, local_path)
67
+ return download_result
68
+
69
+ async def write_file(self, remote_path: str, content: Union[str, bytes]) -> None:
70
+ """Write content to file in sandbox.
71
+
72
+ Args:
73
+ remote_path: Remote file path in sandbox
74
+ content: File content to write (string or bytes)
75
+ """
76
+ write_result = await self.sandbox.write_file_to_sandbox(remote_path, content)
77
+ return write_result
78
+
79
+ async def read_file(
80
+ self,
81
+ remote_path: str,
82
+ options: Optional[Dict[str, str]] = None
83
+ ) -> Union[str, bytes]:
84
+ """Read content from file in sandbox.
85
+
86
+ Args:
87
+ remote_path: Remote file path in sandbox
88
+ options: Dictionary with 'encoding' key ('utf8', 'base64', or 'binary')
89
+
90
+ Returns:
91
+ File content as string or bytes depending on encoding
92
+ """
93
+ read_result = await self.sandbox.read_file_from_sandbox(remote_path, options)
94
+ return read_result