grasp-sdk 0.1.8__tar.gz → 0.2.0a1__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 (54) hide show
  1. {grasp_sdk-0.1.8/grasp_sdk.egg-info → grasp_sdk-0.2.0a1}/PKG-INFO +2 -3
  2. grasp_sdk-0.2.0a1/examples/example_async_context.py +122 -0
  3. grasp_sdk-0.2.0a1/examples/example_binary_file_support.py +127 -0
  4. grasp_sdk-0.2.0a1/examples/example_grasp_usage.py +82 -0
  5. grasp_sdk-0.2.0a1/examples/example_readfile_usage.py +136 -0
  6. grasp_sdk-0.2.0a1/examples/grasp_terminal.py +110 -0
  7. grasp_sdk-0.1.8/example_usage.py → grasp_sdk-0.2.0a1/examples/grasp_usage.py +22 -12
  8. grasp_sdk-0.2.0a1/examples/test_async_context.py +64 -0
  9. grasp_sdk-0.2.0a1/examples/test_get_replay_screenshots.py +90 -0
  10. grasp_sdk-0.2.0a1/examples/test_grasp_classes.py +80 -0
  11. grasp_sdk-0.2.0a1/examples/test_python_script.py +160 -0
  12. grasp_sdk-0.2.0a1/examples/test_removed_methods.py +80 -0
  13. grasp_sdk-0.2.0a1/examples/test_shutdown_deprecation.py +62 -0
  14. grasp_sdk-0.2.0a1/examples/test_terminal_updates.py +196 -0
  15. grasp_sdk-0.2.0a1/grasp_sdk/__init__.py +183 -0
  16. grasp_sdk-0.2.0a1/grasp_sdk/grasp/__init__.py +26 -0
  17. grasp_sdk-0.2.0a1/grasp_sdk/grasp/browser.py +69 -0
  18. grasp_sdk-0.2.0a1/grasp_sdk/grasp/index.py +122 -0
  19. grasp_sdk-0.1.8/grasp_sdk/__init__.py → grasp_sdk-0.2.0a1/grasp_sdk/grasp/server.py +74 -115
  20. grasp_sdk-0.2.0a1/grasp_sdk/grasp/session.py +108 -0
  21. grasp_sdk-0.2.0a1/grasp_sdk/grasp/terminal.py +32 -0
  22. grasp_sdk-0.2.0a1/grasp_sdk/grasp/utils.py +90 -0
  23. {grasp_sdk-0.1.8 → grasp_sdk-0.2.0a1}/grasp_sdk/models/__init__.py +1 -1
  24. grasp_sdk-0.2.0a1/grasp_sdk/services/__init__.py +15 -0
  25. {grasp_sdk-0.1.8 → grasp_sdk-0.2.0a1}/grasp_sdk/services/browser.py +92 -63
  26. grasp_sdk-0.2.0a1/grasp_sdk/services/filesystem.py +94 -0
  27. {grasp_sdk-0.1.8 → grasp_sdk-0.2.0a1}/grasp_sdk/services/sandbox.py +391 -20
  28. grasp_sdk-0.2.0a1/grasp_sdk/services/terminal.py +177 -0
  29. {grasp_sdk-0.1.8 → grasp_sdk-0.2.0a1}/grasp_sdk/utils/auth.py +6 -8
  30. {grasp_sdk-0.1.8 → grasp_sdk-0.2.0a1/grasp_sdk.egg-info}/PKG-INFO +2 -3
  31. grasp_sdk-0.2.0a1/grasp_sdk.egg-info/SOURCES.txt +79 -0
  32. {grasp_sdk-0.1.8 → grasp_sdk-0.2.0a1}/grasp_sdk.egg-info/requires.txt +1 -3
  33. {grasp_sdk-0.1.8 → grasp_sdk-0.2.0a1}/grasp_sdk.egg-info/top_level.txt +1 -0
  34. {grasp_sdk-0.1.8 → grasp_sdk-0.2.0a1}/pyproject.toml +2 -4
  35. {grasp_sdk-0.1.8 → grasp_sdk-0.2.0a1}/requirements.txt +2 -1
  36. grasp_sdk-0.1.8/grasp_sdk/sandbox/bootstrap-chrome-stable.mjs +0 -69
  37. grasp_sdk-0.1.8/grasp_sdk/sandbox/chrome-stable.mjs +0 -424
  38. grasp_sdk-0.1.8/grasp_sdk/sandbox/chromium.mjs +0 -395
  39. grasp_sdk-0.1.8/grasp_sdk/sandbox/http-proxy.mjs +0 -324
  40. grasp_sdk-0.1.8/grasp_sdk/services/__init__.py +0 -8
  41. grasp_sdk-0.1.8/grasp_sdk.egg-info/SOURCES.txt +0 -43
  42. grasp_sdk-0.1.8/test_install.py +0 -87
  43. {grasp_sdk-0.1.8 → grasp_sdk-0.2.0a1}/MANIFEST.in +0 -0
  44. {grasp_sdk-0.1.8 → grasp_sdk-0.2.0a1}/README.md +0 -0
  45. {grasp_sdk-0.1.8 → grasp_sdk-0.2.0a1}/build_and_publish.py +0 -0
  46. {grasp_sdk-0.1.8 → grasp_sdk-0.2.0a1}/grasp_sdk/utils/__init__.py +0 -0
  47. {grasp_sdk-0.1.8 → grasp_sdk-0.2.0a1}/grasp_sdk/utils/config.py +0 -0
  48. {grasp_sdk-0.1.8 → grasp_sdk-0.2.0a1}/grasp_sdk/utils/logger.py +0 -0
  49. {grasp_sdk-0.1.8 → grasp_sdk-0.2.0a1}/grasp_sdk.egg-info/dependency_links.txt +0 -0
  50. {grasp_sdk-0.1.8 → grasp_sdk-0.2.0a1}/grasp_sdk.egg-info/entry_points.txt +0 -0
  51. {grasp_sdk-0.1.8 → grasp_sdk-0.2.0a1}/grasp_sdk.egg-info/not-zip-safe +0 -0
  52. {grasp_sdk-0.1.8 → grasp_sdk-0.2.0a1}/py.typed +0 -0
  53. {grasp_sdk-0.1.8 → grasp_sdk-0.2.0a1}/setup.cfg +0 -0
  54. {grasp_sdk-0.1.8 → grasp_sdk-0.2.0a1}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: grasp_sdk
3
- Version: 0.1.8
3
+ Version: 0.2.0a1
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
@@ -27,6 +27,7 @@ Description-Content-Type: text/markdown
27
27
  Requires-Dist: aiohttp>=3.9.0
28
28
  Requires-Dist: python-dotenv>=1.0.0
29
29
  Requires-Dist: typing-extensions>=4.8.0
30
+ Requires-Dist: websockets>=12.0
30
31
  Requires-Dist: e2b>=1.5.0
31
32
  Requires-Dist: e2b-code-interpreter>=1.5.0
32
33
  Provides-Extra: dev
@@ -37,8 +38,6 @@ Requires-Dist: flake8>=6.0.0; extra == "dev"
37
38
  Requires-Dist: mypy>=1.0.0; extra == "dev"
38
39
  Provides-Extra: browser
39
40
  Requires-Dist: playwright>=1.40.0; extra == "browser"
40
- Provides-Extra: websocket
41
- Requires-Dist: websockets>=12.0; extra == "websocket"
42
41
  Provides-Extra: validation
43
42
  Requires-Dist: pydantic>=2.5.0; extra == "validation"
44
43
  Dynamic: author
@@ -0,0 +1,122 @@
1
+ #!/usr/bin/env python3
2
+ """Grasp 异步上下文管理器使用示例"""
3
+
4
+ import asyncio
5
+ import os
6
+ from grasp_sdk import Grasp
7
+
8
+ async def example_with_context_manager():
9
+ """使用异步上下文管理器的完整示例"""
10
+ print("🚀 Grasp 异步上下文管理器使用示例")
11
+
12
+ # 初始化 Grasp SDK
13
+ grasp = Grasp({
14
+ 'apiKey': os.environ.get('GRASP_KEY', 'your-api-key-here')
15
+ })
16
+
17
+ try:
18
+ # 使用异步上下文管理器启动会话
19
+ print("\n📱 启动浏览器会话...")
20
+ async with grasp.launch_context({
21
+ 'browser': {
22
+ 'type': 'chromium',
23
+ 'headless': True,
24
+ 'timeout': 30000
25
+ }
26
+ }) as session:
27
+ print(f"✅ 会话已启动: {type(session).__name__}")
28
+
29
+ # 访问浏览器
30
+ print(f"🌐 浏览器端点: {session.browser.get_endpoint()}")
31
+
32
+ # 创建终端
33
+ print("💻 创建终端...")
34
+ terminal = session.terminal.create()
35
+ print(f"✅ 终端已创建: {type(terminal).__name__}")
36
+
37
+ # 访问文件系统
38
+ print(f"📁 文件系统可用: {type(session.files).__name__}")
39
+
40
+ print("\n🔄 在这里可以进行各种操作...")
41
+ print(" - 浏览器自动化")
42
+ print(" - 终端命令执行")
43
+ print(" - 文件系统操作")
44
+
45
+ # 模拟一些工作
46
+ await asyncio.sleep(0.1)
47
+
48
+ # 会话会在这里自动关闭
49
+ print("\n🔒 会话已自动关闭")
50
+
51
+ except Exception as e:
52
+ print(f"❌ 错误: {e}")
53
+ print("💡 提示: 请确保设置了有效的 GRASP_KEY 环境变量")
54
+
55
+ async def example_multiple_sessions():
56
+ """演示多个会话的使用"""
57
+ print("\n\n🔄 多会话使用示例")
58
+
59
+ grasp = Grasp({
60
+ 'apiKey': os.environ.get('GRASP_KEY', 'your-api-key-here')
61
+ })
62
+
63
+ try:
64
+ # 第一个会话
65
+ print("\n📱 启动第一个会话...")
66
+ async with grasp.launch_context({
67
+ 'browser': {'type': 'chromium', 'headless': True}
68
+ }) as session1:
69
+ print("✅ 第一个会话已启动")
70
+ await asyncio.sleep(0.1)
71
+
72
+ print("🔒 第一个会话已关闭")
73
+
74
+ # 第二个会话
75
+ print("\n📱 启动第二个会话...")
76
+ async with grasp.launch_context({
77
+ 'browser': {'type': 'chromium', 'headless': False}
78
+ }) as session2:
79
+ print("✅ 第二个会话已启动")
80
+ await asyncio.sleep(0.1)
81
+
82
+ print("🔒 第二个会话已关闭")
83
+
84
+ except Exception as e:
85
+ print(f"❌ 错误: {e}")
86
+
87
+ async def example_error_handling():
88
+ """演示错误处理"""
89
+ print("\n\n⚠️ 错误处理示例")
90
+
91
+ grasp = Grasp({'apiKey': 'test-key'})
92
+
93
+ # 尝试重复使用同一个实例
94
+ try:
95
+ grasp.launch_context({'browser': {'type': 'chromium'}})
96
+
97
+ async with grasp as session1:
98
+ print("第一个会话启动")
99
+ # 尝试在已有会话时启动新会话
100
+ async with grasp.launch_context({'browser': {'type': 'firefox'}}) as session2:
101
+ print("这不应该被执行")
102
+
103
+ except RuntimeError as e:
104
+ print(f"✅ 正确捕获错误: {e}")
105
+ except Exception as e:
106
+ print(f"其他错误: {e}")
107
+
108
+ async def main():
109
+ """主函数"""
110
+ await example_with_context_manager()
111
+ await example_multiple_sessions()
112
+ await example_error_handling()
113
+
114
+ print("\n\n📚 总结:")
115
+ print("✅ 异步上下文管理器已成功实现")
116
+ print("✅ 支持 async with grasp.launch_context(options) as session 语法")
117
+ print("✅ 自动管理会话生命周期")
118
+ print("✅ 提供适当的错误处理")
119
+ print("\n🎉 所有功能测试完成!")
120
+
121
+ if __name__ == '__main__':
122
+ asyncio.run(main())
@@ -0,0 +1,127 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Example demonstrating binary file support in Grasp SDK.
4
+
5
+ This example shows how to:
6
+ 1. Read files with different encoding options (utf8, base64, binary)
7
+ 2. Write binary content to files
8
+ 3. Handle different file types automatically
9
+ """
10
+
11
+ import asyncio
12
+ import base64
13
+ from grasp_sdk import Grasp
14
+
15
+
16
+ async def main():
17
+ """Demonstrate binary file support."""
18
+
19
+ # Initialize Grasp
20
+ grasp = Grasp()
21
+
22
+ try:
23
+ # Launch browser session
24
+ session = await grasp.launch({
25
+ 'browser': {
26
+ 'type': 'chromium',
27
+ 'headless': True
28
+ }
29
+ })
30
+
31
+ print("✅ Browser session launched successfully")
32
+
33
+ # Get filesystem service
34
+ fs = session.files
35
+
36
+ # Example 1: Write and read text file with different encodings
37
+ print("\n📝 Example 1: Text file with different encodings")
38
+
39
+ text_content = "Hello, World! 你好世界! 🌍"
40
+ await fs.write_file("/tmp/test.txt", text_content)
41
+
42
+ # Read as UTF-8 (default for text files)
43
+ content_utf8 = await fs.read_file("/tmp/test.txt", {'encoding': 'utf8'})
44
+ print(f"UTF-8: {content_utf8}")
45
+
46
+ # Read as base64
47
+ content_base64 = await fs.read_file("/tmp/test.txt", {'encoding': 'base64'})
48
+ print(f"Base64: {content_base64}")
49
+
50
+ # Read as binary
51
+ content_binary = await fs.read_file("/tmp/test.txt", {'encoding': 'binary'})
52
+ print(f"Binary: {content_binary}")
53
+
54
+ # Example 2: Write and read binary content
55
+ print("\n🔢 Example 2: Binary content")
56
+
57
+ # Create some binary data (a simple PNG-like header)
58
+ binary_data = b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01'
59
+
60
+ # Write binary data
61
+ await fs.write_file("/tmp/test.png", binary_data)
62
+ print("✅ Binary data written to /tmp/test.png")
63
+
64
+ # Read binary file (will auto-detect as binary based on extension)
65
+ content_auto = await fs.read_file("/tmp/test.png")
66
+ print(f"Auto-detected binary: {type(content_auto)} - {len(content_auto)} bytes")
67
+
68
+ # Read explicitly as base64
69
+ content_b64 = await fs.read_file("/tmp/test.png", {'encoding': 'base64'})
70
+ print(f"Base64 encoded: {content_b64}")
71
+
72
+ # Example 3: Working with base64 encoded data
73
+ print("\n📋 Example 3: Base64 workflow")
74
+
75
+ # Encode some data to base64
76
+ original_data = b"This is some binary data with special chars: \x00\x01\x02\xFF"
77
+ b64_encoded = base64.b64encode(original_data).decode('utf-8')
78
+
79
+ # Write the base64 string as text
80
+ await fs.write_file("/tmp/encoded.txt", b64_encoded)
81
+
82
+ # Read it back and decode
83
+ read_b64 = await fs.read_file("/tmp/encoded.txt", {'encoding': 'utf8'})
84
+ decoded_data = base64.b64decode(read_b64)
85
+
86
+ print(f"Original: {original_data}")
87
+ print(f"Encoded: {b64_encoded}")
88
+ print(f"Decoded: {decoded_data}")
89
+ print(f"Match: {original_data == decoded_data}")
90
+
91
+ # Example 4: File type detection
92
+ print("\n🔍 Example 4: File type detection")
93
+
94
+ test_files = [
95
+ ("/tmp/text.txt", "Plain text content"),
96
+ ("/tmp/image.jpg", b"\xFF\xD8\xFF\xE0"), # JPEG header
97
+ ("/tmp/document.pdf", b"%PDF-1.4"), # PDF header
98
+ ("/tmp/archive.zip", b"PK\x03\x04"), # ZIP header
99
+ ]
100
+
101
+ for filepath, content in test_files:
102
+ await fs.write_file(filepath, content)
103
+
104
+ # Read without specifying encoding (auto-detect)
105
+ result = await fs.read_file(filepath)
106
+
107
+ if isinstance(result, str):
108
+ print(f"{filepath}: Detected as text - {result[:50]}...")
109
+ else:
110
+ print(f"{filepath}: Detected as binary - {len(result)} bytes")
111
+
112
+ print("\n✅ All examples completed successfully!")
113
+
114
+ except Exception as e:
115
+ print(f"❌ Error: {e}")
116
+
117
+ finally:
118
+ # Clean up
119
+ try:
120
+ await session.close()
121
+ print("🧹 Cleanup completed")
122
+ except Exception as e:
123
+ print(f"⚠️ Cleanup error: {e}")
124
+
125
+
126
+ if __name__ == "__main__":
127
+ asyncio.run(main())
@@ -0,0 +1,82 @@
1
+ #!/usr/bin/env python3
2
+ """Example usage of the new Grasp classes."""
3
+
4
+ import asyncio
5
+ import os
6
+ from grasp_sdk import Grasp, GraspSession, launch_browser
7
+
8
+
9
+ async def example_grasp_usage():
10
+ """Example demonstrating how to use the new Grasp classes."""
11
+ print("=== Grasp SDK Python Implementation Example ===")
12
+
13
+ # Initialize Grasp SDK
14
+ grasp = Grasp({
15
+ 'apiKey': os.environ.get('GRASP_KEY', 'your-api-key-here')
16
+ })
17
+
18
+ print(f"Initialized Grasp SDK with API key: {grasp.key[:8]}...")
19
+
20
+ # Example 1: Launch a new browser session
21
+ print("\n1. Launching a new browser session...")
22
+ try:
23
+ session = await grasp.launch({
24
+ 'browser': {
25
+ 'type': 'chromium',
26
+ 'headless': True,
27
+ 'timeout': 30000
28
+ }
29
+ })
30
+ print(f"✓ Session launched with ID: {session.id}")
31
+
32
+ # Access browser endpoint
33
+ browser_endpoint = session.browser.get_endpoint()
34
+ print(f"✓ Browser WebSocket endpoint: {browser_endpoint}")
35
+
36
+ # Create terminal
37
+ terminal = session.terminal.create()
38
+ print("✓ Terminal service created")
39
+
40
+ # Access file system
41
+ print(f"✓ File system service available: {type(session.files).__name__}")
42
+
43
+ # Close session
44
+ await session.close()
45
+ print("✓ Session closed successfully")
46
+
47
+ except Exception as e:
48
+ print(f"❌ Error launching session: {e}")
49
+ print("Note: This requires a valid API key and network access")
50
+
51
+ # Example 2: Using static method
52
+ print("\n2. Using static launch_browser method...")
53
+ try:
54
+ connection = await launch_browser({
55
+ 'type': 'chromium',
56
+ 'headless': True
57
+ })
58
+ print(f"✓ Browser launched with connection: {connection.ws_url}")
59
+
60
+ except Exception as e:
61
+ print(f"❌ Error with static method: {e}")
62
+ print("Note: This requires a valid API key and network access")
63
+
64
+ # Example 3: Connect to existing session
65
+ print("\n3. Connecting to existing session...")
66
+ try:
67
+ # This would connect to an existing session ID
68
+ # session = await grasp.connect('existing-session-id')
69
+ print("✓ Connection method available (requires existing session ID)")
70
+
71
+ except Exception as e:
72
+ print(f"❌ Error connecting: {e}")
73
+
74
+ print("\n=== Example completed ===")
75
+ print("\nFor full functionality, ensure you have:")
76
+ print("- Valid GRASP_KEY environment variable")
77
+ print("- Network access to Grasp services")
78
+ print("- Proper sandbox configuration")
79
+
80
+
81
+ if __name__ == '__main__':
82
+ asyncio.run(example_grasp_usage())
@@ -0,0 +1,136 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Example usage of the readFile functionality in Grasp Python SDK.
4
+
5
+ This example demonstrates:
6
+ 1. How to create a filesystem service
7
+ 2. How to write files to sandbox
8
+ 3. How to read files from sandbox
9
+ 4. Error handling for file operations
10
+ """
11
+
12
+ import asyncio
13
+ import os
14
+ from grasp_sdk import Grasp
15
+
16
+
17
+ async def main():
18
+ """Main example function."""
19
+ print("📚 Grasp SDK readFile Example")
20
+ print("==============================")
21
+
22
+ session = None
23
+ try:
24
+ # Launch browser and get session
25
+ print("\n🚀 Step 1: Launching browser...")
26
+ grasp = Grasp()
27
+ session = await grasp.launch({
28
+ 'browser': {
29
+ 'headless': True,
30
+ 'debug': False # Set to True for more detailed logs
31
+ },
32
+ 'keepAliveMS': 30000,
33
+ 'timeout': 60000
34
+ })
35
+ print(f"✅ Browser launched! Session ID: {session.id}")
36
+
37
+ # Get filesystem service from session
38
+ print("\n📁 Step 2: Getting filesystem service...")
39
+ filesystem = session.files
40
+ print("✅ Filesystem service ready!")
41
+
42
+ # Example 1: Write and read a text file
43
+ print("\n📝 Example 1: Text file operations")
44
+ print("-" * 40)
45
+
46
+ text_file_path = "/home/user/example.txt"
47
+ text_content = """Hello from Grasp SDK!
48
+
49
+ This is an example text file.
50
+ It contains multiple lines and unicode: 你好世界 🌍
51
+
52
+ Timestamp: $(date)
53
+ """
54
+
55
+ print(f"Writing content to {text_file_path}...")
56
+ await filesystem.write_file(text_file_path, text_content)
57
+ print("✅ File written successfully!")
58
+
59
+ print(f"Reading content from {text_file_path}...")
60
+ read_content = await filesystem.read_file(text_file_path)
61
+ print("✅ File read successfully!")
62
+ print(f"📄 Content preview (first 100 chars): {repr(read_content[:100])}...")
63
+
64
+ # Example 2: Read a configuration file
65
+ print("\n⚙️ Example 2: Configuration file")
66
+ print("-" * 40)
67
+
68
+ config_path = "/home/user/config.json"
69
+ config_content = '''{
70
+ "app_name": "My Grasp App",
71
+ "version": "1.0.0",
72
+ "settings": {
73
+ "debug": true,
74
+ "max_connections": 100,
75
+ "allowed_origins": ["localhost", "*.example.com"]
76
+ }
77
+ }'''
78
+
79
+ await filesystem.write_file(config_path, config_content)
80
+ config_data = await filesystem.read_file(config_path)
81
+
82
+ # Parse JSON
83
+ import json
84
+ parsed_config = json.loads(config_data)
85
+ print(f"✅ Config loaded: {parsed_config['app_name']} v{parsed_config['version']}")
86
+
87
+ # Example 3: Error handling
88
+ print("\n🚨 Example 3: Error handling")
89
+ print("-" * 40)
90
+
91
+ try:
92
+ await filesystem.read_file("/home/user/nonexistent.txt")
93
+ print("❌ This should not happen!")
94
+ except Exception as e:
95
+ print(f"✅ Correctly caught error: {type(e).__name__}: {e}")
96
+
97
+ # Example 4: Binary file handling (simulated)
98
+ print("\n🖼️ Example 4: Binary file handling")
99
+ print("-" * 40)
100
+
101
+ # Create a fake binary file (in real usage, this might be an image)
102
+ binary_path = "/home/user/data.bin"
103
+ binary_content = "This simulates binary data\x00\x01\x02\xFF"
104
+
105
+ await filesystem.write_file(binary_path, binary_content)
106
+ binary_data = await filesystem.read_file(binary_path)
107
+
108
+ print(f"✅ Binary file handled (length: {len(binary_data)} chars)")
109
+ print(f"📊 Data preview: {repr(binary_data[:50])}...")
110
+
111
+ print("\n🎉 All examples completed successfully!")
112
+
113
+ except Exception as e:
114
+ print(f"\n❌ Example failed: {e}")
115
+ import traceback
116
+ traceback.print_exc()
117
+
118
+ finally:
119
+ if session:
120
+ print("\n🧹 Cleaning up...")
121
+ await session.close()
122
+ print("✅ Cleanup completed!")
123
+
124
+
125
+ if __name__ == "__main__":
126
+ print("Starting Grasp SDK readFile example...")
127
+
128
+ # Check environment
129
+ if not os.getenv('GRASP_KEY'):
130
+ print("\n⚠️ Warning: GRASP_KEY environment variable not set.")
131
+ print(" Please set your Grasp API key to run this example:")
132
+ print(" export GRASP_KEY='your-api-key-here'")
133
+ print("\n You can get your API key from: https://grasp.dev/dashboard")
134
+ exit(1)
135
+
136
+ asyncio.run(main())
@@ -0,0 +1,110 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Grasp Terminal Example
4
+
5
+ This example demonstrates how to use the Grasp Terminal service
6
+ to run commands and manage files in a sandbox environment.
7
+
8
+ Equivalent to the TypeScript version in examples/grasp-terminal.ts
9
+ """
10
+
11
+ import asyncio
12
+ import os
13
+ import sys
14
+ from typing import Dict, Any
15
+ from dotenv import load_dotenv
16
+
17
+ # Add parent directory to path for imports
18
+ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
19
+
20
+ from grasp_sdk import Grasp
21
+
22
+ # 加载环境变量
23
+ load_dotenv("../.env.grasp")
24
+
25
+ async def main():
26
+ """Main function demonstrating terminal service usage."""
27
+ print("🚀 Starting Grasp Terminal example...")
28
+
29
+ try:
30
+ grasp = Grasp()
31
+ session = await grasp.launch({
32
+ 'browser': {
33
+ # 'type': 'chrome-stable',
34
+ # 'headless': False,
35
+ # 'adblock': True,
36
+ # 'debug': True,
37
+ },
38
+ 'keepAliveMS': 10000,
39
+ 'timeout': 3600000, # 容器最长运行1小时(最大值可以为一天 86400000)
40
+ })
41
+
42
+ connection = session.browser.connection
43
+ print(f"✅ Browser launched! Connection ID: {connection.id}")
44
+
45
+ # Create terminal connection
46
+ print("📡 Creating terminal connection...")
47
+ terminal = session.terminal.create()
48
+
49
+ print("✅ Terminal connected!")
50
+
51
+ # Test FFmpeg availability (equivalent to TypeScript version)
52
+ print("🔍 Checking FFmpeg availability...")
53
+ command = await terminal.run_command('ffmpeg')
54
+ result = await command.json()
55
+ print(f"FFmpeg version check result: {result}")
56
+
57
+ # Create and write a test JavaScript file
58
+ print("📝 Writing test JavaScript file...")
59
+ code = """
60
+ (async function() {
61
+ for(let i = 0; i < 10; i++) {
62
+ console.log(i);
63
+ await new Promise(resolve => setTimeout(resolve, 200));
64
+ }
65
+ })();
66
+ """
67
+ await session.files.write_file('test.js', code)
68
+ print("✅ File 'test.js' written successfully!")
69
+
70
+ # Run the Node.js script
71
+ print("🚀 Running Node.js script...")
72
+ command2 = await terminal.run_command('node test.js')
73
+
74
+ # Handle streaming output (Python equivalent of TypeScript pipe)
75
+ print("📊 Streaming output:")
76
+
77
+ # Set up stdout streaming
78
+ # Set up event handlers for real-time output (much simpler!)
79
+ def handle_stdout(data):
80
+ print(f"STDOUT: {data}", end='')
81
+
82
+ def handle_stderr(data):
83
+ print(f"STDERR: {data}", end='', file=sys.stderr)
84
+
85
+ # Register event handlers - now it works just like Node.js!
86
+ command2.on('stdout', handle_stdout)
87
+ command2.on('stderr', handle_stderr)
88
+
89
+ # Wait for command completion
90
+ await command2.end()
91
+
92
+ # Close terminal connection
93
+ # print("🔌 Closing terminal connection...")
94
+ # terminal.close()
95
+
96
+ # Close the session
97
+ print("🛑 Closing session...")
98
+ await session.close()
99
+
100
+ print("✅ Example completed successfully!")
101
+
102
+ except Exception as error:
103
+ print(f"❌ Error occurred: {error}")
104
+ import traceback
105
+ traceback.print_exc()
106
+
107
+
108
+ if __name__ == '__main__':
109
+ # Run the async main function
110
+ asyncio.run(main())
@@ -11,7 +11,15 @@ from pathlib import Path
11
11
  from playwright.async_api import async_playwright
12
12
  from dotenv import load_dotenv
13
13
 
14
- from grasp_sdk import GraspServer
14
+ import sys
15
+ from pathlib import Path
16
+
17
+ from websockets import connect
18
+
19
+ # 添加父目录到 Python 路径,以便导入 grasp_sdk
20
+ sys.path.insert(0, str(Path(__file__).parent.parent))
21
+
22
+ from grasp_sdk import Grasp, GraspServer
15
23
  from grasp_sdk.models import IBrowserConfig, ISandboxConfig
16
24
 
17
25
  # 加载环境变量
@@ -31,24 +39,26 @@ async def main():
31
39
 
32
40
  print("🚀 正在启动浏览器...")
33
41
 
34
- async with GraspServer({
35
- # 'key': api_key,
36
- # 'type': 'chrome-stable',
37
- # 'headless': False,
38
- # 'adblock': True,
39
- # 'debug': True,
42
+ async with Grasp().launch_context({
43
+ 'browser': {
44
+ # 'type': 'chrome-stable',
45
+ # 'headless': False,
46
+ # 'adblock': True,
47
+ },
48
+ 'debug': True,
40
49
  'timeout': 3600000, # 容器最长运行1小时(最大值可以为一天 86400000)
41
- }) as connection:
42
-
50
+ }) as session:
51
+
43
52
  try:
53
+ connection = session.browser.connection
44
54
  print(f"连接信息: {connection}")
45
- print(f"WebSocket URL: {connection['ws_url']}")
46
- print(f"HTTP URL: {connection['http_url']}")
55
+ print(f"WebSocket URL: {connection.ws_url}")
56
+ print(f"HTTP URL: {connection.http_url}")
47
57
 
48
58
  # 使用 Playwright 连接到 CDP
49
59
  async with async_playwright() as p:
50
60
  browser = await p.chromium.connect_over_cdp(
51
- connection['ws_url'],
61
+ session.browser.get_endpoint(),
52
62
  timeout=150000
53
63
  )
54
64