termix-sdk 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.
Files changed (213) hide show
  1. termix_sdk-0.1.0/.gitignore +15 -0
  2. termix_sdk-0.1.0/CHANGELOG.md +65 -0
  3. termix_sdk-0.1.0/CONTRIBUTING.md +155 -0
  4. termix_sdk-0.1.0/LICENSE +21 -0
  5. termix_sdk-0.1.0/NOTICE +43 -0
  6. termix_sdk-0.1.0/PKG-INFO +191 -0
  7. termix_sdk-0.1.0/README.md +157 -0
  8. termix_sdk-0.1.0/examples/README.md +21 -0
  9. termix_sdk-0.1.0/examples/async_quickstart.py +28 -0
  10. termix_sdk-0.1.0/examples/file_manager_session.py +39 -0
  11. termix_sdk-0.1.0/examples/login_with_totp.py +32 -0
  12. termix_sdk-0.1.0/examples/sync_quickstart.py +31 -0
  13. termix_sdk-0.1.0/pyproject.toml +85 -0
  14. termix_sdk-0.1.0/src/termix_sdk/__init__.py +57 -0
  15. termix_sdk-0.1.0/src/termix_sdk/_api_requestor.py +357 -0
  16. termix_sdk-0.1.0/src/termix_sdk/_async_client.py +217 -0
  17. termix_sdk-0.1.0/src/termix_sdk/_auth.py +225 -0
  18. termix_sdk-0.1.0/src/termix_sdk/_client.py +237 -0
  19. termix_sdk-0.1.0/src/termix_sdk/_client_options.py +114 -0
  20. termix_sdk-0.1.0/src/termix_sdk/_error.py +156 -0
  21. termix_sdk-0.1.0/src/termix_sdk/_http_client.py +387 -0
  22. termix_sdk-0.1.0/src/termix_sdk/_object.py +181 -0
  23. termix_sdk-0.1.0/src/termix_sdk/_request_options.py +44 -0
  24. termix_sdk-0.1.0/src/termix_sdk/_response.py +106 -0
  25. termix_sdk-0.1.0/src/termix_sdk/_service.py +153 -0
  26. termix_sdk-0.1.0/src/termix_sdk/_session.py +214 -0
  27. termix_sdk-0.1.0/src/termix_sdk/_sse.py +79 -0
  28. termix_sdk-0.1.0/src/termix_sdk/_util.py +93 -0
  29. termix_sdk-0.1.0/src/termix_sdk/_version.py +6 -0
  30. termix_sdk-0.1.0/src/termix_sdk/models/__init__.py +3 -0
  31. termix_sdk-0.1.0/src/termix_sdk/models/ai.py +59 -0
  32. termix_sdk-0.1.0/src/termix_sdk/models/alerts.py +115 -0
  33. termix_sdk-0.1.0/src/termix_sdk/models/api_keys.py +29 -0
  34. termix_sdk-0.1.0/src/termix_sdk/models/audit.py +21 -0
  35. termix_sdk-0.1.0/src/termix_sdk/models/automations.py +79 -0
  36. termix_sdk-0.1.0/src/termix_sdk/models/credentials.py +102 -0
  37. termix_sdk-0.1.0/src/termix_sdk/models/dashboard.py +36 -0
  38. termix_sdk-0.1.0/src/termix_sdk/models/database.py +38 -0
  39. termix_sdk-0.1.0/src/termix_sdk/models/docker.py +106 -0
  40. termix_sdk-0.1.0/src/termix_sdk/models/encryption.py +35 -0
  41. termix_sdk-0.1.0/src/termix_sdk/models/file_manager.py +234 -0
  42. termix_sdk-0.1.0/src/termix_sdk/models/fleets.py +75 -0
  43. termix_sdk-0.1.0/src/termix_sdk/models/guacamole.py +24 -0
  44. termix_sdk-0.1.0/src/termix_sdk/models/homepage.py +46 -0
  45. termix_sdk-0.1.0/src/termix_sdk/models/host_file_manager.py +34 -0
  46. termix_sdk-0.1.0/src/termix_sdk/models/hosts.py +608 -0
  47. termix_sdk-0.1.0/src/termix_sdk/models/instance_settings.py +215 -0
  48. termix_sdk-0.1.0/src/termix_sdk/models/metrics.py +253 -0
  49. termix_sdk-0.1.0/src/termix_sdk/models/network_topology.py +14 -0
  50. termix_sdk-0.1.0/src/termix_sdk/models/open_tabs.py +43 -0
  51. termix_sdk-0.1.0/src/termix_sdk/models/preferences.py +105 -0
  52. termix_sdk-0.1.0/src/termix_sdk/models/proxmox.py +11 -0
  53. termix_sdk-0.1.0/src/termix_sdk/models/proxmox_stats.py +37 -0
  54. termix_sdk-0.1.0/src/termix_sdk/models/rbac.py +105 -0
  55. termix_sdk-0.1.0/src/termix_sdk/models/session_logs.py +30 -0
  56. termix_sdk-0.1.0/src/termix_sdk/models/session_sharing.py +34 -0
  57. termix_sdk-0.1.0/src/termix_sdk/models/snippets.py +84 -0
  58. termix_sdk-0.1.0/src/termix_sdk/models/sso.py +31 -0
  59. termix_sdk-0.1.0/src/termix_sdk/models/sync.py +27 -0
  60. termix_sdk-0.1.0/src/termix_sdk/models/system.py +22 -0
  61. termix_sdk-0.1.0/src/termix_sdk/models/tailscale.py +15 -0
  62. termix_sdk-0.1.0/src/termix_sdk/models/terminal.py +27 -0
  63. termix_sdk-0.1.0/src/termix_sdk/models/termix_id.py +91 -0
  64. termix_sdk-0.1.0/src/termix_sdk/models/tmux.py +15 -0
  65. termix_sdk-0.1.0/src/termix_sdk/models/tunnel.py +36 -0
  66. termix_sdk-0.1.0/src/termix_sdk/models/tunnel_presets.py +22 -0
  67. termix_sdk-0.1.0/src/termix_sdk/models/user_admin.py +60 -0
  68. termix_sdk-0.1.0/src/termix_sdk/models/users.py +142 -0
  69. termix_sdk-0.1.0/src/termix_sdk/models/vault.py +26 -0
  70. termix_sdk-0.1.0/src/termix_sdk/models/webauthn.py +32 -0
  71. termix_sdk-0.1.0/src/termix_sdk/models/workspaces.py +95 -0
  72. termix_sdk-0.1.0/src/termix_sdk/py.typed +0 -0
  73. termix_sdk-0.1.0/src/termix_sdk/resources/__init__.py +3 -0
  74. termix_sdk-0.1.0/src/termix_sdk/resources/ai.py +457 -0
  75. termix_sdk-0.1.0/src/termix_sdk/resources/alerts.py +562 -0
  76. termix_sdk-0.1.0/src/termix_sdk/resources/api_keys.py +113 -0
  77. termix_sdk-0.1.0/src/termix_sdk/resources/audit.py +120 -0
  78. termix_sdk-0.1.0/src/termix_sdk/resources/automations.py +332 -0
  79. termix_sdk-0.1.0/src/termix_sdk/resources/credentials.py +648 -0
  80. termix_sdk-0.1.0/src/termix_sdk/resources/dashboard.py +287 -0
  81. termix_sdk-0.1.0/src/termix_sdk/resources/database.py +214 -0
  82. termix_sdk-0.1.0/src/termix_sdk/resources/docker.py +689 -0
  83. termix_sdk-0.1.0/src/termix_sdk/resources/encryption.py +145 -0
  84. termix_sdk-0.1.0/src/termix_sdk/resources/file_manager.py +1468 -0
  85. termix_sdk-0.1.0/src/termix_sdk/resources/fleets.py +535 -0
  86. termix_sdk-0.1.0/src/termix_sdk/resources/guacamole.py +133 -0
  87. termix_sdk-0.1.0/src/termix_sdk/resources/homepage.py +364 -0
  88. termix_sdk-0.1.0/src/termix_sdk/resources/host_file_manager.py +331 -0
  89. termix_sdk-0.1.0/src/termix_sdk/resources/hosts.py +1047 -0
  90. termix_sdk-0.1.0/src/termix_sdk/resources/instance_settings.py +1381 -0
  91. termix_sdk-0.1.0/src/termix_sdk/resources/metrics.py +1917 -0
  92. termix_sdk-0.1.0/src/termix_sdk/resources/network_topology.py +82 -0
  93. termix_sdk-0.1.0/src/termix_sdk/resources/open_tabs.py +233 -0
  94. termix_sdk-0.1.0/src/termix_sdk/resources/preferences.py +376 -0
  95. termix_sdk-0.1.0/src/termix_sdk/resources/proxmox.py +117 -0
  96. termix_sdk-0.1.0/src/termix_sdk/resources/proxmox_stats.py +279 -0
  97. termix_sdk-0.1.0/src/termix_sdk/resources/rbac.py +762 -0
  98. termix_sdk-0.1.0/src/termix_sdk/resources/session_logs.py +208 -0
  99. termix_sdk-0.1.0/src/termix_sdk/resources/session_sharing.py +197 -0
  100. termix_sdk-0.1.0/src/termix_sdk/resources/snippets.py +495 -0
  101. termix_sdk-0.1.0/src/termix_sdk/resources/sso.py +223 -0
  102. termix_sdk-0.1.0/src/termix_sdk/resources/sync.py +183 -0
  103. termix_sdk-0.1.0/src/termix_sdk/resources/system.py +116 -0
  104. termix_sdk-0.1.0/src/termix_sdk/resources/tailscale.py +50 -0
  105. termix_sdk-0.1.0/src/termix_sdk/resources/terminal.py +278 -0
  106. termix_sdk-0.1.0/src/termix_sdk/resources/termix_id.py +620 -0
  107. termix_sdk-0.1.0/src/termix_sdk/resources/tmux.py +516 -0
  108. termix_sdk-0.1.0/src/termix_sdk/resources/tunnel.py +217 -0
  109. termix_sdk-0.1.0/src/termix_sdk/resources/tunnel_presets.py +152 -0
  110. termix_sdk-0.1.0/src/termix_sdk/resources/user_admin.py +356 -0
  111. termix_sdk-0.1.0/src/termix_sdk/resources/users.py +884 -0
  112. termix_sdk-0.1.0/src/termix_sdk/resources/vault.py +155 -0
  113. termix_sdk-0.1.0/src/termix_sdk/resources/webauthn.py +269 -0
  114. termix_sdk-0.1.0/src/termix_sdk/resources/workspaces.py +403 -0
  115. termix_sdk-0.1.0/src/termix_sdk/types/__init__.py +3 -0
  116. termix_sdk-0.1.0/src/termix_sdk/types/ai.py +41 -0
  117. termix_sdk-0.1.0/src/termix_sdk/types/alerts.py +56 -0
  118. termix_sdk-0.1.0/src/termix_sdk/types/api_keys.py +16 -0
  119. termix_sdk-0.1.0/src/termix_sdk/types/audit.py +19 -0
  120. termix_sdk-0.1.0/src/termix_sdk/types/automations.py +38 -0
  121. termix_sdk-0.1.0/src/termix_sdk/types/credentials.py +86 -0
  122. termix_sdk-0.1.0/src/termix_sdk/types/dashboard.py +39 -0
  123. termix_sdk-0.1.0/src/termix_sdk/types/database.py +20 -0
  124. termix_sdk-0.1.0/src/termix_sdk/types/docker.py +60 -0
  125. termix_sdk-0.1.0/src/termix_sdk/types/encryption.py +11 -0
  126. termix_sdk-0.1.0/src/termix_sdk/types/file_manager.py +181 -0
  127. termix_sdk-0.1.0/src/termix_sdk/types/fleets.py +53 -0
  128. termix_sdk-0.1.0/src/termix_sdk/types/guacamole.py +27 -0
  129. termix_sdk-0.1.0/src/termix_sdk/types/homepage.py +46 -0
  130. termix_sdk-0.1.0/src/termix_sdk/types/host_file_manager.py +40 -0
  131. termix_sdk-0.1.0/src/termix_sdk/types/hosts.py +355 -0
  132. termix_sdk-0.1.0/src/termix_sdk/types/instance_settings.py +114 -0
  133. termix_sdk-0.1.0/src/termix_sdk/types/metrics.py +130 -0
  134. termix_sdk-0.1.0/src/termix_sdk/types/network_topology.py +14 -0
  135. termix_sdk-0.1.0/src/termix_sdk/types/open_tabs.py +30 -0
  136. termix_sdk-0.1.0/src/termix_sdk/types/preferences.py +68 -0
  137. termix_sdk-0.1.0/src/termix_sdk/types/proxmox.py +18 -0
  138. termix_sdk-0.1.0/src/termix_sdk/types/proxmox_stats.py +27 -0
  139. termix_sdk-0.1.0/src/termix_sdk/types/rbac.py +53 -0
  140. termix_sdk-0.1.0/src/termix_sdk/types/session_logs.py +14 -0
  141. termix_sdk-0.1.0/src/termix_sdk/types/session_sharing.py +21 -0
  142. termix_sdk-0.1.0/src/termix_sdk/types/snippets.py +52 -0
  143. termix_sdk-0.1.0/src/termix_sdk/types/sso.py +33 -0
  144. termix_sdk-0.1.0/src/termix_sdk/types/sync.py +27 -0
  145. termix_sdk-0.1.0/src/termix_sdk/types/system.py +19 -0
  146. termix_sdk-0.1.0/src/termix_sdk/types/tailscale.py +11 -0
  147. termix_sdk-0.1.0/src/termix_sdk/types/terminal.py +31 -0
  148. termix_sdk-0.1.0/src/termix_sdk/types/termix_id.py +51 -0
  149. termix_sdk-0.1.0/src/termix_sdk/types/tmux.py +54 -0
  150. termix_sdk-0.1.0/src/termix_sdk/types/tunnel.py +38 -0
  151. termix_sdk-0.1.0/src/termix_sdk/types/tunnel_presets.py +24 -0
  152. termix_sdk-0.1.0/src/termix_sdk/types/user_admin.py +36 -0
  153. termix_sdk-0.1.0/src/termix_sdk/types/users.py +81 -0
  154. termix_sdk-0.1.0/src/termix_sdk/types/vault.py +42 -0
  155. termix_sdk-0.1.0/src/termix_sdk/types/webauthn.py +31 -0
  156. termix_sdk-0.1.0/src/termix_sdk/types/workspaces.py +35 -0
  157. termix_sdk-0.1.0/tests/__init__.py +0 -0
  158. termix_sdk-0.1.0/tests/conftest.py +85 -0
  159. termix_sdk-0.1.0/tests/contract/__init__.py +4 -0
  160. termix_sdk-0.1.0/tests/contract/test_ai.py +362 -0
  161. termix_sdk-0.1.0/tests/contract/test_alerts.py +789 -0
  162. termix_sdk-0.1.0/tests/contract/test_api_keys.py +114 -0
  163. termix_sdk-0.1.0/tests/contract/test_audit.py +80 -0
  164. termix_sdk-0.1.0/tests/contract/test_automations.py +396 -0
  165. termix_sdk-0.1.0/tests/contract/test_credentials.py +640 -0
  166. termix_sdk-0.1.0/tests/contract/test_dashboard.py +193 -0
  167. termix_sdk-0.1.0/tests/contract/test_database.py +381 -0
  168. termix_sdk-0.1.0/tests/contract/test_docker.py +640 -0
  169. termix_sdk-0.1.0/tests/contract/test_encryption.py +203 -0
  170. termix_sdk-0.1.0/tests/contract/test_file_manager.py +1943 -0
  171. termix_sdk-0.1.0/tests/contract/test_fleets.py +481 -0
  172. termix_sdk-0.1.0/tests/contract/test_guacamole.py +130 -0
  173. termix_sdk-0.1.0/tests/contract/test_homepage.py +259 -0
  174. termix_sdk-0.1.0/tests/contract/test_host_file_manager.py +208 -0
  175. termix_sdk-0.1.0/tests/contract/test_hosts.py +4333 -0
  176. termix_sdk-0.1.0/tests/contract/test_instance_settings.py +1328 -0
  177. termix_sdk-0.1.0/tests/contract/test_metrics.py +1709 -0
  178. termix_sdk-0.1.0/tests/contract/test_network_topology.py +51 -0
  179. termix_sdk-0.1.0/tests/contract/test_open_tabs.py +217 -0
  180. termix_sdk-0.1.0/tests/contract/test_preferences.py +921 -0
  181. termix_sdk-0.1.0/tests/contract/test_proxmox.py +180 -0
  182. termix_sdk-0.1.0/tests/contract/test_proxmox_stats.py +300 -0
  183. termix_sdk-0.1.0/tests/contract/test_rbac.py +925 -0
  184. termix_sdk-0.1.0/tests/contract/test_session_logs.py +135 -0
  185. termix_sdk-0.1.0/tests/contract/test_session_sharing.py +158 -0
  186. termix_sdk-0.1.0/tests/contract/test_snippets.py +493 -0
  187. termix_sdk-0.1.0/tests/contract/test_sso.py +183 -0
  188. termix_sdk-0.1.0/tests/contract/test_sync.py +99 -0
  189. termix_sdk-0.1.0/tests/contract/test_system.py +160 -0
  190. termix_sdk-0.1.0/tests/contract/test_tailscale.py +82 -0
  191. termix_sdk-0.1.0/tests/contract/test_terminal.py +188 -0
  192. termix_sdk-0.1.0/tests/contract/test_termix_id.py +457 -0
  193. termix_sdk-0.1.0/tests/contract/test_tmux.py +319 -0
  194. termix_sdk-0.1.0/tests/contract/test_tunnel.py +291 -0
  195. termix_sdk-0.1.0/tests/contract/test_tunnel_presets.py +501 -0
  196. termix_sdk-0.1.0/tests/contract/test_user_admin.py +377 -0
  197. termix_sdk-0.1.0/tests/contract/test_users.py +812 -0
  198. termix_sdk-0.1.0/tests/contract/test_vault.py +209 -0
  199. termix_sdk-0.1.0/tests/contract/test_webauthn.py +527 -0
  200. termix_sdk-0.1.0/tests/contract/test_workspaces.py +394 -0
  201. termix_sdk-0.1.0/tests/http_client_mock.py +124 -0
  202. termix_sdk-0.1.0/tests/test_api_requestor.py +196 -0
  203. termix_sdk-0.1.0/tests/test_auth.py +157 -0
  204. termix_sdk-0.1.0/tests/test_client.py +52 -0
  205. termix_sdk-0.1.0/tests/test_error.py +87 -0
  206. termix_sdk-0.1.0/tests/test_generated_pilot.py +163 -0
  207. termix_sdk-0.1.0/tests/test_http_client.py +57 -0
  208. termix_sdk-0.1.0/tests/test_object.py +102 -0
  209. termix_sdk-0.1.0/tests/test_options.py +72 -0
  210. termix_sdk-0.1.0/tests/test_response.py +59 -0
  211. termix_sdk-0.1.0/tests/test_session.py +147 -0
  212. termix_sdk-0.1.0/tests/test_sse.py +68 -0
  213. termix_sdk-0.1.0/tests/test_util.py +41 -0
@@ -0,0 +1,15 @@
1
+ __pycache__/
2
+ *.pyc
3
+ .venv/
4
+ venv/
5
+ dist/
6
+ build/
7
+ *.egg-info/
8
+ .pytest_cache/
9
+ .mypy_cache/
10
+ .ruff_cache/
11
+ /docs/
12
+ node_modules/
13
+ .coverage
14
+ coverage.xml
15
+ htmlcov/
@@ -0,0 +1,65 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented in this file.
4
+
5
+ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
6
+
7
+ ## [Unreleased]
8
+
9
+ ## [0.1.0] - 2026-09-19
10
+
11
+ First release. Generated against Termix `release-2.7.1-tag`.
12
+
13
+ ### Added
14
+
15
+ - `TermixClient` / `AsyncTermixClient`: connection options (`base_url`,
16
+ per-service `service_urls` override, `api_key` or `jwt`, timeout,
17
+ retries), a low-level `request()` escape hatch, and context-manager
18
+ support.
19
+ - `TermixClient.login()` / `AsyncTermixClient.login()`: username/password
20
+ login handling Termix's native-app-header requirement for getting a
21
+ usable JWT back, including the TOTP second-factor flow
22
+ (`PendingTOTP`/`AsyncPendingTOTP`).
23
+ - A typed exception hierarchy (`TermixError` and subclasses —
24
+ `AuthenticationError`, `PermissionError`, `NotFoundError`,
25
+ `RateLimitError` with `remaining_time`, `DataLockedError`,
26
+ `TOTPRequiredError`, `SessionExpiredError`, `InvalidRequestError`,
27
+ `ServerError`) mapped from Termix's actual error response shapes.
28
+ - `TermixObject`: dict-backed base model with attribute access,
29
+ `to_dict()`, and secret redaction in `repr()`/logs.
30
+ - 477 of 491 real Termix endpoints, generated across 43 resource
31
+ modules (`client.hosts`, `client.credentials`, `client.file_manager`,
32
+ `client.metrics`, ... — see `tools/sdk-gen/config/resource-map.json`
33
+ for the full list), each with a sync and an async client.
34
+ - `ssh_session()` / `async_ssh_session()`: connect/keepalive/disconnect
35
+ lifecycle helper for the SSH-backed session pattern `file_manager` and
36
+ `docker` share.
37
+ - SSE support (`iter_sse_events`/`aiter_sse_events`, `SSEEvent`) for the
38
+ 3 `text/event-stream` endpoints (tunnel status, Proxmox discovery, AI
39
+ chat streaming).
40
+ - Real multipart file upload (`fleets.transfer_push`) where a field has
41
+ no JSON equivalent.
42
+ - `tools/sdk-gen/generate.py`: the OpenAPI-spec-to-Python-client
43
+ generator, plus `tests/contract/`, one generated test per operation.
44
+ - `py.typed` marker, so mypy/pyright pick up the package's inline types.
45
+
46
+ ### Fixed
47
+
48
+ - `ssh_session()`/`async_ssh_session()` now type-check when passed a
49
+ generated service (`client.file_manager`, `client.docker`): the
50
+ session protocol accepted only `**params: Any` methods, which the
51
+ generated `Unpack[TypedDict]` signatures don't satisfy.
52
+
53
+ ### Known gaps
54
+
55
+ - 3 `file_manager` endpoints (`uploadFileStream`, `downloadFileStream`,
56
+ `uploadFileChunk`) send/receive a raw body the spec has no documented
57
+ schema for — not generated, to avoid inventing behavior the source
58
+ doesn't support.
59
+ - 11 endpoints are out of scope by design: internal-auth-only routes,
60
+ browser-redirect OIDC/opkssh flows, and `POST /users/login` /
61
+ `POST /users/totp/verify-login` (handled by `TermixClient.login()`
62
+ instead).
63
+ - No live-instance test suite yet (`tests/` and `tests/contract/` run
64
+ against a scripted fake transport only) — see CONTRIBUTING.md's
65
+ testing-philosophy section.
@@ -0,0 +1,155 @@
1
+ # Contributing
2
+
3
+ ## Setup
4
+
5
+ ```bash
6
+ git clone https://github.com/MatheusAlves96/termix-sdk.git
7
+ cd termix-sdk
8
+ uv sync --extra dev # creates .venv from uv.lock (pinned tool versions)
9
+ ```
10
+
11
+ Without [uv](https://docs.astral.sh/uv/): `python -m venv .venv`, activate
12
+ it, `pip install -e ".[dev]"`. You then get whatever `ruff`/`mypy`/`pytest`
13
+ versions resolve today rather than the locked ones CI uses, so a check may
14
+ pass locally and fail in CI (or the reverse) — the lockfile exists to avoid
15
+ exactly that.
16
+
17
+ ## Running the checks
18
+
19
+ ```bash
20
+ uv run pytest # tests/ (unit) + tests/contract/ (generated)
21
+ uv run ruff check .
22
+ uv run ruff format --check .
23
+ uv run mypy # src/ and examples/
24
+ ```
25
+
26
+ ## What CI checks
27
+
28
+ `.github/workflows/ci.yml` runs on every push to `main` and every PR:
29
+
30
+ | Job | What it does |
31
+ |-----|--------------|
32
+ | `lint` | `ruff check`, `ruff format --check`, `mypy` (Python 3.13) |
33
+ | `test (3.10..3.13)` | `pytest` with coverage; the 3.13 run uploads to Codecov (informational, never blocks) |
34
+ | `generated-sync` | runs `tools/sdk-gen/generate.py` and fails if any committed file changes — hand-edits to `resources/`, `models/`, `types/` or `tests/contract/` are caught here |
35
+ | `spec-gen-typecheck` | `npm ci && npm run typecheck` in `tools/spec-gen` |
36
+ | `build` | `uv build`, `twine check`, installs the wheel into a clean venv, imports it and checks `py.typed` is shipped; uploads `dist/` |
37
+
38
+ `uv.lock` is enforced (`UV_LOCKED=1`): if you change `pyproject.toml`
39
+ dependencies, run `uv lock` and commit the lockfile, or CI fails at
40
+ `uv sync`.
41
+
42
+ `.github/workflows/zizmor.yml` lints the workflows themselves for
43
+ security issues (unpinned actions, template injection, leaked
44
+ credentials). Actions are pinned by commit SHA; Dependabot
45
+ (`.github/dependabot.yml`) opens weekly PRs to bump them, plus Python
46
+ and `tools/spec-gen` npm dependencies.
47
+
48
+ ## Releasing
49
+
50
+ Publishing is driven by a `vX.Y.Z` tag (`.github/workflows/release.yml`)
51
+ and goes to PyPI through Trusted Publishing — no API token lives in the
52
+ repo. The `pypi` GitHub environment requires a manual approval before
53
+ the publish step runs.
54
+
55
+ 1. Bump `__version__` in `src/termix_sdk/_version.py`.
56
+ 2. In `CHANGELOG.md`, rename `## [Unreleased]` to `## [X.Y.Z] - YYYY-MM-DD`
57
+ and add a fresh empty `## [Unreleased]` above it. The release notes on
58
+ GitHub are that section, verbatim.
59
+ 3. Open a PR with those two changes and merge it.
60
+ 4. `git tag vX.Y.Z main && git push origin vX.Y.Z`.
61
+ 5. The `preflight` job checks the tag matches `__version__` and that the
62
+ CHANGELOG section exists, then the full CI runs again against the tag.
63
+ 6. Approve the `pypi` environment deployment in the Actions UI. The
64
+ workflow publishes to PyPI and creates the GitHub Release with
65
+ `dist/` attached.
66
+
67
+ ## How this repo is put together
68
+
69
+ Three layers, in dependency order:
70
+
71
+ 1. **`tools/spec-gen/`** (TypeScript) — mines the Termix backend source
72
+ for every route, its auth, its request/response shape, and the
73
+ Drizzle schema, and emits `spec/termix-openapi.json`. See
74
+ [its own strategy doc](tools/spec-gen/docs/spec-generation-strategy.md)
75
+ for why this exists instead of trusting Termix's own `openapi.json`.
76
+ Not something you normally need to touch or re-run — only when
77
+ targeting a new Termix release (see below).
78
+
79
+ 2. **`tools/sdk-gen/generate.py`** (Python) — reads `spec/termix-
80
+ openapi.json` plus the curated `tools/sdk-gen/config/resource-map.json`
81
+ (which operations exist, what Python method name and module each
82
+ gets — the spec alone doesn't have good naming) and writes
83
+ `src/termix_sdk/resources/*.py`, `models/*.py`, `types/*.py`, and
84
+ `tests/contract/test_*.py`. **Never hand-edit any of those** — the
85
+ next `python tools/sdk-gen/generate.py` run overwrites them.
86
+
87
+ 3. **Hand-written core** (`src/termix_sdk/_*.py`) — the client,
88
+ requestor, error hierarchy, object model, session helper, SSE/stream
89
+ parsing. This is what the generated code in (2) is built on top of,
90
+ and most of it is adapted from
91
+ [stripe-python](https://github.com/stripe/stripe-python) (see
92
+ [NOTICE](NOTICE)) rather than written from scratch.
93
+
94
+ ## Adding or fixing a generated operation
95
+
96
+ 1. Confirm the operation exists in `spec/termix-openapi.json` (search by
97
+ path). If it's missing entirely, that's a `tools/spec-gen` gap, not
98
+ an `sdk-gen` one.
99
+ 2. Add (or fix) its entry in `tools/sdk-gen/config/resource-map.json`,
100
+ under the right module. An empty `{}` takes the default name from
101
+ `generate.py`'s `default_method_name()`; most operations need an
102
+ explicit `"method"` override — the default rule only really fits
103
+ plain CRUD on a bare `{id}`.
104
+ 3. Run `python tools/sdk-gen/generate.py`. It also runs `ruff format` +
105
+ `ruff check --fix` on everything it just wrote, and fails loudly (not
106
+ silently) on:
107
+ - a method name that collides with another in the same module
108
+ (`ruff`'s `F811`, at the `ruff check --fix` step)
109
+ - a method name that's a Python keyword (raised directly)
110
+ - a `resource-map.json` entry naming an `operationId` the spec
111
+ doesn't have (raised directly)
112
+ 4. Run `uv run pytest`, `uv run mypy`, `uv run ruff check .` — same as CI.
113
+ Commit the regenerated files: the `generated-sync` CI job regenerates
114
+ and fails on any diff.
115
+ 5. A field typed `Dict[str, Any]`/`List[...]` (or a list of a generated
116
+ model) is, at runtime, wrapped as a nested `TermixObject`/list of
117
+ `TermixObject` regardless of that static annotation — compare via
118
+ `.to_dict()` in any test you hand-write against generated code, not
119
+ field-by-field attribute equality against a plain dict/list literal.
120
+
121
+ `tools/sdk-gen/generate.py`'s own module docstring lists the known
122
+ simplifications this makes (every request-body field optional in the
123
+ generated `TypedDict` regardless of the spec's `required` list, no
124
+ per-operation `x-requires-admin`/`x-requires-data-access` docstring
125
+ callouts, etc.) — read it before making a structural change to the
126
+ generator itself.
127
+
128
+ ## Regenerating against a new Termix release
129
+
130
+ ```bash
131
+ cd tools/spec-gen
132
+ npm ci
133
+ npm run generate -- --tag <release-tag> # or --tag latest
134
+ cd ../..
135
+ python tools/sdk-gen/generate.py
136
+ pytest
137
+ ```
138
+
139
+ Diff the new `spec/termix-openapi.json` against the previous one for new
140
+ or removed operations before touching `resource-map.json` — `sdk-gen`
141
+ only processes what the map lists, so a brand-new endpoint won't appear
142
+ in the SDK until it's added there.
143
+
144
+ ## Testing philosophy
145
+
146
+ `tests/` (hand-written) and `tests/contract/` (generated, one test per
147
+ operation) both run against a scripted fake transport
148
+ (`tests/http_client_mock.py`) — nothing here talks to a real Termix
149
+ instance. That's a deliberate scope decision, not an oversight: broad
150
+ live-instance coverage would need real SSH hosts, Docker containers, and
151
+ tmux sessions to test against, plus care around endpoints with real
152
+ side effects (`database/export`, `encryption/regenerate-jwt`,
153
+ `delete-account`, fleet actions across real hosts) that shouldn't run
154
+ unattended in CI. If you want to exercise the SDK against a real
155
+ instance, do so manually — there's no `tests/live/` yet.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Matheus Alves
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,43 @@
1
+ termix-sdk includes code adapted from stripe-python
2
+ (https://github.com/stripe/stripe-python), used under the MIT License:
3
+
4
+ The MIT License
5
+
6
+ Copyright (c) 2010-2018 Stripe (http://stripe.com)
7
+
8
+ Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ of this software and associated documentation files (the "Software"), to deal
10
+ in the Software without restriction, including without limitation the rights
11
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
+ copies of the Software, and to permit persons to whom the Software is
13
+ furnished to do so, subject to the following conditions:
14
+
15
+ The above copyright notice and this permission notice shall be included in
16
+ all copies or substantial portions of the Software.
17
+
18
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24
+ THE SOFTWARE.
25
+
26
+ Files adapted from stripe-python (each carries its own header pointing back
27
+ here) as of 2026-09-19, per docs/sdk-plan.md section 5:
28
+
29
+ src/termix_sdk/_error.py <- stripe/_error.py
30
+ src/termix_sdk/_object.py <- stripe/_stripe_object.py
31
+ src/termix_sdk/_response.py <- stripe/_stripe_response.py
32
+ src/termix_sdk/_client_options.py <- stripe/_client_options.py, stripe/_requestor_options.py
33
+ src/termix_sdk/_request_options.py <- stripe/_request_options.py
34
+ src/termix_sdk/_http_client.py <- stripe/_http_client.py
35
+ src/termix_sdk/_api_requestor.py <- stripe/_api_requestor.py
36
+ src/termix_sdk/_service.py <- stripe/_stripe_service.py
37
+ src/termix_sdk/_client.py <- stripe/_stripe_client.py
38
+ src/termix_sdk/_async_client.py <- stripe/_stripe_client.py
39
+ src/termix_sdk/_util.py <- stripe/_util.py
40
+
41
+ None of this implies any endorsement by or affiliation with Stripe. Termix
42
+ is a separate, unrelated project; termix-sdk is an independent, unofficial
43
+ client for it.
@@ -0,0 +1,191 @@
1
+ Metadata-Version: 2.5
2
+ Name: termix-sdk
3
+ Version: 0.1.0
4
+ Summary: Unofficial Python client SDK for the Termix REST API
5
+ Project-URL: Homepage, https://github.com/MatheusAlves96/termix-sdk
6
+ Project-URL: Issues, https://github.com/MatheusAlves96/termix-sdk/issues
7
+ Project-URL: Changelog, https://github.com/MatheusAlves96/termix-sdk/blob/main/CHANGELOG.md
8
+ Author-email: Matheus Alves <matheusalves965@gmail.com>
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ License-File: NOTICE
12
+ Keywords: api-client,sdk,ssh,termix
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
+ Classifier: Typing :: Typed
24
+ Requires-Python: >=3.10
25
+ Requires-Dist: httpx>=0.27
26
+ Requires-Dist: typing-extensions>=4.10
27
+ Provides-Extra: dev
28
+ Requires-Dist: mypy>=1.10; extra == 'dev'
29
+ Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
30
+ Requires-Dist: pytest-cov>=6.0; extra == 'dev'
31
+ Requires-Dist: pytest>=8.0; extra == 'dev'
32
+ Requires-Dist: ruff>=0.6; extra == 'dev'
33
+ Description-Content-Type: text/markdown
34
+
35
+ # termix-sdk
36
+
37
+ Unofficial Python client SDK for the [Termix](https://github.com/Termix-SSH/Termix) REST API.
38
+
39
+ Generated from a spec this repo derives directly from the Termix backend
40
+ source, not from Termix's own hand-written `openapi.json` — see
41
+ [Generating our own Termix API spec](tools/spec-gen/docs/spec-generation-strategy.md)
42
+ for why. As of `release-2.7.1-tag`, that covers **477 of 491** real
43
+ endpoints; the rest are either genuinely out of scope (browser-redirect
44
+ OIDC flows, internal-only routes) or need a documented request/response
45
+ shape the spec doesn't have (3 `file_manager` streaming-upload endpoints).
46
+
47
+ ## Install
48
+
49
+ ```bash
50
+ pip install termix-sdk
51
+ ```
52
+
53
+ Requires Python 3.10+. The only runtime dependencies are `httpx` and
54
+ `typing-extensions`.
55
+
56
+ ## Quickstart
57
+
58
+ An API key (`tmx_...`, created in Termix under Settings → API Keys) is the
59
+ recommended way to authenticate — it skips the login/TOTP dance entirely:
60
+
61
+ ```python
62
+ from termix_sdk import TermixClient
63
+
64
+ client = TermixClient(base_url="https://termix.example.com", api_key="tmx_...")
65
+
66
+ for host in client.hosts.list():
67
+ print(host.name, host.ip)
68
+
69
+ client.close()
70
+ ```
71
+
72
+ `TermixClient` is also a context manager:
73
+
74
+ ```python
75
+ with TermixClient(base_url="https://termix.example.com", api_key="tmx_...") as client:
76
+ host = client.hosts.retrieve("42")
77
+ ```
78
+
79
+ ### Username/password login
80
+
81
+ `TermixClient.login()` handles Termix's login quirk for you: the backend
82
+ only puts the JWT in the response body (rather than only in a cookie a
83
+ Python client can't use) when the request looks like it came from a
84
+ native app, so this always sends that header.
85
+
86
+ ```python
87
+ from termix_sdk import TermixClient, PendingTOTP
88
+
89
+ result = TermixClient.login(
90
+ base_url="https://termix.example.com",
91
+ username="alice",
92
+ password="...",
93
+ )
94
+
95
+ if isinstance(result, PendingTOTP):
96
+ # The account has TOTP enabled and this device isn't trusted yet.
97
+ client = result.verify(input("6-digit code: "))
98
+ else:
99
+ client = result
100
+ ```
101
+
102
+ ### Async
103
+
104
+ Every resource has an async twin, via a separate client rather than
105
+ `_async` suffixed methods:
106
+
107
+ ```python
108
+ import asyncio
109
+ from termix_sdk import AsyncTermixClient
110
+
111
+
112
+ async def main():
113
+ async with AsyncTermixClient(
114
+ base_url="https://termix.example.com", api_key="tmx_..."
115
+ ) as client:
116
+ hosts = await client.hosts.list()
117
+ print([h.name for h in hosts])
118
+
119
+
120
+ asyncio.run(main())
121
+ ```
122
+
123
+ ### SSH-backed sessions (file manager, Docker)
124
+
125
+ `file_manager` and `docker` open an SSH-backed session server-side
126
+ before you can call anything else on them. `ssh_session()` (and its
127
+ async twin `async_ssh_session()`) manage that lifecycle — connect,
128
+ optional keepalive, disconnect on exit, even on error:
129
+
130
+ ```python
131
+ from termix_sdk import ssh_session
132
+
133
+ with ssh_session(client.file_manager, host_id=42) as fm:
134
+ for entry in fm.list_files(path="/"):
135
+ print(entry)
136
+ # disconnected automatically here
137
+ ```
138
+
139
+ Every method is still callable directly with an explicit `session_id` if
140
+ you'd rather manage the lifecycle yourself — `ssh_session()` is a
141
+ convenience layer on top, not a requirement.
142
+
143
+ ### Errors
144
+
145
+ Every non-2xx response raises a typed subclass of `TermixError`:
146
+
147
+ ```python
148
+ from termix_sdk import AuthenticationError, NotFoundError, RateLimitError, TermixError
149
+
150
+ try:
151
+ client.hosts.retrieve("does-not-exist")
152
+ except NotFoundError as e:
153
+ print(e.code, e.details)
154
+ except RateLimitError as e:
155
+ print("retry after", e.remaining_time, "seconds")
156
+ except TermixError as e:
157
+ print(e.http_status, e)
158
+ ```
159
+
160
+ ## What's generated vs. hand-written
161
+
162
+ `src/termix_sdk/resources/`, `models/`, and `types/` are generated by
163
+ `tools/sdk-gen/generate.py` from `spec/termix-openapi.json` and
164
+ `tools/sdk-gen/config/resource-map.json` — don't hand-edit them, changes
165
+ will be overwritten on the next run. Everything else (`_client.py`,
166
+ `_error.py`, `_object.py`, `_session.py`, `_sse.py`, ...) is hand-written
167
+ core, adapted from [stripe/stripe-python](https://github.com/stripe/stripe-python)
168
+ where noted — see [NOTICE](NOTICE) for the MIT attribution this carries.
169
+
170
+ ## Development
171
+
172
+ ```bash
173
+ pip install -e ".[dev]"
174
+ pytest
175
+ ruff check .
176
+ mypy
177
+ ```
178
+
179
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for how the spec/generator/tests
180
+ fit together and how to regenerate the SDK after a Termix release.
181
+
182
+ ## Documentation
183
+
184
+ - [Generating our own Termix API spec](tools/spec-gen/docs/spec-generation-strategy.md): why the SDK does not rely on the official `openapi.json`, and how the spec is derived from the Termix backend source (route discovery, auth, typed request bodies, every response per status code, Drizzle schema, test examples).
185
+ - [CHANGELOG.md](CHANGELOG.md)
186
+ - [CONTRIBUTING.md](CONTRIBUTING.md)
187
+
188
+ ## License
189
+
190
+ MIT — see [LICENSE](LICENSE). Portions adapted from stripe-python are
191
+ also MIT; see [NOTICE](NOTICE).
@@ -0,0 +1,157 @@
1
+ # termix-sdk
2
+
3
+ Unofficial Python client SDK for the [Termix](https://github.com/Termix-SSH/Termix) REST API.
4
+
5
+ Generated from a spec this repo derives directly from the Termix backend
6
+ source, not from Termix's own hand-written `openapi.json` — see
7
+ [Generating our own Termix API spec](tools/spec-gen/docs/spec-generation-strategy.md)
8
+ for why. As of `release-2.7.1-tag`, that covers **477 of 491** real
9
+ endpoints; the rest are either genuinely out of scope (browser-redirect
10
+ OIDC flows, internal-only routes) or need a documented request/response
11
+ shape the spec doesn't have (3 `file_manager` streaming-upload endpoints).
12
+
13
+ ## Install
14
+
15
+ ```bash
16
+ pip install termix-sdk
17
+ ```
18
+
19
+ Requires Python 3.10+. The only runtime dependencies are `httpx` and
20
+ `typing-extensions`.
21
+
22
+ ## Quickstart
23
+
24
+ An API key (`tmx_...`, created in Termix under Settings → API Keys) is the
25
+ recommended way to authenticate — it skips the login/TOTP dance entirely:
26
+
27
+ ```python
28
+ from termix_sdk import TermixClient
29
+
30
+ client = TermixClient(base_url="https://termix.example.com", api_key="tmx_...")
31
+
32
+ for host in client.hosts.list():
33
+ print(host.name, host.ip)
34
+
35
+ client.close()
36
+ ```
37
+
38
+ `TermixClient` is also a context manager:
39
+
40
+ ```python
41
+ with TermixClient(base_url="https://termix.example.com", api_key="tmx_...") as client:
42
+ host = client.hosts.retrieve("42")
43
+ ```
44
+
45
+ ### Username/password login
46
+
47
+ `TermixClient.login()` handles Termix's login quirk for you: the backend
48
+ only puts the JWT in the response body (rather than only in a cookie a
49
+ Python client can't use) when the request looks like it came from a
50
+ native app, so this always sends that header.
51
+
52
+ ```python
53
+ from termix_sdk import TermixClient, PendingTOTP
54
+
55
+ result = TermixClient.login(
56
+ base_url="https://termix.example.com",
57
+ username="alice",
58
+ password="...",
59
+ )
60
+
61
+ if isinstance(result, PendingTOTP):
62
+ # The account has TOTP enabled and this device isn't trusted yet.
63
+ client = result.verify(input("6-digit code: "))
64
+ else:
65
+ client = result
66
+ ```
67
+
68
+ ### Async
69
+
70
+ Every resource has an async twin, via a separate client rather than
71
+ `_async` suffixed methods:
72
+
73
+ ```python
74
+ import asyncio
75
+ from termix_sdk import AsyncTermixClient
76
+
77
+
78
+ async def main():
79
+ async with AsyncTermixClient(
80
+ base_url="https://termix.example.com", api_key="tmx_..."
81
+ ) as client:
82
+ hosts = await client.hosts.list()
83
+ print([h.name for h in hosts])
84
+
85
+
86
+ asyncio.run(main())
87
+ ```
88
+
89
+ ### SSH-backed sessions (file manager, Docker)
90
+
91
+ `file_manager` and `docker` open an SSH-backed session server-side
92
+ before you can call anything else on them. `ssh_session()` (and its
93
+ async twin `async_ssh_session()`) manage that lifecycle — connect,
94
+ optional keepalive, disconnect on exit, even on error:
95
+
96
+ ```python
97
+ from termix_sdk import ssh_session
98
+
99
+ with ssh_session(client.file_manager, host_id=42) as fm:
100
+ for entry in fm.list_files(path="/"):
101
+ print(entry)
102
+ # disconnected automatically here
103
+ ```
104
+
105
+ Every method is still callable directly with an explicit `session_id` if
106
+ you'd rather manage the lifecycle yourself — `ssh_session()` is a
107
+ convenience layer on top, not a requirement.
108
+
109
+ ### Errors
110
+
111
+ Every non-2xx response raises a typed subclass of `TermixError`:
112
+
113
+ ```python
114
+ from termix_sdk import AuthenticationError, NotFoundError, RateLimitError, TermixError
115
+
116
+ try:
117
+ client.hosts.retrieve("does-not-exist")
118
+ except NotFoundError as e:
119
+ print(e.code, e.details)
120
+ except RateLimitError as e:
121
+ print("retry after", e.remaining_time, "seconds")
122
+ except TermixError as e:
123
+ print(e.http_status, e)
124
+ ```
125
+
126
+ ## What's generated vs. hand-written
127
+
128
+ `src/termix_sdk/resources/`, `models/`, and `types/` are generated by
129
+ `tools/sdk-gen/generate.py` from `spec/termix-openapi.json` and
130
+ `tools/sdk-gen/config/resource-map.json` — don't hand-edit them, changes
131
+ will be overwritten on the next run. Everything else (`_client.py`,
132
+ `_error.py`, `_object.py`, `_session.py`, `_sse.py`, ...) is hand-written
133
+ core, adapted from [stripe/stripe-python](https://github.com/stripe/stripe-python)
134
+ where noted — see [NOTICE](NOTICE) for the MIT attribution this carries.
135
+
136
+ ## Development
137
+
138
+ ```bash
139
+ pip install -e ".[dev]"
140
+ pytest
141
+ ruff check .
142
+ mypy
143
+ ```
144
+
145
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for how the spec/generator/tests
146
+ fit together and how to regenerate the SDK after a Termix release.
147
+
148
+ ## Documentation
149
+
150
+ - [Generating our own Termix API spec](tools/spec-gen/docs/spec-generation-strategy.md): why the SDK does not rely on the official `openapi.json`, and how the spec is derived from the Termix backend source (route discovery, auth, typed request bodies, every response per status code, Drizzle schema, test examples).
151
+ - [CHANGELOG.md](CHANGELOG.md)
152
+ - [CONTRIBUTING.md](CONTRIBUTING.md)
153
+
154
+ ## License
155
+
156
+ MIT — see [LICENSE](LICENSE). Portions adapted from stripe-python are
157
+ also MIT; see [NOTICE](NOTICE).
@@ -0,0 +1,21 @@
1
+ # Examples
2
+
3
+ Each script expects a real, reachable Termix instance and reads its
4
+ connection details from environment variables — nothing here talks to a
5
+ mock. Set at least:
6
+
7
+ ```bash
8
+ export TERMIX_BASE_URL="https://termix.example.com"
9
+ export TERMIX_API_KEY="tmx_..." # sync_quickstart.py, async_quickstart.py, file_manager_session.py
10
+ export TERMIX_USERNAME="alice" # login_with_totp.py
11
+ export TERMIX_PASSWORD="..." # login_with_totp.py
12
+ ```
13
+
14
+ | Script | Shows |
15
+ |---|---|
16
+ | [`sync_quickstart.py`](sync_quickstart.py) | `TermixClient`, listing hosts, error handling |
17
+ | [`async_quickstart.py`](async_quickstart.py) | `AsyncTermixClient`, the same thing with `asyncio` |
18
+ | [`login_with_totp.py`](login_with_totp.py) | `TermixClient.login()`, handling `PendingTOTP` |
19
+ | [`file_manager_session.py`](file_manager_session.py) | `ssh_session()` against `client.file_manager` |
20
+
21
+ Run any of them with `python examples/<script>.py`.