grasp-sdk 0.1.7__tar.gz → 0.1.9__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.

Potentially problematic release.


This version of grasp-sdk might be problematic. Click here for more details.

Files changed (30) hide show
  1. {grasp_sdk-0.1.7/grasp_sdk.egg-info → grasp_sdk-0.1.9}/PKG-INFO +1 -1
  2. {grasp_sdk-0.1.7 → grasp_sdk-0.1.9}/grasp_sdk/__init__.py +3 -3
  3. {grasp_sdk-0.1.7 → grasp_sdk-0.1.9}/grasp_sdk/services/browser.py +53 -73
  4. {grasp_sdk-0.1.7 → grasp_sdk-0.1.9}/grasp_sdk/services/sandbox.py +16 -19
  5. {grasp_sdk-0.1.7 → grasp_sdk-0.1.9/grasp_sdk.egg-info}/PKG-INFO +1 -1
  6. {grasp_sdk-0.1.7 → grasp_sdk-0.1.9}/grasp_sdk.egg-info/SOURCES.txt +0 -3
  7. {grasp_sdk-0.1.7 → grasp_sdk-0.1.9}/pyproject.toml +1 -1
  8. grasp_sdk-0.1.7/grasp_sdk/sandbox/chrome-stable.mjs +0 -424
  9. grasp_sdk-0.1.7/grasp_sdk/sandbox/chromium.mjs +0 -395
  10. grasp_sdk-0.1.7/grasp_sdk/sandbox/http-proxy.mjs +0 -322
  11. {grasp_sdk-0.1.7 → grasp_sdk-0.1.9}/MANIFEST.in +0 -0
  12. {grasp_sdk-0.1.7 → grasp_sdk-0.1.9}/README.md +0 -0
  13. {grasp_sdk-0.1.7 → grasp_sdk-0.1.9}/build_and_publish.py +0 -0
  14. {grasp_sdk-0.1.7 → grasp_sdk-0.1.9}/example_usage.py +0 -0
  15. {grasp_sdk-0.1.7 → grasp_sdk-0.1.9}/grasp_sdk/models/__init__.py +0 -0
  16. {grasp_sdk-0.1.7 → grasp_sdk-0.1.9}/grasp_sdk/services/__init__.py +0 -0
  17. {grasp_sdk-0.1.7 → grasp_sdk-0.1.9}/grasp_sdk/utils/__init__.py +0 -0
  18. {grasp_sdk-0.1.7 → grasp_sdk-0.1.9}/grasp_sdk/utils/auth.py +0 -0
  19. {grasp_sdk-0.1.7 → grasp_sdk-0.1.9}/grasp_sdk/utils/config.py +0 -0
  20. {grasp_sdk-0.1.7 → grasp_sdk-0.1.9}/grasp_sdk/utils/logger.py +0 -0
  21. {grasp_sdk-0.1.7 → grasp_sdk-0.1.9}/grasp_sdk.egg-info/dependency_links.txt +0 -0
  22. {grasp_sdk-0.1.7 → grasp_sdk-0.1.9}/grasp_sdk.egg-info/entry_points.txt +0 -0
  23. {grasp_sdk-0.1.7 → grasp_sdk-0.1.9}/grasp_sdk.egg-info/not-zip-safe +0 -0
  24. {grasp_sdk-0.1.7 → grasp_sdk-0.1.9}/grasp_sdk.egg-info/requires.txt +0 -0
  25. {grasp_sdk-0.1.7 → grasp_sdk-0.1.9}/grasp_sdk.egg-info/top_level.txt +0 -0
  26. {grasp_sdk-0.1.7 → grasp_sdk-0.1.9}/py.typed +0 -0
  27. {grasp_sdk-0.1.7 → grasp_sdk-0.1.9}/requirements.txt +0 -0
  28. {grasp_sdk-0.1.7 → grasp_sdk-0.1.9}/setup.cfg +0 -0
  29. {grasp_sdk-0.1.7 → grasp_sdk-0.1.9}/setup.py +0 -0
  30. {grasp_sdk-0.1.7 → grasp_sdk-0.1.9}/test_install.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: grasp_sdk
3
- Version: 0.1.7
3
+ Version: 0.1.9
4
4
  Summary: Python SDK for Grasp E2B - Browser automation and sandbox management
5
5
  Home-page: https://github.com/grasp-team/grasp-e2b
6
6
  Author: Grasp Team
@@ -24,7 +24,7 @@ from .models import (
24
24
  SandboxStatus,
25
25
  )
26
26
 
27
- __version__ = "0.1.7"
27
+ __version__ = "0.1.9"
28
28
  __author__ = "Grasp Team"
29
29
  __email__ = "team@grasp.dev"
30
30
 
@@ -168,7 +168,7 @@ class GraspServer:
168
168
  self.config['sandbox'],
169
169
  browser_config
170
170
  )
171
- await self.browser_service.initialize()
171
+ await self.browser_service.initialize(browser_type)
172
172
 
173
173
  # Register server
174
174
  _servers[str(self.browser_service.id)] = self
@@ -177,7 +177,7 @@ class GraspServer:
177
177
  })
178
178
 
179
179
  self.logger.info('🌐 Launching Chromium browser with CDP...')
180
- cdp_connection = await self.browser_service.launch_browser(browser_type)
180
+ cdp_connection = await self.browser_service.launch_browser()
181
181
 
182
182
  self.logger.info('✅ Browser launched successfully!')
183
183
  self.logger.debug(
@@ -91,19 +91,34 @@ class BrowserService:
91
91
  })
92
92
  return default_logger.child('BrowserService')
93
93
 
94
- async def initialize(self) -> None:
94
+ async def initialize(self, browser_type: str) -> None:
95
95
  """Initialize the Grasp sandbox.
96
96
 
97
97
  Returns:
98
98
  Promise that resolves when sandbox is ready
99
99
  """
100
100
  self.logger.info('Initializing Browser service')
101
- await self.sandbox_service.create_sandbox('grasp-run-template')
101
+ envs = {
102
+ 'CDP_PORT': '9222',
103
+ 'BROWSER_ARGS': json.dumps(self.config['args']),
104
+ 'LAUNCH_TIMEOUT': str(self.config['launchTimeout']),
105
+ 'SANDBOX_TIMEOUT': str(self.sandbox_service.timeout),
106
+ 'HEADLESS': str(self.config['headless']).lower(),
107
+ 'NODE_ENV': 'production',
108
+ # 'SANDBOX_ID': self.sandbox_service.id,
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',
113
+ **self.config['envs']
114
+ }
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)
102
118
  self.logger.info('Grasp sandbox initialized successfully')
103
119
 
104
120
  async def launch_browser(
105
121
  self,
106
- browser_type: str = 'chromium'
107
122
  ) -> CDPConnection:
108
123
  """Launch Chromium browser with CDP server.
109
124
 
@@ -123,41 +138,16 @@ class BrowserService:
123
138
  self.logger.info(
124
139
  f'Launching Chromium browser with CDP (port: 9222, headless: {self.config["headless"]})')
125
140
 
126
-
127
- # Check if adblock is enabled and adjust browser type
128
- if (
129
- self.config['envs'].get('ADBLOCK') == 'true' and
130
- browser_type == 'chromium'
131
- ):
132
- self.logger.warn(
133
- '⚠️ Adblock is enabled. Should use chrome-stable instead.'
134
- )
135
- browser_type = 'chrome-stable'
136
-
137
141
  # Read the Playwright script
138
- script_path = Path(__file__).parent.parent / 'sandbox' / 'http-proxy.mjs'
142
+ # script_path = Path(__file__).parent.parent / 'sandbox' / 'http-proxy.mjs'
139
143
 
140
- try:
141
- with open(script_path, 'r', encoding='utf-8') as f:
142
- playwright_script = f.read()
143
- except FileNotFoundError:
144
- raise RuntimeError(f'Browser script not found: {script_path}')
145
-
146
- # Prepare environment variables
147
- envs = {
148
- 'CDP_PORT': '9222',
149
- 'BROWSER_ARGS': json.dumps(self.config['args']),
150
- 'LAUNCH_TIMEOUT': str(self.config['launchTimeout']),
151
- 'SANDBOX_TIMEOUT': str(self.sandbox_service.timeout),
152
- 'HEADLESS': str(self.config['headless']).lower(),
153
- 'NODE_ENV': 'production',
154
- 'SANDBOX_ID': self.sandbox_service.id,
155
- 'WORKSPACE': self.sandbox_service.workspace,
156
- 'BS_SOURCE_TOKEN': 'Qth8JGboEKVersqr1PSsUFMW',
157
- 'BS_INGESTING_HOST': 's1363065.eu-nbg-2.betterstackdata.com',
158
- 'SENTRY_DSN': 'https://21fa729ceb72d7f0adef06b4f786c067@o4509574910509056.ingest.us.sentry.io/4509574913720320',
159
- **self.config['envs']
160
- }
144
+ # try:
145
+ # with open(script_path, 'r', encoding='utf-8') as f:
146
+ # playwright_script = f.read()
147
+ # except FileNotFoundError:
148
+ # raise RuntimeError(f'Browser script not found: {script_path}')
149
+
150
+ playwright_script = '/home/user/http-proxy.js'
161
151
 
162
152
  # Prepare script options
163
153
  from ..models import IScriptOptions
@@ -166,8 +156,7 @@ class BrowserService:
166
156
  'background': True,
167
157
  'nohup': not self.sandbox_service.is_debug,
168
158
  'timeoutMs': 0,
169
- 'envs': envs,
170
- 'preCommand': '' if self.config['headless'] else 'xvfb-run -a -s "-screen 0 1280x1024x24" '
159
+ 'preCommand': ''
171
160
  }
172
161
 
173
162
  # Run the Playwright script in background
@@ -237,7 +226,7 @@ class BrowserService:
237
226
  Raises:
238
227
  RuntimeError: If CDP server fails to become ready within timeout
239
228
  """
240
- delay_ms = 100
229
+ delay_ms = 50
241
230
  max_attempts = self.config['launchTimeout'] // delay_ms
242
231
 
243
232
  for attempt in range(1, max_attempts + 1):
@@ -251,40 +240,31 @@ class BrowserService:
251
240
  )
252
241
 
253
242
  # Check if CDP endpoint is responding
254
- options: ICommandOptions = {
255
- 'timeout': 0,
256
- 'inBackground': False
257
- }
258
- result = await self.sandbox_service.run_command(
259
- f'curl -s https://{host}/json/version',
260
- options,
261
- True,
262
- )
263
-
264
- if (
265
- getattr(result, 'exit_code', None) == 0 and
266
- 'Browser' in getattr(result, 'stdout', '')
267
- ):
268
- stdout_content = getattr(result, 'stdout', '')
269
- metadata = json.loads(stdout_content)
270
-
271
- # Update URLs for external access
272
- ws_url = metadata['webSocketDebuggerUrl'].replace(
273
- 'ws://', 'wss://'
274
- ).replace(
275
- f'localhost:9222', host
276
- )
277
-
278
- http_url = f'https://{host}'
279
-
280
- connection = CDPConnection(
281
- ws_url=ws_url,
282
- http_url=http_url,
283
- port=9222
284
- )
285
-
286
- self.logger.info(f'CDP server is ready (metadata: {metadata})')
287
- return connection
243
+ async with aiohttp.ClientSession() as session:
244
+ async with session.get(f'https://{host}/json/version') as response:
245
+ if response.status == 200:
246
+ response_text = await response.text()
247
+ if 'Browser' in response_text:
248
+ stdout_content = response_text
249
+ metadata = json.loads(stdout_content)
250
+
251
+ # Update URLs for external access
252
+ ws_url = metadata['webSocketDebuggerUrl'].replace(
253
+ 'ws://', 'wss://'
254
+ ).replace(
255
+ f'localhost:9222', host
256
+ )
257
+
258
+ http_url = f'https://{host}'
259
+
260
+ connection = CDPConnection(
261
+ ws_url=ws_url,
262
+ http_url=http_url,
263
+ port=9222
264
+ )
265
+
266
+ self.logger.info(f'CDP server is ready (metadata: {metadata})')
267
+ return connection
288
268
 
289
269
  except Exception as error:
290
270
  self.logger.debug(
@@ -123,7 +123,7 @@ class SandboxService:
123
123
  """Get timeout value."""
124
124
  return self.config['timeout']
125
125
 
126
- async def create_sandbox(self, template_id: str) -> None:
126
+ async def create_sandbox(self, template_id: str, envs: Optional[Dict[str, str]] = None) -> None:
127
127
  """
128
128
  Creates and starts a new sandbox.
129
129
 
@@ -149,7 +149,8 @@ class SandboxService:
149
149
  self.sandbox = await AsyncSandbox.create(
150
150
  template=template_id,
151
151
  api_key=api_key,
152
- timeout=self.config['timeout'] // 1000 # Convert ms to seconds
152
+ timeout=self.config['timeout'] // 1000, # Convert ms to seconds
153
+ envs=envs,
153
154
  )
154
155
 
155
156
  self.status = SandboxStatus.RUNNING
@@ -322,7 +323,7 @@ class SandboxService:
322
323
  Runs JavaScript code in the sandbox.
323
324
 
324
325
  Args:
325
- code: JavaScript code to execute
326
+ code: JavaScript code to execute, or file path starting with '/home/user/'
326
327
  options: Script execution options
327
328
 
328
329
  Returns:
@@ -331,6 +332,10 @@ class SandboxService:
331
332
 
332
333
  Raises:
333
334
  RuntimeError: If sandbox is not running or script execution fails
335
+
336
+ Note:
337
+ If code starts with '/home/user/', it will be treated as a file path.
338
+ Otherwise, it will be treated as JavaScript code and written to a temporary file.
334
339
  """
335
340
  if not self.sandbox or self.status != SandboxStatus.RUNNING:
336
341
  raise RuntimeError('Sandbox is not running. Call create_sandbox() first.')
@@ -341,9 +346,14 @@ class SandboxService:
341
346
  try:
342
347
  # Generate temporary file name in working directory
343
348
  timestamp = int(time.time() * 1000)
344
- extension = 'mjs' if options['type'] == 'esm' else 'js'
345
- working_dir = options.get('cwd', self.DEFAULT_WORKING_DIRECTORY)
346
- script_path = f'{working_dir}/script_{timestamp}.{extension}'
349
+ script_path = code
350
+
351
+ if not code.startswith('/home/user/'):
352
+ extension = 'mjs' if options['type'] == 'esm' else 'js'
353
+ working_dir = options.get('cwd', self.DEFAULT_WORKING_DIRECTORY)
354
+ script_path = f'{working_dir}/script_{timestamp}.{extension}'
355
+ # Write code to temporary file
356
+ await self.sandbox.files.write(script_path, code)
347
357
 
348
358
  self.logger.debug('Running JavaScript code in sandbox', {
349
359
  'type': options['type'],
@@ -351,9 +361,6 @@ class SandboxService:
351
361
  'codeLength': len(code),
352
362
  })
353
363
 
354
- # Write code to temporary file
355
- await self.sandbox.files.write(script_path, code)
356
-
357
364
  # Choose execution command based on type
358
365
  pre_command = options.get('preCommand', '')
359
366
  command = f'{pre_command}node {script_path}'
@@ -374,16 +381,6 @@ class SandboxService:
374
381
  # Execute the script
375
382
  result = await self.run_command(command, cmd_options)
376
383
 
377
- # Cleanup temporary file (if not background execution)
378
- if not options.get('background', False):
379
- try:
380
- await self.run_command(f'rm -f {script_path}')
381
- except Exception as cleanup_error:
382
- self.logger.warn('Failed to cleanup script file', {
383
- 'scriptPath': script_path,
384
- 'error': cleanup_error,
385
- })
386
-
387
384
  return result
388
385
 
389
386
  except Exception as error:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: grasp_sdk
3
- Version: 0.1.7
3
+ Version: 0.1.9
4
4
  Summary: Python SDK for Grasp E2B - Browser automation and sandbox management
5
5
  Home-page: https://github.com/grasp-team/grasp-e2b
6
6
  Author: Grasp Team
@@ -30,9 +30,6 @@ grasp_sdk.egg-info/not-zip-safe
30
30
  grasp_sdk.egg-info/requires.txt
31
31
  grasp_sdk.egg-info/top_level.txt
32
32
  grasp_sdk/models/__init__.py
33
- grasp_sdk/sandbox/chrome-stable.mjs
34
- grasp_sdk/sandbox/chromium.mjs
35
- grasp_sdk/sandbox/http-proxy.mjs
36
33
  grasp_sdk/services/__init__.py
37
34
  grasp_sdk/services/browser.py
38
35
  grasp_sdk/services/sandbox.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "grasp_sdk"
7
- version = "0.1.7"
7
+ version = "0.1.9"
8
8
  authors = [
9
9
  {name = "Grasp Team", email = "team@grasp.com"},
10
10
  ]