agentfense 0.2.1__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.
Files changed (35) hide show
  1. agentfense-0.2.1/PKG-INFO +378 -0
  2. agentfense-0.2.1/README.md +344 -0
  3. agentfense-0.2.1/agentfense/__init__.py +191 -0
  4. agentfense-0.2.1/agentfense/_async/__init__.py +21 -0
  5. agentfense-0.2.1/agentfense/_async/client.py +679 -0
  6. agentfense-0.2.1/agentfense/_async/sandbox.py +667 -0
  7. agentfense-0.2.1/agentfense/_gen/__init__.py +0 -0
  8. agentfense-0.2.1/agentfense/_gen/codebase_pb2.py +78 -0
  9. agentfense-0.2.1/agentfense/_gen/codebase_pb2.pyi +141 -0
  10. agentfense-0.2.1/agentfense/_gen/codebase_pb2_grpc.py +366 -0
  11. agentfense-0.2.1/agentfense/_gen/common_pb2.py +47 -0
  12. agentfense-0.2.1/agentfense/_gen/common_pb2.pyi +68 -0
  13. agentfense-0.2.1/agentfense/_gen/common_pb2_grpc.py +24 -0
  14. agentfense-0.2.1/agentfense/_gen/sandbox_pb2.py +123 -0
  15. agentfense-0.2.1/agentfense/_gen/sandbox_pb2.pyi +255 -0
  16. agentfense-0.2.1/agentfense/_gen/sandbox_pb2_grpc.py +678 -0
  17. agentfense-0.2.1/agentfense/_shared.py +238 -0
  18. agentfense-0.2.1/agentfense/client.py +751 -0
  19. agentfense-0.2.1/agentfense/exceptions.py +333 -0
  20. agentfense-0.2.1/agentfense/presets.py +192 -0
  21. agentfense-0.2.1/agentfense/sandbox.py +672 -0
  22. agentfense-0.2.1/agentfense/types.py +256 -0
  23. agentfense-0.2.1/agentfense/utils.py +286 -0
  24. agentfense-0.2.1/agentfense.egg-info/PKG-INFO +378 -0
  25. agentfense-0.2.1/agentfense.egg-info/SOURCES.txt +33 -0
  26. agentfense-0.2.1/agentfense.egg-info/dependency_links.txt +1 -0
  27. agentfense-0.2.1/agentfense.egg-info/requires.txt +11 -0
  28. agentfense-0.2.1/agentfense.egg-info/top_level.txt +1 -0
  29. agentfense-0.2.1/pyproject.toml +75 -0
  30. agentfense-0.2.1/setup.cfg +4 -0
  31. agentfense-0.2.1/tests/test_async.py +283 -0
  32. agentfense-0.2.1/tests/test_presets.py +197 -0
  33. agentfense-0.2.1/tests/test_sandbox.py +531 -0
  34. agentfense-0.2.1/tests/test_session.py +158 -0
  35. agentfense-0.2.1/tests/test_types.py +202 -0
@@ -0,0 +1,378 @@
1
+ Metadata-Version: 2.4
2
+ Name: agentfense
3
+ Version: 0.2.1
4
+ Summary: Python SDK for AgentFense - A sandbox infrastructure service for AI Agents with fine-grained file permission control
5
+ Author-email: Ajax Zhan <ajaxzhan@example.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/AjaxZhan/AgentFense
8
+ Project-URL: Documentation, https://github.com/AjaxZhan/AgentFense#readme
9
+ Project-URL: Repository, https://github.com/AjaxZhan/AgentFense
10
+ Project-URL: Issues, https://github.com/AjaxZhan/AgentFense/issues
11
+ Keywords: agentfense,sandbox,ai,agent,grpc,isolation
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.9
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
+ Requires-Python: >=3.9
23
+ Description-Content-Type: text/markdown
24
+ Requires-Dist: grpcio>=1.50.0
25
+ Requires-Dist: protobuf>=4.21.0
26
+ Requires-Dist: googleapis-common-protos>=1.60.0
27
+ Provides-Extra: dev
28
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
29
+ Requires-Dist: pytest-asyncio>=0.20.0; extra == "dev"
30
+ Requires-Dist: grpcio-tools>=1.50.0; extra == "dev"
31
+ Requires-Dist: black>=23.0.0; extra == "dev"
32
+ Requires-Dist: mypy>=1.0.0; extra == "dev"
33
+ Requires-Dist: ruff>=0.0.260; extra == "dev"
34
+
35
+ # AgentFense SDK for Python
36
+
37
+ Python client library for the AgentFense service - a sandbox infrastructure for AI Agents with fine-grained file permission control.
38
+
39
+ ## Installation
40
+
41
+ ```bash
42
+ pip install agentfense
43
+ ```
44
+
45
+ Or install from source:
46
+
47
+ ```bash
48
+ cd sdk/python
49
+ pip install -e .
50
+ ```
51
+
52
+ ## Quick Start (High-Level API)
53
+
54
+ The easiest way to use the SDK is with the high-level `Sandbox` class:
55
+
56
+ ```python
57
+ from agentfense import Sandbox
58
+
59
+ # One-liner to create a sandbox from a local directory
60
+ with Sandbox.from_local("./my-project") as sandbox:
61
+ result = sandbox.run("python main.py")
62
+ print(result.stdout)
63
+ ```
64
+
65
+ This automatically:
66
+ 1. Creates a codebase
67
+ 2. Uploads all files from the directory
68
+ 3. Creates a sandbox with the "agent-safe" preset
69
+ 4. Starts the sandbox
70
+ 5. Cleans up everything when done
71
+
72
+ ### Using Docker Runtime with Resource Limits
73
+
74
+ ```python
75
+ from agentfense import Sandbox, RuntimeType, ResourceLimits
76
+
77
+ with Sandbox.from_local(
78
+ "./my-project",
79
+ preset="agent-safe",
80
+ runtime=RuntimeType.DOCKER,
81
+ image="python:3.11-slim",
82
+ resources=ResourceLimits(
83
+ memory_bytes=512 * 1024 * 1024, # 512 MB
84
+ pids_limit=100,
85
+ ),
86
+ ) as sandbox:
87
+ # Use a session for stateful commands
88
+ with sandbox.session() as session:
89
+ session.exec("cd /workspace")
90
+ session.exec("pip install -r requirements.txt")
91
+ result = session.exec("pytest")
92
+ print(result.stdout)
93
+ ```
94
+
95
+ ### Permission Presets
96
+
97
+ The SDK includes several built-in permission presets:
98
+
99
+ | Preset | Description |
100
+ |--------|-------------|
101
+ | `agent-safe` | Read all files, write to /output and /tmp, hide secrets (.env, *.key, etc.) |
102
+ | `read-only` | Read all files, no write access |
103
+ | `full-access` | Full read/write access to all files |
104
+ | `development` | Full access except secrets |
105
+ | `view-only` | Can see file names but not read content |
106
+
107
+ ```python
108
+ from agentfense import list_presets, get_preset, extend_preset
109
+
110
+ # List all available presets
111
+ print(list_presets()) # ['agent-safe', 'development', 'full-access', 'read-only', 'view-only']
112
+
113
+ # Extend a preset with custom rules
114
+ rules = extend_preset(
115
+ "agent-safe",
116
+ additions=[{"pattern": "/custom/**", "permission": "write"}],
117
+ )
118
+ ```
119
+
120
+ ### Error Handling
121
+
122
+ The SDK provides semantic exception classes:
123
+
124
+ ```python
125
+ from agentfense import Sandbox, SandboxError, CommandTimeoutError, CommandExecutionError
126
+
127
+ try:
128
+ with Sandbox.from_local("./my-project") as sandbox:
129
+ result = sandbox.run("python main.py", timeout=30, raise_on_error=True)
130
+ except CommandTimeoutError as e:
131
+ print(f"Command timed out: {e}")
132
+ except CommandExecutionError as e:
133
+ print(f"Command failed with exit code {e.exit_code}: {e.stderr}")
134
+ except SandboxError as e:
135
+ print(f"Sandbox error: {e}")
136
+ ```
137
+
138
+ ## Low-Level API
139
+
140
+ For fine-grained control, use the `SandboxClient` directly:
141
+
142
+ ```python
143
+ from agentfense import SandboxClient, RuntimeType, ResourceLimits
144
+
145
+ # Connect to the sandbox server
146
+ client = SandboxClient(endpoint="localhost:9000")
147
+
148
+ # Create a codebase (file storage)
149
+ codebase = client.create_codebase(name="my-project", owner_id="user_123")
150
+
151
+ # Upload a file
152
+ client.upload_file(codebase.id, "hello.py", b'print("Hello, World!")')
153
+
154
+ # Create a sandbox with permissions and Docker runtime
155
+ sandbox = client.create_sandbox(
156
+ codebase_id=codebase.id,
157
+ permissions=[
158
+ {"pattern": "**/*.py", "permission": "read"},
159
+ {"pattern": "/docs/**", "permission": "write"},
160
+ ],
161
+ runtime=RuntimeType.DOCKER,
162
+ image="python:3.11-slim",
163
+ resources=ResourceLimits(memory_bytes=256 * 1024 * 1024),
164
+ )
165
+
166
+ # Start the sandbox
167
+ client.start_sandbox(sandbox.id)
168
+
169
+ # Execute a command
170
+ result = client.exec(sandbox.id, command="python hello.py")
171
+ print(result.stdout) # Output: Hello, World!
172
+
173
+ # Clean up
174
+ client.destroy_sandbox(sandbox.id)
175
+ client.delete_codebase(codebase.id)
176
+ ```
177
+
178
+ ## Features
179
+
180
+ ### Permission Levels
181
+
182
+ The SDK supports four permission levels:
183
+
184
+ | Level | Description |
185
+ |-------|-------------|
186
+ | `none` | Completely invisible, not shown in `ls` |
187
+ | `view` | Visible in `ls`, but cannot read content |
188
+ | `read` | Can read file content |
189
+ | `write` | Can modify file |
190
+
191
+ ### Permission Patterns
192
+
193
+ You can use glob patterns, directory paths, or file paths:
194
+
195
+ ```python
196
+ from agentfense import PermissionRule, Permission, PatternType
197
+
198
+ permissions = [
199
+ # Glob pattern - matches all .py files
200
+ PermissionRule(pattern="**/*.py", permission=Permission.READ),
201
+
202
+ # Directory - applies to entire directory
203
+ PermissionRule(pattern="/docs/", permission=Permission.WRITE, type=PatternType.DIRECTORY),
204
+
205
+ # Specific file
206
+ PermissionRule(pattern="/config.yaml", permission=Permission.VIEW, type=PatternType.FILE),
207
+ ]
208
+
209
+ sandbox = client.create_sandbox(codebase_id=codebase.id, permissions=permissions)
210
+ ```
211
+
212
+ ### Stateful Sessions
213
+
214
+ Sessions maintain a persistent shell process that preserves working directory and environment variables:
215
+
216
+ ```python
217
+ with client.session(sandbox.id) as session:
218
+ session.exec("cd /workspace/src") # Change directory
219
+ session.exec("export DEBUG=1") # Set environment variable
220
+ session.exec("source venv/bin/activate") # Activate virtualenv
221
+ result = session.exec("python main.py") # All above state is preserved
222
+ ```
223
+
224
+ ### Command Execution
225
+
226
+ Execute commands with optional stdin, environment variables, and timeout:
227
+
228
+ ```python
229
+ from datetime import timedelta
230
+
231
+ result = client.exec(
232
+ sandbox_id=sandbox.id,
233
+ command="python script.py",
234
+ stdin="input data",
235
+ env={"DEBUG": "1"},
236
+ workdir="/workspace/src",
237
+ timeout=timedelta(seconds=30),
238
+ )
239
+
240
+ print(f"Exit code: {result.exit_code}")
241
+ print(f"stdout: {result.stdout}")
242
+ print(f"stderr: {result.stderr}")
243
+ print(f"Duration: {result.duration}")
244
+ ```
245
+
246
+ ### Streaming Output
247
+
248
+ For long-running commands, use streaming:
249
+
250
+ ```python
251
+ for chunk in client.exec_stream(sandbox.id, command="long-running-task"):
252
+ print(chunk.decode(), end="")
253
+ ```
254
+
255
+ ### File Operations
256
+
257
+ ```python
258
+ # List files
259
+ files = client.list_files(codebase.id, path="/src", recursive=True)
260
+ for f in files:
261
+ print(f"{f.path} - {f.size} bytes")
262
+
263
+ # Download a file
264
+ content = client.download_file(codebase.id, "config.yaml")
265
+ print(content.decode())
266
+
267
+ # Upload a file
268
+ result = client.upload_file(codebase.id, "new_file.txt", b"file content")
269
+ print(f"Uploaded {result.size} bytes, checksum: {result.checksum}")
270
+ ```
271
+
272
+ ## Context Manager
273
+
274
+ Both the high-level `Sandbox` and low-level `SandboxClient` support context managers:
275
+
276
+ ```python
277
+ # High-level (recommended)
278
+ with Sandbox.from_local("./project") as sandbox:
279
+ sandbox.run("make test")
280
+ # Automatically cleans up sandbox and codebase
281
+
282
+ # Low-level
283
+ with SandboxClient(endpoint="localhost:9000") as client:
284
+ sandbox = client.create_sandbox(codebase_id="cb_123")
285
+ # ... use the sandbox ...
286
+ # Connection is automatically closed
287
+ ```
288
+
289
+ ## API Reference
290
+
291
+ ### High-Level API
292
+
293
+ #### Sandbox Class
294
+
295
+ - `Sandbox.from_local(path, preset, permissions, runtime, image, resources, ...)` - Create sandbox from local directory
296
+ - `Sandbox.from_codebase(codebase_id, preset, ...)` - Create sandbox from existing codebase
297
+ - `Sandbox.connect(sandbox_id)` - Connect to existing sandbox
298
+ - `sandbox.run(command, timeout, env, workdir, raise_on_error)` - Execute command (simplified)
299
+ - `sandbox.exec(command, stdin, env, workdir, timeout)` - Execute command (full API)
300
+ - `sandbox.session(shell, env)` - Create stateful session
301
+ - `sandbox.read_file(path)` - Read file as string
302
+ - `sandbox.write_file(path, content)` - Write file
303
+ - `sandbox.list_files(path, recursive)` - List files
304
+ - `sandbox.start()` / `sandbox.stop()` - Lifecycle control
305
+ - `sandbox.destroy(delete_codebase)` - Clean up
306
+
307
+ #### Presets
308
+
309
+ - `get_preset(name)` - Get preset as PermissionRule list
310
+ - `get_preset_dicts(name)` - Get preset as dict list
311
+ - `extend_preset(base, additions, overrides)` - Extend preset
312
+ - `list_presets()` - List available presets
313
+ - `register_preset(name, rules)` - Register custom preset
314
+
315
+ ### Low-Level API
316
+
317
+ #### SandboxClient
318
+
319
+ - `create_sandbox(codebase_id, permissions, runtime, image, resources, labels, expires_in)` - Create sandbox
320
+ - `get_sandbox(sandbox_id)` - Get sandbox info
321
+ - `list_sandboxes(codebase_id)` - List sandboxes
322
+ - `start_sandbox(sandbox_id)` - Start sandbox
323
+ - `stop_sandbox(sandbox_id)` - Stop sandbox
324
+ - `destroy_sandbox(sandbox_id)` - Destroy sandbox
325
+ - `exec(sandbox_id, command, stdin, env, workdir, timeout)` - Execute command
326
+ - `exec_stream(sandbox_id, command, ...)` - Execute with streaming
327
+ - `create_session(sandbox_id, shell, env)` - Create session
328
+ - `session_exec(session_id, command, timeout)` - Execute in session
329
+ - `create_codebase(name, owner_id)` - Create codebase
330
+ - `get_codebase(codebase_id)` - Get codebase info
331
+ - `list_codebases(owner_id)` - List codebases
332
+ - `delete_codebase(codebase_id)` - Delete codebase
333
+ - `upload_file(codebase_id, file_path, content)` - Upload file
334
+ - `download_file(codebase_id, file_path)` - Download file
335
+ - `list_files(codebase_id, path, recursive)` - List files
336
+
337
+ ### Types
338
+
339
+ - `RuntimeType` - BWRAP, DOCKER
340
+ - `ResourceLimits` - memory_bytes, cpu_quota, cpu_shares, pids_limit
341
+ - `Permission` - NONE, VIEW, READ, WRITE
342
+ - `PatternType` - GLOB, DIRECTORY, FILE
343
+ - `SandboxStatus` - PENDING, RUNNING, STOPPED, ERROR
344
+ - `SessionStatus` - UNKNOWN, ACTIVE, CLOSED
345
+
346
+ ### Exceptions
347
+
348
+ - `SandboxError` - Base exception
349
+ - `SandboxNotFoundError` - Sandbox not found
350
+ - `SandboxNotRunningError` - Sandbox not running
351
+ - `CodebaseError` - Codebase operation error
352
+ - `CodebaseNotFoundError` - Codebase not found
353
+ - `CommandTimeoutError` - Command timed out
354
+ - `CommandExecutionError` - Command failed (non-zero exit)
355
+ - `PermissionDeniedError` - Permission denied
356
+ - `SessionError` - Session operation error
357
+ - `ConnectionError` - Connection to service failed
358
+
359
+ ## Development
360
+
361
+ ```bash
362
+ # Install dev dependencies
363
+ pip install -e ".[dev]"
364
+
365
+ # Run tests
366
+ pytest
367
+
368
+ # Format code
369
+ black agentfense tests
370
+ ruff check --fix agentfense tests
371
+
372
+ # Type check
373
+ mypy agentfense
374
+ ```
375
+
376
+ ## License
377
+
378
+ MIT