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.
- {grasp_sdk-0.1.8/grasp_sdk.egg-info → grasp_sdk-0.2.0a1}/PKG-INFO +2 -3
- grasp_sdk-0.2.0a1/examples/example_async_context.py +122 -0
- grasp_sdk-0.2.0a1/examples/example_binary_file_support.py +127 -0
- grasp_sdk-0.2.0a1/examples/example_grasp_usage.py +82 -0
- grasp_sdk-0.2.0a1/examples/example_readfile_usage.py +136 -0
- grasp_sdk-0.2.0a1/examples/grasp_terminal.py +110 -0
- grasp_sdk-0.1.8/example_usage.py → grasp_sdk-0.2.0a1/examples/grasp_usage.py +22 -12
- grasp_sdk-0.2.0a1/examples/test_async_context.py +64 -0
- grasp_sdk-0.2.0a1/examples/test_get_replay_screenshots.py +90 -0
- grasp_sdk-0.2.0a1/examples/test_grasp_classes.py +80 -0
- grasp_sdk-0.2.0a1/examples/test_python_script.py +160 -0
- grasp_sdk-0.2.0a1/examples/test_removed_methods.py +80 -0
- grasp_sdk-0.2.0a1/examples/test_shutdown_deprecation.py +62 -0
- grasp_sdk-0.2.0a1/examples/test_terminal_updates.py +196 -0
- grasp_sdk-0.2.0a1/grasp_sdk/__init__.py +183 -0
- grasp_sdk-0.2.0a1/grasp_sdk/grasp/__init__.py +26 -0
- grasp_sdk-0.2.0a1/grasp_sdk/grasp/browser.py +69 -0
- grasp_sdk-0.2.0a1/grasp_sdk/grasp/index.py +122 -0
- grasp_sdk-0.1.8/grasp_sdk/__init__.py → grasp_sdk-0.2.0a1/grasp_sdk/grasp/server.py +74 -115
- grasp_sdk-0.2.0a1/grasp_sdk/grasp/session.py +108 -0
- grasp_sdk-0.2.0a1/grasp_sdk/grasp/terminal.py +32 -0
- grasp_sdk-0.2.0a1/grasp_sdk/grasp/utils.py +90 -0
- {grasp_sdk-0.1.8 → grasp_sdk-0.2.0a1}/grasp_sdk/models/__init__.py +1 -1
- grasp_sdk-0.2.0a1/grasp_sdk/services/__init__.py +15 -0
- {grasp_sdk-0.1.8 → grasp_sdk-0.2.0a1}/grasp_sdk/services/browser.py +92 -63
- grasp_sdk-0.2.0a1/grasp_sdk/services/filesystem.py +94 -0
- {grasp_sdk-0.1.8 → grasp_sdk-0.2.0a1}/grasp_sdk/services/sandbox.py +391 -20
- grasp_sdk-0.2.0a1/grasp_sdk/services/terminal.py +177 -0
- {grasp_sdk-0.1.8 → grasp_sdk-0.2.0a1}/grasp_sdk/utils/auth.py +6 -8
- {grasp_sdk-0.1.8 → grasp_sdk-0.2.0a1/grasp_sdk.egg-info}/PKG-INFO +2 -3
- grasp_sdk-0.2.0a1/grasp_sdk.egg-info/SOURCES.txt +79 -0
- {grasp_sdk-0.1.8 → grasp_sdk-0.2.0a1}/grasp_sdk.egg-info/requires.txt +1 -3
- {grasp_sdk-0.1.8 → grasp_sdk-0.2.0a1}/grasp_sdk.egg-info/top_level.txt +1 -0
- {grasp_sdk-0.1.8 → grasp_sdk-0.2.0a1}/pyproject.toml +2 -4
- {grasp_sdk-0.1.8 → grasp_sdk-0.2.0a1}/requirements.txt +2 -1
- grasp_sdk-0.1.8/grasp_sdk/sandbox/bootstrap-chrome-stable.mjs +0 -69
- grasp_sdk-0.1.8/grasp_sdk/sandbox/chrome-stable.mjs +0 -424
- grasp_sdk-0.1.8/grasp_sdk/sandbox/chromium.mjs +0 -395
- grasp_sdk-0.1.8/grasp_sdk/sandbox/http-proxy.mjs +0 -324
- grasp_sdk-0.1.8/grasp_sdk/services/__init__.py +0 -8
- grasp_sdk-0.1.8/grasp_sdk.egg-info/SOURCES.txt +0 -43
- grasp_sdk-0.1.8/test_install.py +0 -87
- {grasp_sdk-0.1.8 → grasp_sdk-0.2.0a1}/MANIFEST.in +0 -0
- {grasp_sdk-0.1.8 → grasp_sdk-0.2.0a1}/README.md +0 -0
- {grasp_sdk-0.1.8 → grasp_sdk-0.2.0a1}/build_and_publish.py +0 -0
- {grasp_sdk-0.1.8 → grasp_sdk-0.2.0a1}/grasp_sdk/utils/__init__.py +0 -0
- {grasp_sdk-0.1.8 → grasp_sdk-0.2.0a1}/grasp_sdk/utils/config.py +0 -0
- {grasp_sdk-0.1.8 → grasp_sdk-0.2.0a1}/grasp_sdk/utils/logger.py +0 -0
- {grasp_sdk-0.1.8 → grasp_sdk-0.2.0a1}/grasp_sdk.egg-info/dependency_links.txt +0 -0
- {grasp_sdk-0.1.8 → grasp_sdk-0.2.0a1}/grasp_sdk.egg-info/entry_points.txt +0 -0
- {grasp_sdk-0.1.8 → grasp_sdk-0.2.0a1}/grasp_sdk.egg-info/not-zip-safe +0 -0
- {grasp_sdk-0.1.8 → grasp_sdk-0.2.0a1}/py.typed +0 -0
- {grasp_sdk-0.1.8 → grasp_sdk-0.2.0a1}/setup.cfg +0 -0
- {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.
|
|
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
|
-
|
|
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
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
|
42
|
-
|
|
50
|
+
}) as session:
|
|
51
|
+
|
|
43
52
|
try:
|
|
53
|
+
connection = session.browser.connection
|
|
44
54
|
print(f"连接信息: {connection}")
|
|
45
|
-
print(f"WebSocket URL: {connection
|
|
46
|
-
print(f"HTTP URL: {connection
|
|
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
|
-
|
|
61
|
+
session.browser.get_endpoint(),
|
|
52
62
|
timeout=150000
|
|
53
63
|
)
|
|
54
64
|
|