grasp-sdk 0.1.0__tar.gz → 0.1.2__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 (33) hide show
  1. {grasp_sdk-0.1.0/grasp_sdk.egg-info → grasp_sdk-0.1.2}/PKG-INFO +139 -33
  2. grasp_sdk-0.1.2/README.md +260 -0
  3. grasp_sdk-0.1.2/example_usage.py +101 -0
  4. {grasp_sdk-0.1.0 → grasp_sdk-0.1.2}/grasp_sdk/__init__.py +42 -20
  5. {grasp_sdk-0.1.0 → grasp_sdk-0.1.2}/grasp_sdk/sandbox/chrome-stable.mjs +4 -0
  6. {grasp_sdk-0.1.0 → grasp_sdk-0.1.2}/grasp_sdk/sandbox/chromium.mjs +5 -1
  7. {grasp_sdk-0.1.0 → grasp_sdk-0.1.2}/grasp_sdk/services/browser.py +9 -18
  8. {grasp_sdk-0.1.0 → grasp_sdk-0.1.2}/grasp_sdk/services/sandbox.py +10 -7
  9. {grasp_sdk-0.1.0 → grasp_sdk-0.1.2/grasp_sdk.egg-info}/PKG-INFO +139 -33
  10. {grasp_sdk-0.1.0 → grasp_sdk-0.1.2}/grasp_sdk.egg-info/SOURCES.txt +0 -2
  11. {grasp_sdk-0.1.0 → grasp_sdk-0.1.2}/pyproject.toml +1 -1
  12. {grasp_sdk-0.1.0 → grasp_sdk-0.1.2}/setup.py +1 -1
  13. grasp_sdk-0.1.0/README.md +0 -154
  14. grasp_sdk-0.1.0/example_simple.py +0 -138
  15. grasp_sdk-0.1.0/example_usage.py +0 -100
  16. {grasp_sdk-0.1.0 → grasp_sdk-0.1.2}/MANIFEST.in +0 -0
  17. {grasp_sdk-0.1.0 → grasp_sdk-0.1.2}/build_and_publish.py +0 -0
  18. {grasp_sdk-0.1.0 → grasp_sdk-0.1.2}/grasp_sdk/models/__init__.py +0 -0
  19. {grasp_sdk-0.1.0 → grasp_sdk-0.1.2}/grasp_sdk/sandbox/jsconfig.json +0 -0
  20. {grasp_sdk-0.1.0 → grasp_sdk-0.1.2}/grasp_sdk/services/__init__.py +0 -0
  21. {grasp_sdk-0.1.0 → grasp_sdk-0.1.2}/grasp_sdk/utils/__init__.py +0 -0
  22. {grasp_sdk-0.1.0 → grasp_sdk-0.1.2}/grasp_sdk/utils/auth.py +0 -0
  23. {grasp_sdk-0.1.0 → grasp_sdk-0.1.2}/grasp_sdk/utils/config.py +0 -0
  24. {grasp_sdk-0.1.0 → grasp_sdk-0.1.2}/grasp_sdk/utils/logger.py +0 -0
  25. {grasp_sdk-0.1.0 → grasp_sdk-0.1.2}/grasp_sdk.egg-info/dependency_links.txt +0 -0
  26. {grasp_sdk-0.1.0 → grasp_sdk-0.1.2}/grasp_sdk.egg-info/entry_points.txt +0 -0
  27. {grasp_sdk-0.1.0 → grasp_sdk-0.1.2}/grasp_sdk.egg-info/not-zip-safe +0 -0
  28. {grasp_sdk-0.1.0 → grasp_sdk-0.1.2}/grasp_sdk.egg-info/requires.txt +0 -0
  29. {grasp_sdk-0.1.0 → grasp_sdk-0.1.2}/grasp_sdk.egg-info/top_level.txt +0 -0
  30. {grasp_sdk-0.1.0 → grasp_sdk-0.1.2}/py.typed +0 -0
  31. {grasp_sdk-0.1.0 → grasp_sdk-0.1.2}/requirements.txt +0 -0
  32. {grasp_sdk-0.1.0 → grasp_sdk-0.1.2}/setup.cfg +0 -0
  33. {grasp_sdk-0.1.0 → grasp_sdk-0.1.2}/test_install.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: grasp_sdk
3
- Version: 0.1.0
3
+ Version: 0.1.2
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
@@ -73,35 +73,90 @@ pip install -e ".[dev]"
73
73
 
74
74
  ## 🔧 Quick Start
75
75
 
76
+ ### Using GraspServer Context Manager (Recommended)
77
+
76
78
  ```python
77
79
  import asyncio
80
+ import os
81
+ from pathlib import Path
82
+ from playwright.async_api import async_playwright
83
+ from dotenv import load_dotenv
84
+
78
85
  from grasp_sdk import GraspServer
86
+ from grasp_sdk.models import IBrowserConfig, ISandboxConfig
87
+
88
+ # 加载环境变量
89
+ load_dotenv("../.env.grasp")
79
90
 
80
91
  async def main():
81
- # Initialize Grasp server
82
- server = GraspServer({
83
- "key": api_key,
84
- "timeout": 30000,
85
- })
92
+ """主函数:演示 Grasp SDK 的基本用法"""
86
93
 
87
- try:
88
- # Start sandbox
89
- await server.start()
90
-
91
- # Execute command
92
- result = await server.execute_command("echo 'Hello from Python!'")
93
- print(f"Output: {result.stdout}")
94
-
95
- # Browser automation
96
- browser_task = await server.create_browser_task()
97
- await browser_task.navigate("https://example.com")
98
- screenshot = await browser_task.screenshot("example.png")
94
+ # 检查是否有 API key
95
+ api_key = os.getenv('GRASP_KEY')
96
+ if not api_key:
97
+ print("⚠️ 警告:未设置 GRASP_KEY 环境变量")
98
+ print("请设置 GRASP_KEY 环境变量或在 .env 文件中配置")
99
+ print("示例:export GRASP_KEY=your_api_key_here")
100
+ return
101
+
102
+ print("🚀 正在启动浏览器...")
103
+
104
+ async with GraspServer({
105
+ # 'key': api_key,
106
+ # 'type': 'chrome-stable',
107
+ # 'headless': False,
108
+ # 'adblock': True,
109
+ # 'debug': True,
110
+ 'timeout': 3600000, # 容器最长运行1小时(最大值可以为一天 86400000)
111
+ }) as connection:
112
+
113
+ try:
114
+ print(f"连接信息: {connection}")
115
+ print(f"WebSocket URL: {connection['ws_url']}")
116
+ print(f"HTTP URL: {connection['http_url']}")
117
+
118
+ # 使用 Playwright 连接到 CDP
119
+ async with async_playwright() as p:
120
+ browser = await p.chromium.connect_over_cdp(
121
+ connection['ws_url'],
122
+ timeout=150000
123
+ )
124
+
125
+ # 创建第一个页面并访问网站
126
+ page1 = await browser.new_page()
127
+ await page1.goto('https://getgrasp.ai/', wait_until='domcontentloaded')
128
+ await page1.screenshot(path='grasp-ai.png')
129
+ await page1.close()
130
+
131
+ # 获取或创建上下文
132
+ contexts = browser.contexts
133
+ context = contexts[0] if contexts else await browser.new_context()
134
+
135
+ # 创建第二个页面
136
+ page2 = await context.new_page()
137
+
138
+ # 将 HTML 字符串渲染到页面中
139
+ await page2.set_content('<h1>Hello Grasp</h1>', wait_until='networkidle')
140
+
141
+ # 截图演示
142
+ await page2.screenshot(path='hello-world.png', full_page=True)
143
+
144
+ # 清理资源
145
+ await page2.close()
146
+ await context.close()
147
+ await browser.close()
148
+
149
+ print('✅ 任务完成。')
150
+
151
+ except Exception as e:
152
+ print(f"❌ 执行过程中出现错误: {str(e)}")
153
+ raise
99
154
 
100
- finally:
101
- # Clean up
102
- await server.close()
155
+ finally:
156
+ # 注意:使用 GraspServer 上下文管理器时,资源会自动清理
157
+ print("程序结束,资源将自动清理")
103
158
 
104
- if __name__ == "__main__":
159
+ if __name__ == '__main__':
105
160
  asyncio.run(main())
106
161
  ```
107
162
 
@@ -145,13 +200,32 @@ GRASP_TEMPLATE=python
145
200
 
146
201
  ## 🧪 Development
147
202
 
203
+ ### Quick Setup
204
+
148
205
  ```bash
149
- # Install development dependencies
206
+ # Install development dependencies automatically
207
+ python install_dev_deps.py
208
+
209
+ # Or manually install
150
210
  pip install -e ".[dev]"
211
+ ```
212
+
213
+ ### Running Tests
214
+
215
+ ```bash
216
+ # Simple tests (no pytest required)
217
+ python test_connect_simple.py
218
+
219
+ # Full test suite (requires pytest)
220
+ pytest tests/
221
+
222
+ # Run specific test file
223
+ pytest tests/test_connect.py -v
224
+ ```
151
225
 
152
- # Run tests
153
- pytest
226
+ ### Code Quality
154
227
 
228
+ ```bash
155
229
  # Format code
156
230
  black .
157
231
  isort .
@@ -163,22 +237,54 @@ mypy .
163
237
  flake8 .
164
238
  ```
165
239
 
240
+ ### Examples
241
+
242
+ ```bash
243
+ # Run Connect usage examples
244
+ python example_connect.py
245
+ ```
246
+
166
247
  ## 📚 API Reference
167
248
 
249
+ ### GraspServer (Recommended)
250
+
251
+ Async context manager for automatic sandbox resource management and browser automation.
252
+
253
+ ```python
254
+ class GraspServer:
255
+ def __init__(self, options: Optional[Dict[str, Any]] = None)
256
+ async def __aenter__(self) -> Dict[str, Any] # Returns connection info
257
+ async def __aexit__(self, exc_type, exc_val, exc_tb) -> None
258
+ ```
259
+
260
+ **Usage:**
261
+ ```python
262
+ async with GraspServer(options) as connection:
263
+ # connection contains: {'id', 'ws_url', 'http_url'}
264
+ # Automatic cleanup on exit
265
+ ```
266
+
267
+ **Options:**
268
+ - `key`: Your Grasp API key (loaded from environment if not provided)
269
+ - `type`: Browser type ('chromium' or 'chrome-stable')
270
+ - `headless`: Run in headless mode (default: True)
271
+ - `adblock`: Enable adblock (default: False)
272
+ - `debug`: Enable debug mode (default: False)
273
+ - `timeout`: Container maximum runtime in milliseconds (default: 30000, max: 86400000)
274
+
168
275
  ### GraspServer
169
276
 
170
277
  Main class for interacting with E2B sandboxes and browser automation.
171
278
 
172
279
  ```python
173
280
  class GraspServer:
174
- def __init__(self, config: ISandboxConfig = None)
175
- async def start(self) -> None
176
- async def close(self) -> None
177
- async def execute_command(self, command: str, options: ICommandOptions = None) -> CommandResult
178
- async def execute_script(self, script_path: str, options: IScriptOptions = None) -> CommandResult
179
- async def create_browser_task(self, config: IBrowserConfig = None) -> BrowserTask
180
- def get_sandbox_status(self) -> SandboxStatus
181
- def get_sandbox_id(self) -> str
281
+ def __init__(self, sandbox_config: Optional[Dict[str, Any]] = None)
282
+ async def create_browser_task(self, browser_type: str = 'chromium', config: Optional[Dict[str, Any]] = None) -> Dict[str, Any]
283
+ async def cleanup(self) -> None
284
+ def get_status(self) -> Optional[SandboxStatus]
285
+ def get_sandbox_id(self) -> Optional[str]
286
+ @property
287
+ def sandbox(self) -> Optional[SandboxService]
182
288
  ```
183
289
 
184
290
  ## 🤝 Compatibility
@@ -0,0 +1,260 @@
1
+ # Grasp SDK - Python Implementation
2
+
3
+ 🐍 Python implementation of Grasp SDK for E2B platform providing secure command execution and browser automation in isolated cloud environments.
4
+
5
+ ## 🚀 Features
6
+
7
+ - **Secure Execution**: Run commands and scripts in isolated E2B sandboxes
8
+ - **Browser Automation**: Control Chromium browsers with Playwright integration
9
+ - **Async/Await Support**: Full async/await support for modern Python development
10
+ - **Type Safety**: Complete type hints with Pydantic models
11
+ - **WebSocket Communication**: Real-time communication with sandbox environments
12
+ - **Multi-language Support**: Compatible with Node.js/TypeScript version
13
+
14
+ ## 📦 Installation
15
+
16
+ ```bash
17
+ # Install from PyPI (when published)
18
+ pip install grasp-sdk
19
+
20
+ # Install from source
21
+ pip install -e .
22
+
23
+ # Install with development dependencies
24
+ pip install -e ".[dev]"
25
+ ```
26
+
27
+ ## 🔧 Quick Start
28
+
29
+ ### Using GraspServer Context Manager (Recommended)
30
+
31
+ ```python
32
+ import asyncio
33
+ import os
34
+ from pathlib import Path
35
+ from playwright.async_api import async_playwright
36
+ from dotenv import load_dotenv
37
+
38
+ from grasp_sdk import GraspServer
39
+ from grasp_sdk.models import IBrowserConfig, ISandboxConfig
40
+
41
+ # 加载环境变量
42
+ load_dotenv("../.env.grasp")
43
+
44
+ async def main():
45
+ """主函数:演示 Grasp SDK 的基本用法"""
46
+
47
+ # 检查是否有 API key
48
+ api_key = os.getenv('GRASP_KEY')
49
+ if not api_key:
50
+ print("⚠️ 警告:未设置 GRASP_KEY 环境变量")
51
+ print("请设置 GRASP_KEY 环境变量或在 .env 文件中配置")
52
+ print("示例:export GRASP_KEY=your_api_key_here")
53
+ return
54
+
55
+ print("🚀 正在启动浏览器...")
56
+
57
+ async with GraspServer({
58
+ # 'key': api_key,
59
+ # 'type': 'chrome-stable',
60
+ # 'headless': False,
61
+ # 'adblock': True,
62
+ # 'debug': True,
63
+ 'timeout': 3600000, # 容器最长运行1小时(最大值可以为一天 86400000)
64
+ }) as connection:
65
+
66
+ try:
67
+ print(f"连接信息: {connection}")
68
+ print(f"WebSocket URL: {connection['ws_url']}")
69
+ print(f"HTTP URL: {connection['http_url']}")
70
+
71
+ # 使用 Playwright 连接到 CDP
72
+ async with async_playwright() as p:
73
+ browser = await p.chromium.connect_over_cdp(
74
+ connection['ws_url'],
75
+ timeout=150000
76
+ )
77
+
78
+ # 创建第一个页面并访问网站
79
+ page1 = await browser.new_page()
80
+ await page1.goto('https://getgrasp.ai/', wait_until='domcontentloaded')
81
+ await page1.screenshot(path='grasp-ai.png')
82
+ await page1.close()
83
+
84
+ # 获取或创建上下文
85
+ contexts = browser.contexts
86
+ context = contexts[0] if contexts else await browser.new_context()
87
+
88
+ # 创建第二个页面
89
+ page2 = await context.new_page()
90
+
91
+ # 将 HTML 字符串渲染到页面中
92
+ await page2.set_content('<h1>Hello Grasp</h1>', wait_until='networkidle')
93
+
94
+ # 截图演示
95
+ await page2.screenshot(path='hello-world.png', full_page=True)
96
+
97
+ # 清理资源
98
+ await page2.close()
99
+ await context.close()
100
+ await browser.close()
101
+
102
+ print('✅ 任务完成。')
103
+
104
+ except Exception as e:
105
+ print(f"❌ 执行过程中出现错误: {str(e)}")
106
+ raise
107
+
108
+ finally:
109
+ # 注意:使用 GraspServer 上下文管理器时,资源会自动清理
110
+ print("程序结束,资源将自动清理")
111
+
112
+ if __name__ == '__main__':
113
+ asyncio.run(main())
114
+ ```
115
+
116
+ ## 🏗️ Architecture
117
+
118
+ The Python implementation mirrors the Node.js/TypeScript version:
119
+
120
+ ```
121
+ py-src/
122
+ ├── __init__.py # Main package exports
123
+ ├── grasp_server.py # Main GraspServer class
124
+ ├── services/ # Core services
125
+ │ ├── __init__.py
126
+ │ ├── sandbox_service.py # E2B sandbox management
127
+ │ └── browser_service.py # Browser automation
128
+ ├── types/ # Type definitions
129
+ │ └── __init__.py
130
+ ├── utils/ # Utilities
131
+ │ ├── __init__.py
132
+ │ ├── config.py # Configuration management
133
+ │ ├── logger.py # Logging utilities
134
+ │ └── auth.py # Authentication
135
+ ├── cli/ # Command line interface
136
+ │ ├── __init__.py
137
+ │ └── main.py
138
+ └── tests/ # Test suite
139
+ └── ...
140
+ ```
141
+
142
+ ## 🔑 Environment Variables
143
+
144
+ ```bash
145
+ # Required
146
+ E2B_API_KEY=your_e2b_api_key_here
147
+
148
+ # Optional
149
+ GRASP_LOG_LEVEL=info
150
+ GRASP_TIMEOUT=30000
151
+ GRASP_TEMPLATE=python
152
+ ```
153
+
154
+ ## 🧪 Development
155
+
156
+ ### Quick Setup
157
+
158
+ ```bash
159
+ # Install development dependencies automatically
160
+ python install_dev_deps.py
161
+
162
+ # Or manually install
163
+ pip install -e ".[dev]"
164
+ ```
165
+
166
+ ### Running Tests
167
+
168
+ ```bash
169
+ # Simple tests (no pytest required)
170
+ python test_connect_simple.py
171
+
172
+ # Full test suite (requires pytest)
173
+ pytest tests/
174
+
175
+ # Run specific test file
176
+ pytest tests/test_connect.py -v
177
+ ```
178
+
179
+ ### Code Quality
180
+
181
+ ```bash
182
+ # Format code
183
+ black .
184
+ isort .
185
+
186
+ # Type checking
187
+ mypy .
188
+
189
+ # Linting
190
+ flake8 .
191
+ ```
192
+
193
+ ### Examples
194
+
195
+ ```bash
196
+ # Run Connect usage examples
197
+ python example_connect.py
198
+ ```
199
+
200
+ ## 📚 API Reference
201
+
202
+ ### GraspServer (Recommended)
203
+
204
+ Async context manager for automatic sandbox resource management and browser automation.
205
+
206
+ ```python
207
+ class GraspServer:
208
+ def __init__(self, options: Optional[Dict[str, Any]] = None)
209
+ async def __aenter__(self) -> Dict[str, Any] # Returns connection info
210
+ async def __aexit__(self, exc_type, exc_val, exc_tb) -> None
211
+ ```
212
+
213
+ **Usage:**
214
+ ```python
215
+ async with GraspServer(options) as connection:
216
+ # connection contains: {'id', 'ws_url', 'http_url'}
217
+ # Automatic cleanup on exit
218
+ ```
219
+
220
+ **Options:**
221
+ - `key`: Your Grasp API key (loaded from environment if not provided)
222
+ - `type`: Browser type ('chromium' or 'chrome-stable')
223
+ - `headless`: Run in headless mode (default: True)
224
+ - `adblock`: Enable adblock (default: False)
225
+ - `debug`: Enable debug mode (default: False)
226
+ - `timeout`: Container maximum runtime in milliseconds (default: 30000, max: 86400000)
227
+
228
+ ### GraspServer
229
+
230
+ Main class for interacting with E2B sandboxes and browser automation.
231
+
232
+ ```python
233
+ class GraspServer:
234
+ def __init__(self, sandbox_config: Optional[Dict[str, Any]] = None)
235
+ async def create_browser_task(self, browser_type: str = 'chromium', config: Optional[Dict[str, Any]] = None) -> Dict[str, Any]
236
+ async def cleanup(self) -> None
237
+ def get_status(self) -> Optional[SandboxStatus]
238
+ def get_sandbox_id(self) -> Optional[str]
239
+ @property
240
+ def sandbox(self) -> Optional[SandboxService]
241
+ ```
242
+
243
+ ## 🤝 Compatibility
244
+
245
+ This Python implementation provides the same API surface as the Node.js/TypeScript version, ensuring:
246
+
247
+ - **Feature Parity**: All features available in both implementations
248
+ - **API Consistency**: Same method names and behavior
249
+ - **Type Safety**: Equivalent type definitions using TypedDict and Pydantic
250
+ - **Error Handling**: Consistent error types and messages
251
+
252
+ ## 📄 License
253
+
254
+ MIT License - see the [LICENSE](../LICENSE) file for details.
255
+
256
+ ## 🔗 Related
257
+
258
+ - [Node.js/TypeScript Implementation](../src/)
259
+ - [E2B Platform](https://e2b.dev/)
260
+ - [Playwright Python](https://playwright.dev/python/)
@@ -0,0 +1,101 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Grasp SDK Python 使用示例
4
+
5
+ 这个示例展示了如何使用 grasp_sdk 启动浏览器、连接 CDP、执行基本操作并截图。
6
+ """
7
+
8
+ import asyncio
9
+ import os
10
+ from pathlib import Path
11
+ from playwright.async_api import async_playwright
12
+ from dotenv import load_dotenv
13
+
14
+ from grasp_sdk import GraspServer
15
+ from grasp_sdk.models import IBrowserConfig, ISandboxConfig
16
+
17
+ # 加载环境变量
18
+ load_dotenv("../.env.grasp")
19
+
20
+
21
+ async def main():
22
+ """主函数:演示 Grasp SDK 的基本用法"""
23
+
24
+ # 检查是否有 API key
25
+ api_key = os.getenv('GRASP_KEY')
26
+ if not api_key:
27
+ print("⚠️ 警告:未设置 GRASP_KEY 环境变量")
28
+ print("请设置 GRASP_KEY 环境变量或在 .env 文件中配置")
29
+ print("示例:export GRASP_KEY=your_api_key_here")
30
+ return
31
+
32
+ print("🚀 正在启动浏览器...")
33
+
34
+ async with GraspServer({
35
+ # 'key': api_key,
36
+ # 'type': 'chrome-stable',
37
+ # 'headless': False,
38
+ # 'adblock': True,
39
+ # 'debug': True,
40
+ 'timeout': 3600000, # 容器最长运行1小时(最大值可以为一天 86400000)
41
+ }) as connection:
42
+
43
+ try:
44
+ print(f"连接信息: {connection}")
45
+ print(f"WebSocket URL: {connection['ws_url']}")
46
+ print(f"HTTP URL: {connection['http_url']}")
47
+
48
+ # 使用 Playwright 连接到 CDP
49
+ async with async_playwright() as p:
50
+ browser = await p.chromium.connect_over_cdp(
51
+ connection['ws_url'],
52
+ timeout=150000
53
+ )
54
+
55
+ # 等待一段时间(可选)
56
+ # await asyncio.sleep(10)
57
+
58
+ # 创建第一个页面并访问网站
59
+ page1 = await browser.new_page()
60
+ await page1.goto('https://getgrasp.ai/', wait_until='domcontentloaded')
61
+ await page1.screenshot(path='grasp-ai.png')
62
+ await page1.close()
63
+
64
+ # 获取或创建上下文
65
+ contexts = browser.contexts
66
+ context = contexts[0] if contexts else await browser.new_context()
67
+
68
+ # 创建第二个页面
69
+ page2 = await context.new_page()
70
+
71
+ # 将 HTML 字符串渲染到页面中
72
+ await page2.set_content('<h1>Hello Grasp</h1>', wait_until='networkidle')
73
+
74
+ # 等待特定元素可见(可选)
75
+ # await page2.wait_for_selector('#my-element')
76
+
77
+ # 截图演示
78
+ await page2.screenshot(path='hello-world.png', full_page=True)
79
+
80
+ # 清理资源
81
+ await page2.close()
82
+ await context.close()
83
+ await browser.close()
84
+
85
+ # print('⏳ 等待10秒...')
86
+ # await asyncio.sleep(10)
87
+ print('✅ 任务完成。')
88
+
89
+ except Exception as e:
90
+ print(f"❌ 执行过程中出现错误: {str(e)}")
91
+ raise
92
+
93
+ finally:
94
+ # 注意:使用 launch_browser 函数时,资源会在程序退出时自动清理
95
+ # 如果需要手动清理,可以使用 GraspServer 类的实例方法
96
+ print("程序结束,资源将自动清理")
97
+
98
+
99
+ if __name__ == '__main__':
100
+ # 运行主函数
101
+ asyncio.run(main())
@@ -24,7 +24,7 @@ from .models import (
24
24
  SandboxStatus,
25
25
  )
26
26
 
27
- __version__ = "0.1.0"
27
+ __version__ = "0.1.1"
28
28
  __author__ = "Grasp Team"
29
29
  __email__ = "team@grasp.dev"
30
30
 
@@ -40,6 +40,19 @@ class GraspServer:
40
40
  """
41
41
  if sandbox_config is None:
42
42
  sandbox_config = {}
43
+
44
+ # Extract browser-specific options
45
+ browser_type = sandbox_config.pop('type', 'chromium')
46
+ headless = sandbox_config.pop('headless', True)
47
+ adblock = sandbox_config.pop('adblock', False)
48
+
49
+ self.__browser_type = browser_type
50
+
51
+ # Create browser task
52
+ self.__browser_config = {
53
+ 'headless': headless,
54
+ 'envs': {'ADBLOCK': 'true' if adblock else 'false'}
55
+ }
43
56
 
44
57
  config = get_config()
45
58
  config['sandbox'].update(sandbox_config)
@@ -55,6 +68,22 @@ class GraspServer:
55
68
  f'GraspE2B initialized (templateId: {config["sandbox"]["templateId"]})'
56
69
  )
57
70
 
71
+ async def __aenter__(self):
72
+ connection = await self.create_browser_task()
73
+
74
+ # Register server
75
+ # if connection['id']:
76
+ # _servers[connection['id']] = self
77
+
78
+ return connection
79
+
80
+ async def __aexit__(self, exc_type, exc, tb):
81
+ if self.browser_service and self.browser_service.id:
82
+ service_id = self.browser_service.id
83
+ self.logger.info(f'Closing browser service {service_id}')
84
+ await _servers[service_id].cleanup()
85
+ del _servers[service_id]
86
+
58
87
  @property
59
88
  def sandbox(self) -> Optional[SandboxService]:
60
89
  """Get the underlying sandbox service.
@@ -82,8 +111,6 @@ class GraspServer:
82
111
 
83
112
  async def create_browser_task(
84
113
  self,
85
- browser_type: Literal['chrome-stable', 'chromium'] = 'chromium',
86
- config: Optional[Dict[str, Any]] = None
87
114
  ) -> Dict[str, Any]:
88
115
  """Create and launch a browser task.
89
116
 
@@ -100,6 +127,9 @@ class GraspServer:
100
127
  if self.browser_service:
101
128
  raise RuntimeError('Browser service can only be initialized once')
102
129
 
130
+ config = self.__browser_config
131
+ browser_type = self.__browser_type
132
+
103
133
  if config is None:
104
134
  config = {}
105
135
 
@@ -132,6 +162,12 @@ class GraspServer:
132
162
  browser_config
133
163
  )
134
164
  await self.browser_service.initialize()
165
+
166
+ # Register server
167
+ _servers[str(self.browser_service.id)] = self
168
+ self.logger.info("🚀 Browser service initialized", {
169
+ 'id': self.browser_service.id,
170
+ })
135
171
 
136
172
  self.logger.info('🌐 Launching Chromium browser with CDP...')
137
173
  cdp_connection = await self.browser_service.launch_browser(browser_type)
@@ -180,25 +216,11 @@ async def launch_browser(
180
216
 
181
217
  Returns:
182
218
  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
-
219
+ """
192
220
  # Create server instance
193
221
  server = GraspServer(options)
194
222
 
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)
223
+ connection = await server.create_browser_task()
202
224
 
203
225
  # Register server
204
226
  if connection['id']:
@@ -258,5 +280,5 @@ __all__ = [
258
280
 
259
281
  # Default export equivalent
260
282
  default = {
261
- 'launch_browser': launch_browser,
283
+ 'GraspServer': GraspServer,
262
284
  }