axern-sdk 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 (206) hide show
  1. axern_sdk-0.2.1/.gitignore +51 -0
  2. axern_sdk-0.2.1/AGENTS.md +75 -0
  3. axern_sdk-0.2.1/PKG-INFO +373 -0
  4. axern_sdk-0.2.1/README.md +358 -0
  5. axern_sdk-0.2.1/examples/_context.py +13 -0
  6. axern_sdk-0.2.1/examples/async_sandbox_programming.py +54 -0
  7. axern_sdk-0.2.1/examples/computer_use.py +42 -0
  8. axern_sdk-0.2.1/examples/function_manifest.py +30 -0
  9. axern_sdk-0.2.1/examples/sandbox_programming.py +70 -0
  10. axern_sdk-0.2.1/examples/sandbox_volume.py +41 -0
  11. axern_sdk-0.2.1/examples/service_gateway.py +142 -0
  12. axern_sdk-0.2.1/pyproject.toml +31 -0
  13. axern_sdk-0.2.1/pyrightconfig.json +17 -0
  14. axern_sdk-0.2.1/scripts/generate_proto.sh +20 -0
  15. axern_sdk-0.2.1/src/axern/__init__.py +1 -0
  16. axern_sdk-0.2.1/src/axern/common/file/v1/__init__.py +6 -0
  17. axern_sdk-0.2.1/src/axern/common/file/v1/file_pb2.py +43 -0
  18. axern_sdk-0.2.1/src/axern/common/file/v1/file_pb2.pyi +47 -0
  19. axern_sdk-0.2.1/src/axern/common/file/v1/file_pb2_grpc.py +24 -0
  20. axern_sdk-0.2.1/src/axern/control/__init__.py +1 -0
  21. axern_sdk-0.2.1/src/axern/control/admin/v1/__init__.py +21 -0
  22. axern_sdk-0.2.1/src/axern/control/admin/v1/allocation_lifecycle_pb2.py +62 -0
  23. axern_sdk-0.2.1/src/axern/control/admin/v1/allocation_lifecycle_pb2.pyi +139 -0
  24. axern_sdk-0.2.1/src/axern/control/admin/v1/allocation_lifecycle_pb2_grpc.py +226 -0
  25. axern_sdk-0.2.1/src/axern/control/admin/v1/audit_pb2.py +50 -0
  26. axern_sdk-0.2.1/src/axern/control/admin/v1/audit_pb2.pyi +81 -0
  27. axern_sdk-0.2.1/src/axern/control/admin/v1/audit_pb2_grpc.py +97 -0
  28. axern_sdk-0.2.1/src/axern/control/admin/v1/node_pb2.py +50 -0
  29. axern_sdk-0.2.1/src/axern/control/admin/v1/node_pb2.pyi +72 -0
  30. axern_sdk-0.2.1/src/axern/control/admin/v1/node_pb2_grpc.py +140 -0
  31. axern_sdk-0.2.1/src/axern/control/admin/v1/reliability_pb2.py +80 -0
  32. axern_sdk-0.2.1/src/axern/control/admin/v1/reliability_pb2.pyi +309 -0
  33. axern_sdk-0.2.1/src/axern/control/admin/v1/reliability_pb2_grpc.py +140 -0
  34. axern_sdk-0.2.1/src/axern/control/admin/v1/service_pb2.py +41 -0
  35. axern_sdk-0.2.1/src/axern/control/admin/v1/service_pb2.pyi +19 -0
  36. axern_sdk-0.2.1/src/axern/control/admin/v1/service_pb2_grpc.py +97 -0
  37. axern_sdk-0.2.1/src/axern/control/admin/v1/storage_pb2.py +57 -0
  38. axern_sdk-0.2.1/src/axern/control/admin/v1/storage_pb2.pyi +127 -0
  39. axern_sdk-0.2.1/src/axern/control/admin/v1/storage_pb2_grpc.py +183 -0
  40. axern_sdk-0.2.1/src/axern/control/agentprofile/v1/agent_profile_pb2.py +100 -0
  41. axern_sdk-0.2.1/src/axern/control/agentprofile/v1/agent_profile_pb2.pyi +293 -0
  42. axern_sdk-0.2.1/src/axern/control/agentprofile/v1/agent_profile_pb2_grpc.py +355 -0
  43. axern_sdk-0.2.1/src/axern/control/catalog/__init__.py +1 -0
  44. axern_sdk-0.2.1/src/axern/control/catalog/v1/__init__.py +6 -0
  45. axern_sdk-0.2.1/src/axern/control/catalog/v1/catalog_pb2.py +81 -0
  46. axern_sdk-0.2.1/src/axern/control/catalog/v1/catalog_pb2.pyi +203 -0
  47. axern_sdk-0.2.1/src/axern/control/catalog/v1/catalog_pb2_grpc.py +226 -0
  48. axern_sdk-0.2.1/src/axern/control/common/__init__.py +0 -0
  49. axern_sdk-0.2.1/src/axern/control/common/v1/__init__.py +6 -0
  50. axern_sdk-0.2.1/src/axern/control/common/v1/common_pb2.py +85 -0
  51. axern_sdk-0.2.1/src/axern/control/common/v1/common_pb2.pyi +299 -0
  52. axern_sdk-0.2.1/src/axern/control/common/v1/common_pb2_grpc.py +24 -0
  53. axern_sdk-0.2.1/src/axern/control/environment/__init__.py +1 -0
  54. axern_sdk-0.2.1/src/axern/control/environment/v1/__init__.py +6 -0
  55. axern_sdk-0.2.1/src/axern/control/environment/v1/environment_pb2.py +77 -0
  56. axern_sdk-0.2.1/src/axern/control/environment/v1/environment_pb2.pyi +164 -0
  57. axern_sdk-0.2.1/src/axern/control/environment/v1/environment_pb2_grpc.py +226 -0
  58. axern_sdk-0.2.1/src/axern/control/function/__init__.py +1 -0
  59. axern_sdk-0.2.1/src/axern/control/function/v1/__init__.py +8 -0
  60. axern_sdk-0.2.1/src/axern/control/function/v1/function_pb2.py +85 -0
  61. axern_sdk-0.2.1/src/axern/control/function/v1/function_pb2.pyi +203 -0
  62. axern_sdk-0.2.1/src/axern/control/function/v1/function_pb2_grpc.py +441 -0
  63. axern_sdk-0.2.1/src/axern/control/function/v1/function_types_pb2.py +113 -0
  64. axern_sdk-0.2.1/src/axern/control/function/v1/function_types_pb2.pyi +446 -0
  65. axern_sdk-0.2.1/src/axern/control/function/v1/function_types_pb2_grpc.py +24 -0
  66. axern_sdk-0.2.1/src/axern/control/gateway/v1/__init__.py +6 -0
  67. axern_sdk-0.2.1/src/axern/control/gateway/v1/gateway_pb2.py +51 -0
  68. axern_sdk-0.2.1/src/axern/control/gateway/v1/gateway_pb2.pyi +91 -0
  69. axern_sdk-0.2.1/src/axern/control/gateway/v1/gateway_pb2_grpc.py +140 -0
  70. axern_sdk-0.2.1/src/axern/control/namespace/v1/__init__.py +6 -0
  71. axern_sdk-0.2.1/src/axern/control/namespace/v1/namespace_pb2.py +56 -0
  72. axern_sdk-0.2.1/src/axern/control/namespace/v1/namespace_pb2.pyi +68 -0
  73. axern_sdk-0.2.1/src/axern/control/namespace/v1/namespace_pb2_grpc.py +226 -0
  74. axern_sdk-0.2.1/src/axern/control/node/v1/__init__.py +6 -0
  75. axern_sdk-0.2.1/src/axern/control/node/v1/node_control_pb2.py +112 -0
  76. axern_sdk-0.2.1/src/axern/control/node/v1/node_control_pb2.pyi +520 -0
  77. axern_sdk-0.2.1/src/axern/control/node/v1/node_control_pb2_grpc.py +312 -0
  78. axern_sdk-0.2.1/src/axern/control/quota/v1/__init__.py +6 -0
  79. axern_sdk-0.2.1/src/axern/control/quota/v1/quota_pb2.py +71 -0
  80. axern_sdk-0.2.1/src/axern/control/quota/v1/quota_pb2.pyi +171 -0
  81. axern_sdk-0.2.1/src/axern/control/quota/v1/quota_pb2_grpc.py +269 -0
  82. axern_sdk-0.2.1/src/axern/control/rollout/v1/rollout_pb2.py +165 -0
  83. axern_sdk-0.2.1/src/axern/control/rollout/v1/rollout_pb2.pyi +745 -0
  84. axern_sdk-0.2.1/src/axern/control/rollout/v1/rollout_pb2_grpc.py +570 -0
  85. axern_sdk-0.2.1/src/axern/control/run/__init__.py +1 -0
  86. axern_sdk-0.2.1/src/axern/control/run/v1/__init__.py +6 -0
  87. axern_sdk-0.2.1/src/axern/control/run/v1/run_pb2.py +73 -0
  88. axern_sdk-0.2.1/src/axern/control/run/v1/run_pb2.pyi +156 -0
  89. axern_sdk-0.2.1/src/axern/control/run/v1/run_pb2_grpc.py +226 -0
  90. axern_sdk-0.2.1/src/axern/control/secret/v1/__init__.py +6 -0
  91. axern_sdk-0.2.1/src/axern/control/secret/v1/secret_pb2.py +76 -0
  92. axern_sdk-0.2.1/src/axern/control/secret/v1/secret_pb2.pyi +138 -0
  93. axern_sdk-0.2.1/src/axern/control/secret/v1/secret_pb2_grpc.py +226 -0
  94. axern_sdk-0.2.1/src/axern/control/service/__init__.py +1 -0
  95. axern_sdk-0.2.1/src/axern/control/service/v1/__init__.py +21 -0
  96. axern_sdk-0.2.1/src/axern/control/service/v1/service_event_pb2.py +46 -0
  97. axern_sdk-0.2.1/src/axern/control/service/v1/service_event_pb2.pyi +80 -0
  98. axern_sdk-0.2.1/src/axern/control/service/v1/service_event_pb2_grpc.py +24 -0
  99. axern_sdk-0.2.1/src/axern/control/service/v1/service_pb2.py +74 -0
  100. axern_sdk-0.2.1/src/axern/control/service/v1/service_pb2.pyi +144 -0
  101. axern_sdk-0.2.1/src/axern/control/service/v1/service_pb2_grpc.py +444 -0
  102. axern_sdk-0.2.1/src/axern/control/service/v1/service_replica_pb2.py +55 -0
  103. axern_sdk-0.2.1/src/axern/control/service/v1/service_replica_pb2.pyi +124 -0
  104. axern_sdk-0.2.1/src/axern/control/service/v1/service_replica_pb2_grpc.py +24 -0
  105. axern_sdk-0.2.1/src/axern/control/service/v1/service_types_pb2.py +80 -0
  106. axern_sdk-0.2.1/src/axern/control/service/v1/service_types_pb2.pyi +273 -0
  107. axern_sdk-0.2.1/src/axern/control/service/v1/service_types_pb2_grpc.py +24 -0
  108. axern_sdk-0.2.1/src/axern/control/storage/v1/__init__.py +8 -0
  109. axern_sdk-0.2.1/src/axern/control/storage/v1/storage_pb2.py +87 -0
  110. axern_sdk-0.2.1/src/axern/control/storage/v1/storage_pb2.pyi +181 -0
  111. axern_sdk-0.2.1/src/axern/control/storage/v1/storage_pb2_grpc.py +398 -0
  112. axern_sdk-0.2.1/src/axern/control/storage/v1/storage_types_pb2.py +78 -0
  113. axern_sdk-0.2.1/src/axern/control/storage/v1/storage_types_pb2.pyi +243 -0
  114. axern_sdk-0.2.1/src/axern/control/storage/v1/storage_types_pb2_grpc.py +24 -0
  115. axern_sdk-0.2.1/src/axern/control/tunnel/__init__.py +1 -0
  116. axern_sdk-0.2.1/src/axern/control/tunnel/v1/__init__.py +6 -0
  117. axern_sdk-0.2.1/src/axern/control/tunnel/v1/tunnel_pb2.py +83 -0
  118. axern_sdk-0.2.1/src/axern/control/tunnel/v1/tunnel_pb2.pyi +316 -0
  119. axern_sdk-0.2.1/src/axern/control/tunnel/v1/tunnel_pb2_grpc.py +398 -0
  120. axern_sdk-0.2.1/src/axern/data/artifact/v1/artifact_pb2.py +41 -0
  121. axern_sdk-0.2.1/src/axern/data/artifact/v1/artifact_pb2.pyi +21 -0
  122. axern_sdk-0.2.1/src/axern/data/artifact/v1/artifact_pb2_grpc.py +97 -0
  123. axern_sdk-0.2.1/src/axern/node/sandbox/v1/__init__.py +6 -0
  124. axern_sdk-0.2.1/src/axern/node/sandbox/v1/node_pb2.py +220 -0
  125. axern_sdk-0.2.1/src/axern/node/sandbox/v1/node_pb2.pyi +1019 -0
  126. axern_sdk-0.2.1/src/axern/node/sandbox/v1/node_pb2_grpc.py +1559 -0
  127. axern_sdk-0.2.1/src/axern/tunnel/__init__.py +1 -0
  128. axern_sdk-0.2.1/src/axern/tunnel/v1/__init__.py +6 -0
  129. axern_sdk-0.2.1/src/axern/tunnel/v1/tunnel_pb2.py +52 -0
  130. axern_sdk-0.2.1/src/axern/tunnel/v1/tunnel_pb2.pyi +67 -0
  131. axern_sdk-0.2.1/src/axern/tunnel/v1/tunnel_pb2_grpc.py +97 -0
  132. axern_sdk-0.2.1/src/axern_sdk/__init__.py +161 -0
  133. axern_sdk-0.2.1/src/axern_sdk/_internal/__init__.py +1 -0
  134. axern_sdk-0.2.1/src/axern_sdk/_internal/channel.py +102 -0
  135. axern_sdk-0.2.1/src/axern_sdk/_internal/errors.py +58 -0
  136. axern_sdk-0.2.1/src/axern_sdk/_internal/resources.py +80 -0
  137. axern_sdk-0.2.1/src/axern_sdk/_internal/specs.py +33 -0
  138. axern_sdk-0.2.1/src/axern_sdk/async_client.py +576 -0
  139. axern_sdk-0.2.1/src/axern_sdk/catalog/__init__.py +27 -0
  140. axern_sdk-0.2.1/src/axern_sdk/catalog/client.py +132 -0
  141. axern_sdk-0.2.1/src/axern_sdk/catalog/models.py +115 -0
  142. axern_sdk-0.2.1/src/axern_sdk/client.py +641 -0
  143. axern_sdk-0.2.1/src/axern_sdk/context.py +82 -0
  144. axern_sdk-0.2.1/src/axern_sdk/errors.py +195 -0
  145. axern_sdk-0.2.1/src/axern_sdk/function/__init__.py +27 -0
  146. axern_sdk-0.2.1/src/axern_sdk/function/function.py +410 -0
  147. axern_sdk-0.2.1/src/axern_sdk/function/manifest.py +324 -0
  148. axern_sdk-0.2.1/src/axern_sdk/function/models.py +121 -0
  149. axern_sdk-0.2.1/src/axern_sdk/function/worker.py +380 -0
  150. axern_sdk-0.2.1/src/axern_sdk/models.py +114 -0
  151. axern_sdk-0.2.1/src/axern_sdk/node/__init__.py +51 -0
  152. axern_sdk-0.2.1/src/axern_sdk/node/async_browser_client.py +179 -0
  153. axern_sdk-0.2.1/src/axern_sdk/node/async_capability_client.py +42 -0
  154. axern_sdk-0.2.1/src/axern_sdk/node/async_client.py +350 -0
  155. axern_sdk-0.2.1/src/axern_sdk/node/async_computer_use_client.py +150 -0
  156. axern_sdk-0.2.1/src/axern_sdk/node/async_file_client.py +346 -0
  157. axern_sdk-0.2.1/src/axern_sdk/node/async_process.py +140 -0
  158. axern_sdk-0.2.1/src/axern_sdk/node/browser_client.py +179 -0
  159. axern_sdk-0.2.1/src/axern_sdk/node/capability_client.py +42 -0
  160. axern_sdk-0.2.1/src/axern_sdk/node/capability_protocol.py +49 -0
  161. axern_sdk-0.2.1/src/axern_sdk/node/client.py +315 -0
  162. axern_sdk-0.2.1/src/axern_sdk/node/commands.py +35 -0
  163. axern_sdk-0.2.1/src/axern_sdk/node/computer_use_client.py +150 -0
  164. axern_sdk-0.2.1/src/axern_sdk/node/computer_use_protocol.py +72 -0
  165. axern_sdk-0.2.1/src/axern_sdk/node/file_client.py +346 -0
  166. axern_sdk-0.2.1/src/axern_sdk/node/models.py +208 -0
  167. axern_sdk-0.2.1/src/axern_sdk/node/process.py +155 -0
  168. axern_sdk-0.2.1/src/axern_sdk/node/protocol.py +126 -0
  169. axern_sdk-0.2.1/src/axern_sdk/py.typed +1 -0
  170. axern_sdk-0.2.1/src/axern_sdk/sandbox/__init__.py +48 -0
  171. axern_sdk-0.2.1/src/axern_sdk/sandbox/archive.py +140 -0
  172. axern_sdk-0.2.1/src/axern_sdk/sandbox/async_browser.py +63 -0
  173. axern_sdk-0.2.1/src/axern_sdk/sandbox/async_capabilities.py +18 -0
  174. axern_sdk-0.2.1/src/axern_sdk/sandbox/async_computer_use.py +88 -0
  175. axern_sdk-0.2.1/src/axern_sdk/sandbox/async_files.py +244 -0
  176. axern_sdk-0.2.1/src/axern_sdk/sandbox/async_lifecycle.py +86 -0
  177. axern_sdk-0.2.1/src/axern_sdk/sandbox/async_renewal.py +77 -0
  178. axern_sdk-0.2.1/src/axern_sdk/sandbox/async_sandbox.py +474 -0
  179. axern_sdk-0.2.1/src/axern_sdk/sandbox/browser.py +57 -0
  180. axern_sdk-0.2.1/src/axern_sdk/sandbox/capabilities.py +18 -0
  181. axern_sdk-0.2.1/src/axern_sdk/sandbox/computer_use.py +88 -0
  182. axern_sdk-0.2.1/src/axern_sdk/sandbox/files.py +242 -0
  183. axern_sdk-0.2.1/src/axern_sdk/sandbox/lifecycle.py +79 -0
  184. axern_sdk-0.2.1/src/axern_sdk/sandbox/renewal.py +84 -0
  185. axern_sdk-0.2.1/src/axern_sdk/sandbox/sandbox.py +474 -0
  186. axern_sdk-0.2.1/src/axern_sdk/sandbox/types.py +45 -0
  187. axern_sdk-0.2.1/src/axern_sdk/tunnel/__init__.py +6 -0
  188. axern_sdk-0.2.1/src/axern_sdk/tunnel/config.py +27 -0
  189. axern_sdk-0.2.1/src/axern_sdk/tunnel/connector.py +131 -0
  190. axern_sdk-0.2.1/src/axern_sdk/tunnel/frames.py +31 -0
  191. axern_sdk-0.2.1/src/axern_sdk/tunnel/streams.py +134 -0
  192. axern_sdk-0.2.1/tests/e2e/computer_use_e2e.py +458 -0
  193. axern_sdk-0.2.1/tests/e2e/python_runtime_e2e.py +84 -0
  194. axern_sdk-0.2.1/tests/e2e/sandbox_tunnel_e2e.py +302 -0
  195. axern_sdk-0.2.1/tests/fakes.py +152 -0
  196. axern_sdk-0.2.1/tests/test_admin_service.py +83 -0
  197. axern_sdk-0.2.1/tests/test_catalog.py +111 -0
  198. axern_sdk-0.2.1/tests/test_context.py +63 -0
  199. axern_sdk-0.2.1/tests/test_contract_v1.py +161 -0
  200. axern_sdk-0.2.1/tests/test_function_manifest.py +335 -0
  201. axern_sdk-0.2.1/tests/test_function_worker.py +99 -0
  202. axern_sdk-0.2.1/tests/test_node_client.py +450 -0
  203. axern_sdk-0.2.1/tests/test_sandbox_exec.py +408 -0
  204. axern_sdk-0.2.1/tests/test_sandbox_files.py +569 -0
  205. axern_sdk-0.2.1/tests/test_sandbox_lifecycle.py +420 -0
  206. axern_sdk-0.2.1/tests/test_service_watch.py +272 -0
@@ -0,0 +1,51 @@
1
+ # Environment
2
+ .env
3
+ .env.*
4
+ !.env.example
5
+ /work/
6
+
7
+ # Common build outputs
8
+ /bin/
9
+ /.axrun/
10
+ dist/
11
+ target/
12
+ build/
13
+ out/
14
+ coverage/
15
+ !runtime/axnoded/build/
16
+ !runtime/axnoded/build/executable/
17
+ !runtime/axnoded/build/executable/**
18
+ runtime/axnoded/executable
19
+
20
+ # Node / TypeScript
21
+ node_modules/
22
+ /apps/docs/.astro/
23
+ .pnpm-debug.log*
24
+ pnpm-debug.log*
25
+ *.tsbuildinfo
26
+
27
+ # Go
28
+ *.exe
29
+ *.dll
30
+ *.so
31
+ *.dylib
32
+ *.test
33
+ *.prof
34
+ __debug_bin*
35
+ gateway/gatewayd/internal/api/http/dashboard/vendor/*
36
+ !gateway/gatewayd/internal/api/http/dashboard/vendor/.gitkeep
37
+
38
+ # Python
39
+ .venv/
40
+ __pycache__/
41
+ *.py[cod]
42
+ .pytest_cache/
43
+ .mypy_cache/
44
+ .ruff_cache/
45
+
46
+ # OS / editors
47
+ .DS_Store
48
+ Thumbs.db
49
+ .idea/
50
+ *.iml
51
+ .dev/
@@ -0,0 +1,75 @@
1
+ # AGENTS.md
2
+
3
+ ## Purpose
4
+
5
+ Local contract for the Axern Python SDK workspace. Follow the root
6
+ [`../../AGENTS.md`](../../AGENTS.md) first, then apply this file under
7
+ `sdk/python`.
8
+
9
+ ## Workspace Boundaries
10
+
11
+ - Keep importable code under [`src`](src), generated protobuf modules under
12
+ [`src/axern`](src/axern), and user-facing SDK code under
13
+ [`src/axern_sdk`](src/axern_sdk).
14
+ - Keep tests under [`tests`](tests).
15
+ - Do not add product apps, demos, services, or platform entrypoints here.
16
+
17
+ ## API And Structure
18
+
19
+ - Treat `axern_sdk.__init__`, `client`, `catalog`, `sandbox`, and `tunnel` as
20
+ public API boundaries; keep exports intentional and small.
21
+ - Do not add compatibility aliases for flawed early API shapes unless a concrete
22
+ external contract requires them.
23
+ - Prefer explicit source models over inferred defaults. Environment and sandbox
24
+ callers should provide exactly one source: `template_id`, `image`, or
25
+ `environment_id`.
26
+ - Keep files cohesive by responsibility. Do not grow orchestration files into
27
+ mixed client, lifecycle, transport, and model implementations.
28
+ - Put private shared helpers in `axern_sdk._internal` when they are not part of
29
+ the SDK surface.
30
+ - Do not hand-edit generated `*_pb2.py` or `*_pb2_grpc.py` files.
31
+
32
+ ## Proto Generation
33
+
34
+ - Regenerate Python protobuf modules with
35
+ [`scripts/generate_proto.sh`](scripts/generate_proto.sh).
36
+ - When adding proto dependencies, update the generation script, required
37
+ generated `__init__.py` files, [`pyproject.toml`](pyproject.toml), and
38
+ [`../../uv.lock`](../../uv.lock) together.
39
+
40
+ ## Sandbox And Tunnel Rules
41
+
42
+ - `Sandbox` owns SDK-created environment, service, tunnel session, connector,
43
+ renewal, and cleanup lifecycle.
44
+ - Long-lived tunnel support must renew finite tunnel TTLs automatically.
45
+ - Cleanup is best-effort and must not mask startup failures or wait too long
46
+ after an earlier cleanup step failed.
47
+ - Tunnel framing, renewal, cleanup, or readiness changes need focused unit tests
48
+ and compose SDK e2e validation.
49
+
50
+ ## Validation
51
+
52
+ - Python SDK code:
53
+
54
+ ```bash
55
+ make test-py
56
+ make lint-py
57
+ ```
58
+
59
+ - Package metadata, generated proto, dependency, or distribution changes:
60
+
61
+ ```bash
62
+ uv build sdk/python
63
+ ```
64
+
65
+ - `Sandbox`, tunnel, cleanup, service lifecycle, or compose workflow changes:
66
+
67
+ ```bash
68
+ make local-compose-python-sdk-e2e
69
+ ```
70
+
71
+ - This file or repo-local documentation:
72
+
73
+ ```bash
74
+ make agent-doc-check
75
+ ```
@@ -0,0 +1,373 @@
1
+ Metadata-Version: 2.4
2
+ Name: axern-sdk
3
+ Version: 0.2.1
4
+ Summary: Python SDK for Axern agentic infrastructure and isolated sandboxes
5
+ Project-URL: Homepage, https://axern.cofy-x.space/sdk/python/
6
+ Project-URL: Documentation, https://axern.cofy-x.space/sdk/python/
7
+ Project-URL: Repository, https://github.com/cofy-x/axern
8
+ Project-URL: Issues, https://github.com/cofy-x/axern/issues
9
+ License-Expression: Apache-2.0
10
+ Requires-Python: >=3.11
11
+ Requires-Dist: grpcio>=1.81.0
12
+ Requires-Dist: protobuf>=6.31.1
13
+ Requires-Dist: pyyaml>=6.0.2
14
+ Description-Content-Type: text/markdown
15
+
16
+ # Axern Python SDK
17
+
18
+ The Axern Python SDK is the first-class programmable interface for Axern
19
+ sandboxes. It exposes both the control plane client and a high-level
20
+ `Sandbox` API for lifecycle, command execution, attached processes, file
21
+ operations, directory transfer, tunnels, capability discovery, and diagnostics.
22
+
23
+ ## Install
24
+
25
+ Install the published package:
26
+
27
+ ```bash
28
+ uv add axern-sdk
29
+ ```
30
+
31
+ For repository development, build and run examples through the root `uv`
32
+ workspace:
33
+
34
+ ```bash
35
+ uv build --no-sources sdk/python
36
+ uv run --package axern-sdk python sdk/python/examples/sandbox_programming.py
37
+ ```
38
+
39
+ ## Connect
40
+
41
+ ```python
42
+ from axern_sdk import AxernClient
43
+
44
+ client = AxernClient.from_context("~/.config/axern/config.json")
45
+ ```
46
+
47
+ TLS-enabled local compose setups can pass certificate paths directly:
48
+
49
+ ```python
50
+ client = AxernClient(
51
+ "127.0.0.1:25000",
52
+ tls_ca_cert=".dev/certs/ca.crt",
53
+ tls_cert=".dev/certs/client.crt",
54
+ tls_key=".dev/certs/client.key",
55
+ )
56
+ ```
57
+
58
+ `AsyncAxernClient` provides the same control-plane surface for asyncio code.
59
+ Constructors are explicit and never read the user directory. `from_context()`
60
+ loads a named CLI context from the supplied path; `from_env()` is available for
61
+ environment-driven automation and reads `AXERN_ENDPOINT` plus the gateway TLS
62
+ and proxy variables.
63
+
64
+ ## Sandbox Sources
65
+
66
+ Create a sandbox from exactly one source:
67
+
68
+ - `template_id="python311"` for a catalog template.
69
+ - `image="docker.io/library/python:3.12-slim"` for an OCI image.
70
+ - `environment_id="..."` for an existing environment.
71
+
72
+ ```python
73
+ from axern_sdk import AxernClient, Sandbox
74
+
75
+ client = AxernClient("127.0.0.1:25000")
76
+
77
+ with Sandbox(client=client, template_id="python311") as sandbox:
78
+ print(sandbox.metadata.allocation_id)
79
+
80
+ client.close()
81
+ ```
82
+
83
+ ## Volumes
84
+
85
+ Use `VolumeMount` to attach Service V1 volumes to service-backed sandboxes.
86
+ The SDK passes volume intent through the public control plane; storage
87
+ resolution, node publish, mount injection, and release remain owned by Axern's
88
+ Storage V1 runtime flow.
89
+
90
+ ```python
91
+ from axern_sdk import Sandbox, VolumeMount
92
+
93
+ with Sandbox(
94
+ client=client,
95
+ template_id="python311",
96
+ volumes=[
97
+ VolumeMount("data", "/data"),
98
+ VolumeMount("cache", "/cache", readonly=True, options=("rbind",)),
99
+ ],
100
+ ) as sandbox:
101
+ result = sandbox.exec("ls /data /cache", text=True, check=True)
102
+ print(result.stdout)
103
+ ```
104
+
105
+ ## Function Manifests
106
+
107
+ `Function.from_file()` loads and validates an `axern/v1` Function resource.
108
+ `Function.package()` creates a deterministic tar bundle, and `Function.deploy()`
109
+ packages the source, uploads it with `FunctionControl.UploadFunctionBundle`,
110
+ and then calls `FunctionControl.DeployFunction`. The `python311` runtime image
111
+ includes the SDK Function worker module used by controld-managed warm workers.
112
+ `Function.invoke()` calls the dedicated Function invocation API and returns a
113
+ decoded invocation result.
114
+
115
+ ```python
116
+ from axern_sdk import AxernClient, Function
117
+
118
+ client = AxernClient.from_context("~/.config/axern/config.json")
119
+ function = Function.from_file(client, "examples/function-hello/function.yaml")
120
+ deployment = function.deploy(labels={"team": "runtime"})
121
+
122
+ print(function.name)
123
+ print(function.spec.handler)
124
+ print(deployment.function.id)
125
+ ```
126
+
127
+ ## Exec
128
+
129
+ Use `exec()` for command-result workflows. Set `text=True` to decode stdout and
130
+ stderr; set `check=True` to raise `SandboxExecError` on non-zero exit.
131
+
132
+ ```python
133
+ with Sandbox(client=client, template_id="python311") as sandbox:
134
+ result = sandbox.exec("python -c \"print('hello')\"", text=True, check=True)
135
+ print(result.stdout)
136
+ ```
137
+
138
+ Use `exec_stream()` when stdout/stderr should be consumed as events:
139
+
140
+ ```python
141
+ for event in sandbox.exec_stream(["python", "-u", "-c", "print('streamed')"]):
142
+ if event.stream == "stdout":
143
+ print(event.text(), end="")
144
+ ```
145
+
146
+ ## Attached Process
147
+
148
+ Use `process()` when your program needs to control stdin, observe output, wait,
149
+ or terminate a running command.
150
+
151
+ ```python
152
+ with sandbox.process(["python", "-u", "-c", "import sys; print(sys.stdin.read().upper())"]) as process:
153
+ process.write("hello process\n")
154
+ process.close_stdin()
155
+
156
+ for event in process.events():
157
+ if event.stream == "stdout":
158
+ print(event.text(), end="")
159
+
160
+ result = process.wait()
161
+ print(result.exit_code)
162
+ ```
163
+
164
+ `AsyncSandbox.process()` returns `AsyncSandboxProcess` with async equivalents of
165
+ `write()`, `close_stdin()`, `events()`, `wait()`, `terminate()`, and `kill()`.
166
+
167
+ ## Image-Backed Processes
168
+
169
+ Use `exec_image()` or `process_image()` to run a tool from a separate image
170
+ against explicit host-backed sandbox paths. OCI and Nydus image refs use the
171
+ same `image` field. When `mounts=None`, the SDK requests `/workspace ->
172
+ /workspace`; pass `mounts=[]` for no shared paths. Use `Sandbox(image=...)`
173
+ when the image should be the sandbox rootfs with normal files, exec, process,
174
+ tunnel, and lifecycle APIs; image-backed processes are temporary side processes
175
+ attached to an existing sandbox.
176
+
177
+ ```python
178
+ from axern_sdk import workspace_mount
179
+
180
+ result = sandbox.exec_image(
181
+ "ghcr.io/cofy-x/agent:latest",
182
+ "tool run",
183
+ mounts=[workspace_mount("/workspace")],
184
+ check=True,
185
+ text=True,
186
+ )
187
+ print(result.stdout)
188
+ ```
189
+
190
+ ## Files
191
+
192
+ Single-file APIs are byte-safe. Text helpers only encode/decode at the SDK
193
+ boundary.
194
+
195
+ ```python
196
+ sandbox.write_text("/tmp/message.txt", "payload\n")
197
+ print(sandbox.read_text("/tmp/message.txt"))
198
+
199
+ sandbox.write_bytes("/tmp/blob.bin", b"\x00\x01")
200
+ data = sandbox.read_bytes("/tmp/blob.bin")
201
+ ```
202
+
203
+ Platform file operations are handled by the node/runtime file service, not by
204
+ SDK-side shell fallbacks:
205
+
206
+ ```python
207
+ sandbox.copy("/tmp/message.txt", "/tmp/message-copy.txt", overwrite=True)
208
+ sandbox.move("/tmp/message-copy.txt", "/tmp/message-final.txt")
209
+ sandbox.chmod("/tmp/message-final.txt", 0o600)
210
+ sandbox.touch("/tmp/message-final.txt")
211
+
212
+ info = sandbox.stat("/tmp/message-final.txt")
213
+ entries = sandbox.list_dir("/tmp")
214
+ exists = sandbox.exists("/tmp/message-final.txt")
215
+ ```
216
+
217
+ ## Directory Transfer
218
+
219
+ Directory upload/download uses archive streaming. The SDK packages local
220
+ directories with `tarfile` and safely extracts downloaded archives; remote file
221
+ semantics remain owned by the platform file service.
222
+
223
+ ```python
224
+ from pathlib import Path
225
+
226
+ source = Path("example-upload")
227
+ source.mkdir(exist_ok=True)
228
+ source.joinpath("data.txt").write_text("directory payload\n")
229
+
230
+ sandbox.upload_dir(source, "/tmp/example-upload", overwrite=True)
231
+ sandbox.download_dir("/tmp/example-upload", "example-download", overwrite=True)
232
+ ```
233
+
234
+ Local symlinks are rejected during upload. Download extraction rejects absolute
235
+ paths, parent traversal, symlinks, and hardlinks.
236
+
237
+ ## Tunnel
238
+
239
+ Pass `upstream` to expose a local TCP service to code running inside the
240
+ sandbox. The SDK owns the tunnel connector and renews finite tunnel TTLs while
241
+ the sandbox is active.
242
+
243
+ ```python
244
+ from axern_sdk import Sandbox
245
+
246
+ with Sandbox(
247
+ client=client,
248
+ image="docker.io/library/python:3.12-slim",
249
+ upstream="127.0.0.1:8080",
250
+ remote_port=8786,
251
+ ) as sandbox:
252
+ print(sandbox.bound_addr)
253
+ ```
254
+
255
+ ## Metadata
256
+
257
+ `Sandbox.state` is the lightweight runtime state. `Sandbox.metadata` is stable
258
+ for logs and diagnostics:
259
+
260
+ ```python
261
+ metadata = sandbox.metadata
262
+ print(metadata.environment_id, metadata.service_id, metadata.allocation_id)
263
+ print(metadata.node_id, metadata.runtime_class, metadata.tunnel_session_id)
264
+ ```
265
+
266
+ ## Capabilities
267
+
268
+ Use `capability_status()` to discover baseline and optional sandboxd-backed
269
+ providers before calling desktop or browser APIs:
270
+
271
+ ```python
272
+ status = sandbox.capability_status()
273
+ print(status.ready, status.capabilities)
274
+
275
+ for provider in status.providers:
276
+ print(provider.name, provider.state, provider.available, provider.reason)
277
+ ```
278
+
279
+ ## Errors
280
+
281
+ SDK exceptions expose fields for programmatic handling:
282
+
283
+ ```python
284
+ from axern_sdk import (
285
+ SandboxConnectionError,
286
+ SandboxPermissionError,
287
+ SandboxPreconditionError,
288
+ SandboxRpcError,
289
+ )
290
+
291
+ try:
292
+ sandbox.exec("python -V", check=True)
293
+ except SandboxConnectionError as exc:
294
+ if exc.retryable:
295
+ print("temporary node/control-plane connectivity issue")
296
+ raise
297
+ except SandboxPermissionError as exc:
298
+ print("credentials do not permit this operation", exc.operation)
299
+ raise
300
+ except SandboxPreconditionError as exc:
301
+ if exc.capability:
302
+ print(
303
+ exc.capability.capability,
304
+ exc.capability.provider,
305
+ exc.capability.provider_state,
306
+ exc.capability.missing_dependencies,
307
+ )
308
+ raise
309
+ except SandboxRpcError as exc:
310
+ print(exc.operation, exc.code, exc.details, exc.allocation_id)
311
+ raise
312
+ ```
313
+
314
+ Common error classes:
315
+
316
+ - `SandboxNotStartedError`: operation requires an active sandbox.
317
+ - `SandboxExecError`: `exec(..., check=True)` observed non-zero exit.
318
+ - `SandboxConnectionError`: transport or connectivity failure.
319
+ - `SandboxPermissionError`: authentication or authorization failure.
320
+ - `SandboxRpcError`: gRPC status mapped from node/runtime APIs.
321
+ - `SandboxTimeoutError`: SDK-side timeout.
322
+
323
+ Sandboxd-backed capability failures keep their normal SDK exception class and
324
+ also expose `exc.capability` when the node returns provider diagnostics. That
325
+ object contains `capability`, `provider`, `provider_state`, `reason`, and
326
+ `missing_dependencies`, so callers can branch on missing browser or
327
+ computer-use dependencies without parsing the full error string.
328
+
329
+ ## Async
330
+
331
+ Async APIs mirror the synchronous shape:
332
+
333
+ ```python
334
+ from axern_sdk import AsyncAxernClient, AsyncSandbox
335
+
336
+ async with AsyncAxernClient("127.0.0.1:25000") as client:
337
+ async with AsyncSandbox(client=client, template_id="python311") as sandbox:
338
+ result = await sandbox.exec("python -c \"print('hello async')\"", text=True, check=True)
339
+ print(result.stdout)
340
+
341
+ async with await sandbox.process(["python", "-u", "-c", "import sys; print(sys.stdin.read())"]) as process:
342
+ await process.write("async input\n")
343
+ await process.close_stdin()
344
+ async for event in process.events():
345
+ if event.stream == "stdout":
346
+ print(event.text(), end="")
347
+ ```
348
+
349
+ ## Examples
350
+
351
+ Runnable examples live in [`examples`](examples):
352
+
353
+ - [`examples/function_manifest.py`](examples/function_manifest.py)
354
+ - [`examples/sandbox_programming.py`](examples/sandbox_programming.py)
355
+ - [`examples/sandbox_volume.py`](examples/sandbox_volume.py)
356
+ - [`examples/async_sandbox_programming.py`](examples/async_sandbox_programming.py)
357
+ - [`examples/computer_use.py`](examples/computer_use.py)
358
+ - [`examples/service_gateway.py`](examples/service_gateway.py)
359
+
360
+ Examples expect a reachable Axern gateway control edge at `127.0.0.1:25000`.
361
+ `service_gateway.py` also expects `AXERN_SERVICE_URL`, for example
362
+ `http://127.0.0.1:25080`. It accepts `AXERN_NAMESPACE`, `AXERN_TEMPLATE_ID`,
363
+ `AXERN_RUNTIME_CLASS`, `AXERN_REQUEST_CPU`, `AXERN_REQUEST_MEMORY`,
364
+ `AXERN_LIMIT_CPU`, and `AXERN_LIMIT_MEMORY` for service configuration.
365
+
366
+ ## Validation
367
+
368
+ ```bash
369
+ make test-py
370
+ make lint-py
371
+ make sdk-python-verify
372
+ make local-compose-python-sdk-e2e
373
+ ```