grasp-sdk 0.1.7__tar.gz → 0.1.8__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 (31) hide show
  1. {grasp_sdk-0.1.7/grasp_sdk.egg-info → grasp_sdk-0.1.8}/PKG-INFO +1 -1
  2. {grasp_sdk-0.1.7 → grasp_sdk-0.1.8}/grasp_sdk/__init__.py +3 -3
  3. grasp_sdk-0.1.8/grasp_sdk/sandbox/bootstrap-chrome-stable.mjs +69 -0
  4. {grasp_sdk-0.1.7 → grasp_sdk-0.1.8}/grasp_sdk/sandbox/http-proxy.mjs +6 -4
  5. {grasp_sdk-0.1.7 → grasp_sdk-0.1.8}/grasp_sdk/services/browser.py +26 -37
  6. {grasp_sdk-0.1.7 → grasp_sdk-0.1.8}/grasp_sdk/services/sandbox.py +16 -19
  7. {grasp_sdk-0.1.7 → grasp_sdk-0.1.8/grasp_sdk.egg-info}/PKG-INFO +1 -1
  8. {grasp_sdk-0.1.7 → grasp_sdk-0.1.8}/grasp_sdk.egg-info/SOURCES.txt +1 -0
  9. {grasp_sdk-0.1.7 → grasp_sdk-0.1.8}/pyproject.toml +1 -1
  10. {grasp_sdk-0.1.7 → grasp_sdk-0.1.8}/MANIFEST.in +0 -0
  11. {grasp_sdk-0.1.7 → grasp_sdk-0.1.8}/README.md +0 -0
  12. {grasp_sdk-0.1.7 → grasp_sdk-0.1.8}/build_and_publish.py +0 -0
  13. {grasp_sdk-0.1.7 → grasp_sdk-0.1.8}/example_usage.py +0 -0
  14. {grasp_sdk-0.1.7 → grasp_sdk-0.1.8}/grasp_sdk/models/__init__.py +0 -0
  15. {grasp_sdk-0.1.7 → grasp_sdk-0.1.8}/grasp_sdk/sandbox/chrome-stable.mjs +0 -0
  16. {grasp_sdk-0.1.7 → grasp_sdk-0.1.8}/grasp_sdk/sandbox/chromium.mjs +0 -0
  17. {grasp_sdk-0.1.7 → grasp_sdk-0.1.8}/grasp_sdk/services/__init__.py +0 -0
  18. {grasp_sdk-0.1.7 → grasp_sdk-0.1.8}/grasp_sdk/utils/__init__.py +0 -0
  19. {grasp_sdk-0.1.7 → grasp_sdk-0.1.8}/grasp_sdk/utils/auth.py +0 -0
  20. {grasp_sdk-0.1.7 → grasp_sdk-0.1.8}/grasp_sdk/utils/config.py +0 -0
  21. {grasp_sdk-0.1.7 → grasp_sdk-0.1.8}/grasp_sdk/utils/logger.py +0 -0
  22. {grasp_sdk-0.1.7 → grasp_sdk-0.1.8}/grasp_sdk.egg-info/dependency_links.txt +0 -0
  23. {grasp_sdk-0.1.7 → grasp_sdk-0.1.8}/grasp_sdk.egg-info/entry_points.txt +0 -0
  24. {grasp_sdk-0.1.7 → grasp_sdk-0.1.8}/grasp_sdk.egg-info/not-zip-safe +0 -0
  25. {grasp_sdk-0.1.7 → grasp_sdk-0.1.8}/grasp_sdk.egg-info/requires.txt +0 -0
  26. {grasp_sdk-0.1.7 → grasp_sdk-0.1.8}/grasp_sdk.egg-info/top_level.txt +0 -0
  27. {grasp_sdk-0.1.7 → grasp_sdk-0.1.8}/py.typed +0 -0
  28. {grasp_sdk-0.1.7 → grasp_sdk-0.1.8}/requirements.txt +0 -0
  29. {grasp_sdk-0.1.7 → grasp_sdk-0.1.8}/setup.cfg +0 -0
  30. {grasp_sdk-0.1.7 → grasp_sdk-0.1.8}/setup.py +0 -0
  31. {grasp_sdk-0.1.7 → grasp_sdk-0.1.8}/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.8
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.8"
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(
@@ -0,0 +1,69 @@
1
+ import { spawn } from 'child_process';
2
+
3
+ // const asblockPlugin = '/home/user/.config/google-chrome/Default/Extensions/adblock';
4
+
5
+ const args = [
6
+ '--no-sandbox',
7
+ '--disable-setuid-sandbox',
8
+ '--disable-dev-shm-usage',
9
+ '--disable-gpu',
10
+ '--disable-software-rasterizer',
11
+ '--user-data-dir=/home/user/.browser-context'
12
+ ];
13
+
14
+ args.push(
15
+ // 避免缓存积累影响性能
16
+ '--disable-application-cache',
17
+
18
+ // 关闭所有硬件加速特性,防止 GPU 相关崩溃
19
+ '--disable-accelerated-2d-canvas',
20
+ '--disable-accelerated-video-decode',
21
+
22
+ // 禁用后台渲染,减少无关资源消耗
23
+ '--disable-background-timer-throttling',
24
+ '--disable-backgrounding-occluded-windows',
25
+ '--disable-renderer-backgrounding',
26
+
27
+ // 避免过度日志影响性能
28
+ '--disable-logging',
29
+
30
+ // 禁用不必要的多媒体解码
31
+ '--mute-audio',
32
+
33
+ // 避免崩溃时弹窗
34
+ '--no-default-browser-check',
35
+ '--no-first-run',
36
+ '--headless=new',
37
+ );
38
+
39
+ args.push(
40
+ `--remote-debugging-port=9222`,
41
+ '--remote-debugging-address=0.0.0.0',
42
+ 'about:blank',
43
+ );
44
+
45
+ const chromePath = '/usr/bin/google-chrome';
46
+
47
+ // 启动 Chrome 并启用远程调试
48
+ const chrome = spawn(chromePath, args, {
49
+ env: { ...process.env, DISPLAY: ':99' }
50
+ });
51
+
52
+ chrome.stdout.on('data', (data) => {
53
+ console.log(`stdout: ${data}`);
54
+ });
55
+
56
+ chrome.stderr.on('data', (data) => {
57
+ console.error(`stderr: ${data}`);
58
+ });
59
+
60
+ chrome.on('close', (code) => {
61
+ console.log(`Chrome process exited with code ${code}`);
62
+ });
63
+
64
+ console.log('Browser launched and ready...');
65
+
66
+ // 保持进程不退出,并监听中止信号
67
+ process.stdin.resume();
68
+ process.on('SIGINT', () => process.exit());
69
+ process.on('SIGTERM', () => process.exit());
@@ -1,5 +1,6 @@
1
1
  import httpProxy from 'http-proxy';
2
2
  import http from 'http';
3
+ import fs from 'fs';
3
4
 
4
5
  import { Logtail } from '@logtail/node';
5
6
  import * as Sentry from "@sentry/node";
@@ -94,7 +95,7 @@ function parseWebSocketFrame(buffer) {
94
95
  return payloadData.toString('utf8');
95
96
  }
96
97
 
97
- const sandboxId = process.env.SANDBOX_ID;
98
+ let sandboxId = '';
98
99
  const cdpPort = Number(process.env.CDP_PORT);
99
100
  const headless = process.env.HEADLESS !== 'false';
100
101
  const enableAdblock = process.env.ADBLOCK !== 'false';
@@ -115,7 +116,8 @@ try {
115
116
 
116
117
  // 监听 WebSocket 事件
117
118
  proxy.on('open', () => {
118
- console.log('🔌 CDP WebSocket connection established');
119
+ sandboxId = fs.readFileSync('/home/user/.sandbox_id', 'utf-8');
120
+ console.log('🔌 CDP WebSocket connection established', sandboxId);
119
121
  const wsId = Date.now();
120
122
  logger.info('CDP WebSocket connection established', { sandboxId, wsId });
121
123
  Sentry.addBreadcrumb({
@@ -230,7 +232,7 @@ try {
230
232
  console.log('📨 CDP WebSocket message:', parsed);
231
233
  logger.info('CDP WebSocket message received', {
232
234
  data: parsed,
233
- sandboxId: process.env.SANDBOX_ID,
235
+ sandboxId,
234
236
  });
235
237
  Sentry.addBreadcrumb({
236
238
  category: 'websocket',
@@ -251,7 +253,7 @@ try {
251
253
  logger.warn('Failed to parse CDP WebSocket message', {
252
254
  error: err.message,
253
255
  data: message,
254
- sandboxId: process.env.SANDBOX_ID
256
+ sandboxId,
255
257
  });
256
258
  }
257
259
  }
@@ -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.mjs'
161
151
 
162
152
  # Prepare script options
163
153
  from ..models import IScriptOptions
@@ -166,7 +156,6 @@ class BrowserService:
166
156
  'background': True,
167
157
  'nohup': not self.sandbox_service.is_debug,
168
158
  'timeoutMs': 0,
169
- 'envs': envs,
170
159
  'preCommand': '' if self.config['headless'] else 'xvfb-run -a -s "-screen 0 1280x1024x24" '
171
160
  }
172
161
 
@@ -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.8
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,6 +30,7 @@ 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/bootstrap-chrome-stable.mjs
33
34
  grasp_sdk/sandbox/chrome-stable.mjs
34
35
  grasp_sdk/sandbox/chromium.mjs
35
36
  grasp_sdk/sandbox/http-proxy.mjs
@@ -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.8"
8
8
  authors = [
9
9
  {name = "Grasp Team", email = "team@grasp.com"},
10
10
  ]
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes