vxsdk 0.1.0__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.
vxsdk-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,287 @@
1
+ Metadata-Version: 2.4
2
+ Name: vxsdk
3
+ Version: 0.1.0
4
+ Summary: Python SDK for the vxcloud / VxCloud platform — provision infrastructure, deploy applications, and manage running services from your terminal or CI pipeline.
5
+ Author-email: vxcloud <engineering@vxcloud.io>
6
+ License: Apache-2.0
7
+ Project-URL: Homepage, https://vxcloud.io
8
+ Project-URL: Documentation, https://vxcloud.io/docs/sdks
9
+ Project-URL: Repository, https://github.com/vxcloud/platform
10
+ Project-URL: Issues, https://github.com/vxcloud/platform/issues
11
+ Project-URL: Changelog, https://github.com/vxcloud/platform/blob/main/services/sdk/python/CHANGELOG.md
12
+ Keywords: vxcloud,vxcloud,vxcli,infrastructure,iac,deploy,ssh,docker,kubernetes,ai-agents
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Intended Audience :: System Administrators
16
+ Classifier: License :: OSI Approved :: Apache Software License
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.9
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
+ Classifier: Topic :: System :: Systems Administration
25
+ Classifier: Topic :: System :: Installation/Setup
26
+ Requires-Python: >=3.9
27
+ Description-Content-Type: text/markdown
28
+ Provides-Extra: async
29
+ Requires-Dist: httpx>=0.25; extra == "async"
30
+ Provides-Extra: dev
31
+ Requires-Dist: pytest>=7; extra == "dev"
32
+ Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
33
+ Requires-Dist: httpx>=0.25; extra == "dev"
34
+ Requires-Dist: build>=1.0; extra == "dev"
35
+ Requires-Dist: twine>=4.0; extra == "dev"
36
+
37
+ # vxsdk (Python, preview)
38
+
39
+ Single-file, stdlib-only Python port of [`../`](..) — the Go SDK.
40
+
41
+ Same wire contract, same auth model (API key + auto-refresh on 401),
42
+ same resource layout. Use this from Python scripts, Jupyter notebooks,
43
+ data pipelines, or any internal Python service that needs to talk to
44
+ the vxcloud platform.
45
+
46
+ ```
47
+ services/sdk/python/
48
+ ├── vxsdk.py sync SDK — stdlib only, drop-in
49
+ ├── vxsdk_async.py async SDK — same API, requires httpx
50
+ ├── deploy_app.py runnable sync demo (container or stack)
51
+ ├── deploy_async.py runnable async demo (3 deploys in parallel)
52
+ └── README.md this file
53
+ ```
54
+
55
+ ## Two flavors
56
+
57
+ | Flavor | Module | Dependency | When to use |
58
+ |---|---|---|---|
59
+ | **Sync** | `vxsdk` | stdlib only | Scripts, notebooks, single-shot operations, simple migrations from `vxcli` |
60
+ | **Async** | `vxsdk_async` | `httpx` (`pip install httpx`) | FastAPI / aiohttp services, concurrent fan-out (multi-host deploys, batch installs), high-throughput integration |
61
+
62
+ Same class hierarchy, same method signatures, same auth model. Switching
63
+ is essentially `vxsdk.Client` → `vxsdk_async.AsyncClient` plus
64
+ `async`/`await`.
65
+
66
+ ## Why a parallel Python implementation?
67
+
68
+ Python and Go don't share runtimes. The Go SDK is the right answer for
69
+ Go services and customers; this Python file is the right answer for
70
+ Python customers. Both are wrappers over the same FastAPI HTTP surface,
71
+ so the JSON wire is identical and they can be regenerated from a single
72
+ OpenAPI spec when the platform team enables one.
73
+
74
+ For now, both are hand-written and kept in sync by review.
75
+
76
+ ## Installation
77
+
78
+ ```bash
79
+ # PyPI (canonical name)
80
+ pip install vxsdk
81
+ pip install vxsdk[async] # adds httpx for vxsdk_async
82
+
83
+ # PyPI (brand-name alias — same code, just `import vxcloud`)
84
+ pip install vxcloud
85
+ pip install vxcloud[async]
86
+
87
+ # Drop-in (no install, stdlib-only)
88
+ cp services/sdk/python/vxsdk.py /path/to/your/project/
89
+ ```
90
+
91
+ Stdlib only for the sync flavor — no extra deps. Tested on Python 3.9+.
92
+
93
+ ## Entry-point styles
94
+
95
+ All four below resolve to the same class object — pick the name you and
96
+ your team prefer. There is no behavior difference:
97
+
98
+ ```python
99
+ import vxsdk
100
+
101
+ c = vxsdk.Client.load_from_vxcli() # canonical
102
+ c = vxsdk.VxCloud.load_from_vxcli() # PascalCase brand (matches TS SDK)
103
+ c = vxsdk.vxcloud.load_from_vxcli() # lowercase brand
104
+
105
+ # Or via the alias package:
106
+ import vxcloud
107
+ c = vxcloud.Client.load_from_vxcli()
108
+ c = vxcloud.load_from_vxcli() # module-level convenience
109
+ ```
110
+
111
+ ## Quick start
112
+
113
+ ```python
114
+ import vxsdk
115
+
116
+ # Reads ~/.vxcloud/credentials.json (the file `vxcli auth login` writes)
117
+ c = vxsdk.Client.load_from_vxcli()
118
+
119
+ # Or with explicit credentials
120
+ # c = vxsdk.Client(api_key="xc_dev_...", username="alice")
121
+
122
+ # Provision a VM — `c.cloud.vm.provision(...)` mirrors the TypeScript SDK.
123
+ # (The legacy flat `c.cloud.create_vm(...)` also still works.)
124
+ vm = c.cloud.vm.provision(
125
+ name="api-vm", cloud="aws", region="us-east-1",
126
+ instance_type="t3.small", key_pair_name="AWSPRODKEY2",
127
+ )
128
+ print(vm["public_ip"])
129
+
130
+ # Deploy FastAPI onto the new VM
131
+ sess = c.deploy.fastapi(
132
+ path="./", entry="app.app:app",
133
+ requirements="requirements.txt",
134
+ app_port=8000, http_port=80, app_name="studio-backend",
135
+ host=vm["public_ip"], ssh_user="ubuntu",
136
+ key_pair_name="AWSPRODKEY1.PEM",
137
+ )
138
+ print(sess["session_id"])
139
+
140
+ # Deploy a Docker container onto a VM
141
+ result = c.deploy.container(
142
+ host="54.197.71.181", ssh_user="ubuntu", key_pair_name="AWSPRODKEY1.PEM",
143
+ image="grafana/grafana:latest",
144
+ name="grafana", ports=["3000:3000"],
145
+ restart_policy="unless-stopped",
146
+ )
147
+ print(result["session_id"], result.get("status"))
148
+
149
+ # Deploy a language stack from a public git repo
150
+ result = c.deploy.stack(
151
+ "golang",
152
+ host="54.197.71.181", ssh_user="ubuntu", key_pair_name="AWSPRODKEY1.PEM",
153
+ repo_url="https://github.com/joelwembo/va-sample-golang.git", branch="main",
154
+ git_provider="github", app_name="va-sample-golang",
155
+ http_port="80", app_port="8080", go_version="1.22",
156
+ )
157
+
158
+ # Run a custom shell script over SSH
159
+ result = c.install.script(
160
+ host="54.197.71.181", ssh_user="ubuntu", key_pair_name="AWSPRODKEY1.PEM",
161
+ script="#!/bin/bash\necho hello\n",
162
+ script_name="hello.sh",
163
+ )
164
+
165
+ # CI/CD
166
+ for p in c.cicd.pipelines.list():
167
+ print(p["id"], p["name"], p.get("repository_url"))
168
+
169
+ build = c.cicd.pipelines.trigger(pipeline_id="abc...", branch="main")
170
+
171
+ # Marketplace
172
+ agents = c.marketplace.agents.list()
173
+ result = c.marketplace.agents.deploy(
174
+ "golang_url_status_agent",
175
+ host="54.197.71.181", ssh_user="ubuntu", key_pair_name="AWSPRODKEY1.PEM",
176
+ http_port="8094",
177
+ )
178
+
179
+ # Cloud provisioning (real AWS resources)
180
+ result = c.cloud.create_s3_bucket("my-bucket-001", region="us-east-1")
181
+ result = c.cloud.create_iam_policy("my-policy-001",
182
+ policy_document={"Version": "2012-10-17", "Statement": [...]})
183
+ ```
184
+
185
+ ## Resource map
186
+
187
+ | Path | Method | Backend endpoint |
188
+ |---|---|---|
189
+ | `c.cicd.pipelines.list/show/trigger` | GET/GET/POST | `/api/v2/cicd/pipelines/...` |
190
+ | `c.cicd.builds.show` | GET | `/api/v2/cicd/builds/{id}` |
191
+ | `c.sessions.list` | GET | `/api/v3/sessions/list` |
192
+ | `c.install.script` | POST multipart | `/api/v2/tenant/install/script` |
193
+ | `c.install.compose` | POST multipart | `/api/v2/tenant/provision/docker-compose/custom` |
194
+ | `c.deploy.container` | POST multipart | `/api/v2/tenant/container/deploy` |
195
+ | `c.deploy.stack(kind)` | POST multipart | `/api/v2/infrastructure/services/<kind>/deploy` |
196
+ | `c.marketplace.agents.list/show/deploy` | GET/GET/POST | `/api/v2/marketplace/agents/...` |
197
+ | `c.marketplace.models.list/show` | GET/GET | `/api/v2/marketplace/models/...` |
198
+ | `c.marketplace.solutions.list/show/provision` | GET/GET/POST | `/api/v2/marketplace/templates`, `/provision` |
199
+ | `c.cloud.create_s3_bucket` | POST | `/api/v2/tenant/provision/storage` |
200
+ | `c.cloud.create_iam_policy/role/keypair` | POST | `/api/v2/tenant/provision/security` |
201
+ | `c.cloud.create_vm` *(legacy)* | POST | `/api/v2/tenant/provision/vm` |
202
+ | `c.cloud.vm.provision/status/action` | POST | `/api/v2/tenant/provision/vm`, `/provision/vm/{status,action}` |
203
+ | `c.cloud.create_vpc` | POST | `/api/v2/tenant/provision/networks` |
204
+ | `c.cloud.create_kubernetes_cluster` | POST | `/api/v2/tenant/provision/kubernetes` |
205
+ | `c.cloud.list_kubernetes_clusters` | GET | `/api/v2/tenant/kubernetes/clusters` |
206
+ | `c.cloud.kubernetes_cluster_details` | POST | `/api/v2/tenant/kubernetes/cluster/details` |
207
+ | `c.cloud.create_serverless_function` | POST | `/api/v2/tenant/provision/serverless` |
208
+ | `c.metaldb.test_connection/provision` | POST | `/api/v2/tenant/metaldb/...` |
209
+ | `c.nodes.list/default/set_default` | GET/POST | `/api/v1/auth/nodes/` (control plane) |
210
+ | `c.workspace.delete_workspace` | DELETE | `/api/v2/setup/workspace` |
211
+ | `c.marketplace.models.deploy` | POST | `/api/v2/marketplace/models/deploy` |
212
+ | `c.agentcontrol.{summary,fine_tuning,training,knowledge,datasets,agents,github}` | GET/POST | `/api/v2/agentcontrol/...` (X-Tenant-ID header) |
213
+ | `c.vxcomputer.info/run/classify/audit_verify` | GET/POST | `/api/v2/vxcomputer/...` |
214
+ | `c.workflow.list/create/validate/execute/export` | GET/POST | `/api/v2/workflow/...` |
215
+ | `c.vxchrono.create_goal/schedule/launch_run` | POST | `/api/v2/vxchrono/...` |
216
+ | `c.robotic.list_robots/register_robot/send_command` | GET/POST | `/api/v2/robotic/...` |
217
+
218
+ Async parity: `vxsdk_async.AsyncClient` exposes the same modules,
219
+ including `c.vxcomputer`, `c.workflow`, `c.vxchrono`, and `c.robotic`.
220
+
221
+ ## Errors
222
+
223
+ ```python
224
+ try:
225
+ c.cicd.pipelines.list()
226
+ except vxsdk.VxAuthError as e: # 401/403
227
+ ...
228
+ except vxsdk.VxValidationError as e: # 400/422
229
+ ...
230
+ except vxsdk.VxRateLimitError as e: # 429 — e.retry_after
231
+ ...
232
+ except vxsdk.VxNotFoundError as e: # 404
233
+ ...
234
+ except vxsdk.VxServerError as e: # 5xx
235
+ ...
236
+ except vxsdk.VxNetworkError as e: # transport
237
+ ...
238
+ except vxsdk.VxError as e: # base, anything else
239
+ ...
240
+ ```
241
+
242
+ The SDK retries `VxNetworkError`, `VxServerError`, and `VxRateLimitError`
243
+ up to 3 times with exponential backoff. Auth errors and validation errors
244
+ are surfaced immediately — retrying them as-is would not succeed.
245
+
246
+ On `401`, the SDK calls `POST /api/v1/auth/developer/keys/login` once
247
+ with the configured API key, replays the original request, and only
248
+ surfaces the error if the refresh itself fails. Application code should
249
+ not see token expiration.
250
+
251
+ ## Run the sync deploy program
252
+
253
+ ```bash
254
+ cd services/sdk/python
255
+ python3 deploy_app.py # whoami container on inst3:8085
256
+ python3 deploy_app.py --image redis:7 --name r --ports 6380:6379
257
+ python3 deploy_app.py --mode stack --kind golang \
258
+ --repo-url https://github.com/joelwembo/va-sample-golang.git
259
+ ```
260
+
261
+ Default: deploys `traefik/whoami:latest` to inst3:8085 and polls until it
262
+ returns HTTP 200. See `python3 deploy_app.py --help` for flags.
263
+
264
+ ## Run the async demo
265
+
266
+ ```bash
267
+ pip install httpx
268
+ python3 deploy_async.py
269
+ ```
270
+
271
+ Drops three redis containers onto a single host **in parallel** via
272
+ `asyncio.gather()`. Verified live: 3 deploys in 22.8s wall clock vs.
273
+ ~57s sequential — a 2.5× speedup, and a 3× win at higher fan-out.
274
+
275
+ ```python
276
+ import asyncio, vxsdk_async as vx
277
+
278
+ async def main():
279
+ async with await vx.AsyncClient.load_from_vxcli() as c:
280
+ results = await asyncio.gather(
281
+ c.deploy.container(host=h1, ssh_user="ubuntu", key_pair_name=K, image="redis:7", ports=["6381:6379"], name="r1"),
282
+ c.deploy.container(host=h2, ssh_user="ubuntu", key_pair_name=K, image="redis:7", ports=["6381:6379"], name="r2"),
283
+ c.deploy.container(host=h3, ssh_user="ubuntu", key_pair_name=K, image="redis:7", ports=["6381:6379"], name="r3"),
284
+ )
285
+
286
+ asyncio.run(main())
287
+ ```
vxsdk-0.1.0/README.md ADDED
@@ -0,0 +1,251 @@
1
+ # vxsdk (Python, preview)
2
+
3
+ Single-file, stdlib-only Python port of [`../`](..) — the Go SDK.
4
+
5
+ Same wire contract, same auth model (API key + auto-refresh on 401),
6
+ same resource layout. Use this from Python scripts, Jupyter notebooks,
7
+ data pipelines, or any internal Python service that needs to talk to
8
+ the vxcloud platform.
9
+
10
+ ```
11
+ services/sdk/python/
12
+ ├── vxsdk.py sync SDK — stdlib only, drop-in
13
+ ├── vxsdk_async.py async SDK — same API, requires httpx
14
+ ├── deploy_app.py runnable sync demo (container or stack)
15
+ ├── deploy_async.py runnable async demo (3 deploys in parallel)
16
+ └── README.md this file
17
+ ```
18
+
19
+ ## Two flavors
20
+
21
+ | Flavor | Module | Dependency | When to use |
22
+ |---|---|---|---|
23
+ | **Sync** | `vxsdk` | stdlib only | Scripts, notebooks, single-shot operations, simple migrations from `vxcli` |
24
+ | **Async** | `vxsdk_async` | `httpx` (`pip install httpx`) | FastAPI / aiohttp services, concurrent fan-out (multi-host deploys, batch installs), high-throughput integration |
25
+
26
+ Same class hierarchy, same method signatures, same auth model. Switching
27
+ is essentially `vxsdk.Client` → `vxsdk_async.AsyncClient` plus
28
+ `async`/`await`.
29
+
30
+ ## Why a parallel Python implementation?
31
+
32
+ Python and Go don't share runtimes. The Go SDK is the right answer for
33
+ Go services and customers; this Python file is the right answer for
34
+ Python customers. Both are wrappers over the same FastAPI HTTP surface,
35
+ so the JSON wire is identical and they can be regenerated from a single
36
+ OpenAPI spec when the platform team enables one.
37
+
38
+ For now, both are hand-written and kept in sync by review.
39
+
40
+ ## Installation
41
+
42
+ ```bash
43
+ # PyPI (canonical name)
44
+ pip install vxsdk
45
+ pip install vxsdk[async] # adds httpx for vxsdk_async
46
+
47
+ # PyPI (brand-name alias — same code, just `import vxcloud`)
48
+ pip install vxcloud
49
+ pip install vxcloud[async]
50
+
51
+ # Drop-in (no install, stdlib-only)
52
+ cp services/sdk/python/vxsdk.py /path/to/your/project/
53
+ ```
54
+
55
+ Stdlib only for the sync flavor — no extra deps. Tested on Python 3.9+.
56
+
57
+ ## Entry-point styles
58
+
59
+ All four below resolve to the same class object — pick the name you and
60
+ your team prefer. There is no behavior difference:
61
+
62
+ ```python
63
+ import vxsdk
64
+
65
+ c = vxsdk.Client.load_from_vxcli() # canonical
66
+ c = vxsdk.VxCloud.load_from_vxcli() # PascalCase brand (matches TS SDK)
67
+ c = vxsdk.vxcloud.load_from_vxcli() # lowercase brand
68
+
69
+ # Or via the alias package:
70
+ import vxcloud
71
+ c = vxcloud.Client.load_from_vxcli()
72
+ c = vxcloud.load_from_vxcli() # module-level convenience
73
+ ```
74
+
75
+ ## Quick start
76
+
77
+ ```python
78
+ import vxsdk
79
+
80
+ # Reads ~/.vxcloud/credentials.json (the file `vxcli auth login` writes)
81
+ c = vxsdk.Client.load_from_vxcli()
82
+
83
+ # Or with explicit credentials
84
+ # c = vxsdk.Client(api_key="xc_dev_...", username="alice")
85
+
86
+ # Provision a VM — `c.cloud.vm.provision(...)` mirrors the TypeScript SDK.
87
+ # (The legacy flat `c.cloud.create_vm(...)` also still works.)
88
+ vm = c.cloud.vm.provision(
89
+ name="api-vm", cloud="aws", region="us-east-1",
90
+ instance_type="t3.small", key_pair_name="AWSPRODKEY2",
91
+ )
92
+ print(vm["public_ip"])
93
+
94
+ # Deploy FastAPI onto the new VM
95
+ sess = c.deploy.fastapi(
96
+ path="./", entry="app.app:app",
97
+ requirements="requirements.txt",
98
+ app_port=8000, http_port=80, app_name="studio-backend",
99
+ host=vm["public_ip"], ssh_user="ubuntu",
100
+ key_pair_name="AWSPRODKEY1.PEM",
101
+ )
102
+ print(sess["session_id"])
103
+
104
+ # Deploy a Docker container onto a VM
105
+ result = c.deploy.container(
106
+ host="54.197.71.181", ssh_user="ubuntu", key_pair_name="AWSPRODKEY1.PEM",
107
+ image="grafana/grafana:latest",
108
+ name="grafana", ports=["3000:3000"],
109
+ restart_policy="unless-stopped",
110
+ )
111
+ print(result["session_id"], result.get("status"))
112
+
113
+ # Deploy a language stack from a public git repo
114
+ result = c.deploy.stack(
115
+ "golang",
116
+ host="54.197.71.181", ssh_user="ubuntu", key_pair_name="AWSPRODKEY1.PEM",
117
+ repo_url="https://github.com/joelwembo/va-sample-golang.git", branch="main",
118
+ git_provider="github", app_name="va-sample-golang",
119
+ http_port="80", app_port="8080", go_version="1.22",
120
+ )
121
+
122
+ # Run a custom shell script over SSH
123
+ result = c.install.script(
124
+ host="54.197.71.181", ssh_user="ubuntu", key_pair_name="AWSPRODKEY1.PEM",
125
+ script="#!/bin/bash\necho hello\n",
126
+ script_name="hello.sh",
127
+ )
128
+
129
+ # CI/CD
130
+ for p in c.cicd.pipelines.list():
131
+ print(p["id"], p["name"], p.get("repository_url"))
132
+
133
+ build = c.cicd.pipelines.trigger(pipeline_id="abc...", branch="main")
134
+
135
+ # Marketplace
136
+ agents = c.marketplace.agents.list()
137
+ result = c.marketplace.agents.deploy(
138
+ "golang_url_status_agent",
139
+ host="54.197.71.181", ssh_user="ubuntu", key_pair_name="AWSPRODKEY1.PEM",
140
+ http_port="8094",
141
+ )
142
+
143
+ # Cloud provisioning (real AWS resources)
144
+ result = c.cloud.create_s3_bucket("my-bucket-001", region="us-east-1")
145
+ result = c.cloud.create_iam_policy("my-policy-001",
146
+ policy_document={"Version": "2012-10-17", "Statement": [...]})
147
+ ```
148
+
149
+ ## Resource map
150
+
151
+ | Path | Method | Backend endpoint |
152
+ |---|---|---|
153
+ | `c.cicd.pipelines.list/show/trigger` | GET/GET/POST | `/api/v2/cicd/pipelines/...` |
154
+ | `c.cicd.builds.show` | GET | `/api/v2/cicd/builds/{id}` |
155
+ | `c.sessions.list` | GET | `/api/v3/sessions/list` |
156
+ | `c.install.script` | POST multipart | `/api/v2/tenant/install/script` |
157
+ | `c.install.compose` | POST multipart | `/api/v2/tenant/provision/docker-compose/custom` |
158
+ | `c.deploy.container` | POST multipart | `/api/v2/tenant/container/deploy` |
159
+ | `c.deploy.stack(kind)` | POST multipart | `/api/v2/infrastructure/services/<kind>/deploy` |
160
+ | `c.marketplace.agents.list/show/deploy` | GET/GET/POST | `/api/v2/marketplace/agents/...` |
161
+ | `c.marketplace.models.list/show` | GET/GET | `/api/v2/marketplace/models/...` |
162
+ | `c.marketplace.solutions.list/show/provision` | GET/GET/POST | `/api/v2/marketplace/templates`, `/provision` |
163
+ | `c.cloud.create_s3_bucket` | POST | `/api/v2/tenant/provision/storage` |
164
+ | `c.cloud.create_iam_policy/role/keypair` | POST | `/api/v2/tenant/provision/security` |
165
+ | `c.cloud.create_vm` *(legacy)* | POST | `/api/v2/tenant/provision/vm` |
166
+ | `c.cloud.vm.provision/status/action` | POST | `/api/v2/tenant/provision/vm`, `/provision/vm/{status,action}` |
167
+ | `c.cloud.create_vpc` | POST | `/api/v2/tenant/provision/networks` |
168
+ | `c.cloud.create_kubernetes_cluster` | POST | `/api/v2/tenant/provision/kubernetes` |
169
+ | `c.cloud.list_kubernetes_clusters` | GET | `/api/v2/tenant/kubernetes/clusters` |
170
+ | `c.cloud.kubernetes_cluster_details` | POST | `/api/v2/tenant/kubernetes/cluster/details` |
171
+ | `c.cloud.create_serverless_function` | POST | `/api/v2/tenant/provision/serverless` |
172
+ | `c.metaldb.test_connection/provision` | POST | `/api/v2/tenant/metaldb/...` |
173
+ | `c.nodes.list/default/set_default` | GET/POST | `/api/v1/auth/nodes/` (control plane) |
174
+ | `c.workspace.delete_workspace` | DELETE | `/api/v2/setup/workspace` |
175
+ | `c.marketplace.models.deploy` | POST | `/api/v2/marketplace/models/deploy` |
176
+ | `c.agentcontrol.{summary,fine_tuning,training,knowledge,datasets,agents,github}` | GET/POST | `/api/v2/agentcontrol/...` (X-Tenant-ID header) |
177
+ | `c.vxcomputer.info/run/classify/audit_verify` | GET/POST | `/api/v2/vxcomputer/...` |
178
+ | `c.workflow.list/create/validate/execute/export` | GET/POST | `/api/v2/workflow/...` |
179
+ | `c.vxchrono.create_goal/schedule/launch_run` | POST | `/api/v2/vxchrono/...` |
180
+ | `c.robotic.list_robots/register_robot/send_command` | GET/POST | `/api/v2/robotic/...` |
181
+
182
+ Async parity: `vxsdk_async.AsyncClient` exposes the same modules,
183
+ including `c.vxcomputer`, `c.workflow`, `c.vxchrono`, and `c.robotic`.
184
+
185
+ ## Errors
186
+
187
+ ```python
188
+ try:
189
+ c.cicd.pipelines.list()
190
+ except vxsdk.VxAuthError as e: # 401/403
191
+ ...
192
+ except vxsdk.VxValidationError as e: # 400/422
193
+ ...
194
+ except vxsdk.VxRateLimitError as e: # 429 — e.retry_after
195
+ ...
196
+ except vxsdk.VxNotFoundError as e: # 404
197
+ ...
198
+ except vxsdk.VxServerError as e: # 5xx
199
+ ...
200
+ except vxsdk.VxNetworkError as e: # transport
201
+ ...
202
+ except vxsdk.VxError as e: # base, anything else
203
+ ...
204
+ ```
205
+
206
+ The SDK retries `VxNetworkError`, `VxServerError`, and `VxRateLimitError`
207
+ up to 3 times with exponential backoff. Auth errors and validation errors
208
+ are surfaced immediately — retrying them as-is would not succeed.
209
+
210
+ On `401`, the SDK calls `POST /api/v1/auth/developer/keys/login` once
211
+ with the configured API key, replays the original request, and only
212
+ surfaces the error if the refresh itself fails. Application code should
213
+ not see token expiration.
214
+
215
+ ## Run the sync deploy program
216
+
217
+ ```bash
218
+ cd services/sdk/python
219
+ python3 deploy_app.py # whoami container on inst3:8085
220
+ python3 deploy_app.py --image redis:7 --name r --ports 6380:6379
221
+ python3 deploy_app.py --mode stack --kind golang \
222
+ --repo-url https://github.com/joelwembo/va-sample-golang.git
223
+ ```
224
+
225
+ Default: deploys `traefik/whoami:latest` to inst3:8085 and polls until it
226
+ returns HTTP 200. See `python3 deploy_app.py --help` for flags.
227
+
228
+ ## Run the async demo
229
+
230
+ ```bash
231
+ pip install httpx
232
+ python3 deploy_async.py
233
+ ```
234
+
235
+ Drops three redis containers onto a single host **in parallel** via
236
+ `asyncio.gather()`. Verified live: 3 deploys in 22.8s wall clock vs.
237
+ ~57s sequential — a 2.5× speedup, and a 3× win at higher fan-out.
238
+
239
+ ```python
240
+ import asyncio, vxsdk_async as vx
241
+
242
+ async def main():
243
+ async with await vx.AsyncClient.load_from_vxcli() as c:
244
+ results = await asyncio.gather(
245
+ c.deploy.container(host=h1, ssh_user="ubuntu", key_pair_name=K, image="redis:7", ports=["6381:6379"], name="r1"),
246
+ c.deploy.container(host=h2, ssh_user="ubuntu", key_pair_name=K, image="redis:7", ports=["6381:6379"], name="r2"),
247
+ c.deploy.container(host=h3, ssh_user="ubuntu", key_pair_name=K, image="redis:7", ports=["6381:6379"], name="r3"),
248
+ )
249
+
250
+ asyncio.run(main())
251
+ ```
@@ -0,0 +1,73 @@
1
+ # Build the `vxsdk` PyPI package from this directory.
2
+ #
3
+ # python -m build # produces dist/vxsdk-<version>-py3-none-any.whl + .tar.gz
4
+ # twine upload dist/* # publishes to PyPI (or TestPyPI with --repository testpypi)
5
+ #
6
+ # The package ships two top-level modules: vxsdk (sync, stdlib-only) and
7
+ # vxsdk_async (async, requires httpx). Async is opt-in via:
8
+ #
9
+ # pip install vxsdk[async]
10
+ #
11
+ # This avoids forcing httpx onto users who only need the sync client.
12
+
13
+ [build-system]
14
+ requires = ["setuptools>=68", "wheel"]
15
+ build-backend = "setuptools.build_meta"
16
+
17
+ [project]
18
+ name = "vxsdk"
19
+ version = "0.1.0"
20
+ description = "Python SDK for the vxcloud / VxCloud platform — provision infrastructure, deploy applications, and manage running services from your terminal or CI pipeline."
21
+ readme = "README.md"
22
+ requires-python = ">=3.9"
23
+ license = { text = "Apache-2.0" }
24
+ authors = [
25
+ { name = "vxcloud", email = "engineering@vxcloud.io" },
26
+ ]
27
+ keywords = [
28
+ "vxcloud", "vxcloud", "vxcli", "infrastructure", "iac",
29
+ "deploy", "ssh", "docker", "kubernetes", "ai-agents",
30
+ ]
31
+ classifiers = [
32
+ "Development Status :: 4 - Beta",
33
+ "Intended Audience :: Developers",
34
+ "Intended Audience :: System Administrators",
35
+ "License :: OSI Approved :: Apache Software License",
36
+ "Operating System :: OS Independent",
37
+ "Programming Language :: Python :: 3",
38
+ "Programming Language :: Python :: 3.9",
39
+ "Programming Language :: Python :: 3.10",
40
+ "Programming Language :: Python :: 3.11",
41
+ "Programming Language :: Python :: 3.12",
42
+ "Topic :: Software Development :: Libraries :: Python Modules",
43
+ "Topic :: System :: Systems Administration",
44
+ "Topic :: System :: Installation/Setup",
45
+ ]
46
+ dependencies = [] # sync flavor is stdlib-only by design
47
+
48
+ [project.optional-dependencies]
49
+ async = ["httpx>=0.25"]
50
+ dev = [
51
+ "pytest>=7",
52
+ "pytest-asyncio>=0.21",
53
+ "httpx>=0.25",
54
+ "build>=1.0",
55
+ "twine>=4.0",
56
+ ]
57
+
58
+ [project.urls]
59
+ Homepage = "https://vxcloud.io"
60
+ Documentation = "https://vxcloud.io/docs/sdks"
61
+ Repository = "https://github.com/vxcloud/platform"
62
+ Issues = "https://github.com/vxcloud/platform/issues"
63
+ Changelog = "https://github.com/vxcloud/platform/blob/main/services/sdk/python/CHANGELOG.md"
64
+
65
+ # We ship two stand-alone modules (vxsdk.py, vxsdk_async.py) rather than
66
+ # a package with __init__.py — keeps the "drop a single file into your
67
+ # tree" use case alive and matches what the README has documented since
68
+ # preview. py-modules is the explicit setuptools way to declare them.
69
+ [tool.setuptools]
70
+ py-modules = ["vxsdk", "vxsdk_async"]
71
+
72
+ [tool.setuptools.package-data]
73
+ "*" = ["README.md", "CHANGELOG.md"]
vxsdk-0.1.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+