dominus-sdk-python 6.0.0__tar.gz → 6.0.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 (66) hide show
  1. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/PKG-INFO +2 -3
  2. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/__init__.py +5 -1
  3. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/namespaces/__init__.py +4 -0
  4. dominus_sdk_python-6.0.1/dominus/namespaces/coder.py +221 -0
  5. dominus_sdk_python-6.0.1/dominus/namespaces/platform.py +130 -0
  6. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/start.py +4 -0
  7. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus_sdk_python.egg-info/PKG-INFO +2 -3
  8. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus_sdk_python.egg-info/SOURCES.txt +3 -0
  9. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/pyproject.toml +3 -4
  10. dominus_sdk_python-6.0.1/tests/test_platform_coder_namespaces.py +118 -0
  11. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/tests/test_public_exports.py +6 -0
  12. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/README.md +0 -0
  13. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/config/__init__.py +0 -0
  14. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/config/endpoints.py +0 -0
  15. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/errors.py +0 -0
  16. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/helpers/__init__.py +0 -0
  17. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/helpers/auth.py +0 -0
  18. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/helpers/cache.py +0 -0
  19. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/helpers/console_capture.py +0 -0
  20. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/helpers/core.py +0 -0
  21. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/helpers/crypto.py +0 -0
  22. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/helpers/sse.py +0 -0
  23. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/helpers/trace.py +0 -0
  24. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/namespaces/admin.py +0 -0
  25. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/namespaces/ai.py +0 -0
  26. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/namespaces/artifacts.py +0 -0
  27. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/namespaces/auth.py +0 -0
  28. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/namespaces/authority.py +0 -0
  29. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/namespaces/browser.py +0 -0
  30. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/namespaces/courier.py +0 -0
  31. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/namespaces/db.py +0 -0
  32. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/namespaces/ddl.py +0 -0
  33. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/namespaces/deployer.py +0 -0
  34. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/namespaces/fastapi.py +0 -0
  35. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/namespaces/files.py +0 -0
  36. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/namespaces/health.py +0 -0
  37. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/namespaces/jobs.py +0 -0
  38. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/namespaces/logs.py +0 -0
  39. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/namespaces/portal.py +0 -0
  40. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/namespaces/processor.py +0 -0
  41. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/namespaces/recipes.py +0 -0
  42. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/namespaces/redis.py +0 -0
  43. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/namespaces/secrets.py +0 -0
  44. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/namespaces/secure.py +0 -0
  45. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/namespaces/stash.py +0 -0
  46. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/namespaces/sync.py +0 -0
  47. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/namespaces/warden.py +0 -0
  48. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/namespaces/workflow.py +0 -0
  49. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/services/__init__.py +0 -0
  50. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus_sdk_python.egg-info/dependency_links.txt +0 -0
  51. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus_sdk_python.egg-info/requires.txt +0 -0
  52. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus_sdk_python.egg-info/top_level.txt +0 -0
  53. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/setup.cfg +0 -0
  54. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/tests/test_auth.py +0 -0
  55. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/tests/test_authority_public_vocabulary.py +0 -0
  56. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/tests/test_browser_namespace.py +0 -0
  57. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/tests/test_control_plane_namespaces.py +0 -0
  58. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/tests/test_errors.py +0 -0
  59. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/tests/test_flat_commands.py +0 -0
  60. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/tests/test_health.py +0 -0
  61. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/tests/test_logs.py +0 -0
  62. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/tests/test_provisioning_parity.py +0 -0
  63. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/tests/test_recipes_namespace.py +0 -0
  64. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/tests/test_transport_compat.py +0 -0
  65. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/tests/test_workflow_lifecycle.py +0 -0
  66. {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/tests/test_workflow_refs.py +0 -0
@@ -1,15 +1,14 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dominus-sdk-python
3
- Version: 6.0.0
3
+ Version: 6.0.1
4
4
  Summary: Python SDK for the Dominus gateway-first platform
5
5
  Author-email: CareBridge Systems <dev@carebridge.io>
6
- License: Proprietary
6
+ License-Expression: LicenseRef-Proprietary
7
7
  Project-URL: Homepage, https://github.com/carebridgesystems/dominus-sdk-python
8
8
  Project-URL: Repository, https://github.com/carebridgesystems/dominus-sdk-python
9
9
  Keywords: dominus,carebridge,sdk,gateway,api,async
10
10
  Classifier: Development Status :: 4 - Beta
11
11
  Classifier: Intended Audience :: Developers
12
- Classifier: License :: Other/Proprietary License
13
12
  Classifier: Operating System :: OS Independent
14
13
  Classifier: Programming Language :: Python :: 3
15
14
  Classifier: Programming Language :: Python :: 3.9
@@ -112,6 +112,8 @@ from .namespaces.browser import BrowserNamespace
112
112
  from .namespaces.recipes import RecipesNamespace
113
113
  from .namespaces.deployer import DeployerNamespace
114
114
  from .namespaces.warden import WardenNamespace
115
+ from .namespaces.platform import PlatformNamespace
116
+ from .namespaces.coder import CoderNamespace
115
117
 
116
118
  # Export AI namespace for agent-runtime operations
117
119
  from .namespaces.ai import (
@@ -162,7 +164,7 @@ from .errors import (
162
164
  TimeoutError as DominusTimeoutError,
163
165
  )
164
166
 
165
- __version__ = "6.0.0"
167
+ __version__ = "6.0.1"
166
168
  __all__ = [
167
169
  # Main SDK instance
168
170
  "dominus",
@@ -213,6 +215,8 @@ __all__ = [
213
215
  "RecipesNamespace",
214
216
  "DeployerNamespace",
215
217
  "WardenNamespace",
218
+ "PlatformNamespace",
219
+ "CoderNamespace",
216
220
  # AI namespace for agent-runtime operations
217
221
  "AiNamespace",
218
222
  "RagSubNamespace",
@@ -19,6 +19,8 @@ from .browser import BrowserNamespace
19
19
  from .recipes import RecipesNamespace
20
20
  from .deployer import DeployerNamespace
21
21
  from .warden import WardenNamespace
22
+ from .platform import PlatformNamespace
23
+ from .coder import CoderNamespace
22
24
  from .ai import (
23
25
  AiNamespace,
24
26
  RagSubNamespace,
@@ -48,6 +50,8 @@ __all__ = [
48
50
  "RecipesNamespace",
49
51
  "DeployerNamespace",
50
52
  "WardenNamespace",
53
+ "PlatformNamespace",
54
+ "CoderNamespace",
51
55
  "AiNamespace",
52
56
  "RagSubNamespace",
53
57
  "ArtifactsSubNamespace",
@@ -0,0 +1,221 @@
1
+ """Coder namespace.
2
+
3
+ Lifecycle helpers for dominus-coder-runtime. This surface intentionally avoids
4
+ raw shell, filesystem browsing, and credential ingress helpers; Platform
5
+ decisions and Coder Runtime own executor policy and workspace operations.
6
+ """
7
+ from __future__ import annotations
8
+
9
+ from typing import Any, Dict, Optional, TYPE_CHECKING
10
+ from urllib.parse import quote, urlencode
11
+
12
+ if TYPE_CHECKING:
13
+ from ..start import Dominus
14
+
15
+
16
+ def _compact(payload: Dict[str, Any]) -> Dict[str, Any]:
17
+ out: Dict[str, Any] = {}
18
+ for key, value in payload.items():
19
+ if value is None:
20
+ continue
21
+ if isinstance(value, str) and value.strip() == "":
22
+ continue
23
+ out[key] = value
24
+ return out
25
+
26
+
27
+ def _query_string(params: Dict[str, Any]) -> str:
28
+ cleaned = _compact(params)
29
+ return f"?{urlencode(cleaned)}" if cleaned else ""
30
+
31
+
32
+ def _normalize_coder_path(path: str) -> str:
33
+ trimmed = path.strip()
34
+ normalized = trimmed if trimmed.startswith("/") else f"/{trimmed}"
35
+ if normalized == "/svc/coder" or normalized.startswith("/svc/coder/"):
36
+ return normalized
37
+ if normalized == "/api/coder" or normalized.startswith("/api/coder/"):
38
+ return normalized.replace("/api/", "/svc/", 1)
39
+ return f"/svc/coder{normalized}"
40
+
41
+
42
+ def _mutation_body(
43
+ *,
44
+ reason: Optional[str] = None,
45
+ idempotency_key: Optional[str] = None,
46
+ metadata: Optional[Dict[str, Any]] = None,
47
+ ) -> Dict[str, Any]:
48
+ return _compact(
49
+ {
50
+ "reason": reason,
51
+ "idempotency_key": idempotency_key,
52
+ "metadata": metadata,
53
+ }
54
+ )
55
+
56
+
57
+ class CoderNamespace:
58
+ """Coder run lifecycle helpers."""
59
+
60
+ def __init__(self, client: "Dominus"):
61
+ self._client = client
62
+
63
+ async def request(
64
+ self,
65
+ path: str,
66
+ *,
67
+ method: str = "GET",
68
+ body: Optional[Dict[str, Any]] = None,
69
+ headers: Optional[Dict[str, str]] = None,
70
+ timeout: float = 30.0,
71
+ ) -> Any:
72
+ return await self._client.gateway_fetch(
73
+ _normalize_coder_path(path),
74
+ method=method,
75
+ body=body,
76
+ headers=headers,
77
+ timeout=timeout,
78
+ )
79
+
80
+ async def health(self) -> Dict[str, Any]:
81
+ return await self.request("/health", method="GET")
82
+
83
+ async def ready(self) -> Dict[str, Any]:
84
+ return await self.request("/ready", method="GET")
85
+
86
+ async def ensure_run(
87
+ self,
88
+ *,
89
+ policy_decision_id: str,
90
+ idempotency_key: Optional[str] = None,
91
+ mode: Optional[str] = None,
92
+ task_recipe_ref: Optional[str] = None,
93
+ workflow_recipe_ref: Optional[str] = None,
94
+ pipeline_recipe_ref: Optional[str] = None,
95
+ group: Optional[str] = None,
96
+ repository: Optional[str] = None,
97
+ branch: Optional[str] = None,
98
+ base_branch: Optional[str] = None,
99
+ working_branch: Optional[str] = None,
100
+ title: Optional[str] = None,
101
+ instructions: Optional[str] = None,
102
+ inputs: Optional[Dict[str, Any]] = None,
103
+ metadata: Optional[Dict[str, Any]] = None,
104
+ ) -> Dict[str, Any]:
105
+ if not policy_decision_id or policy_decision_id.strip() == "":
106
+ raise ValueError("ensure_run requires policy_decision_id")
107
+
108
+ body = _compact(
109
+ {
110
+ "policy_decision_id": policy_decision_id,
111
+ "idempotency_key": idempotency_key,
112
+ "mode": mode,
113
+ "task_recipe_ref": task_recipe_ref,
114
+ "workflow_recipe_ref": workflow_recipe_ref,
115
+ "pipeline_recipe_ref": pipeline_recipe_ref,
116
+ "group": group,
117
+ "repository": repository,
118
+ "branch": branch,
119
+ "base_branch": base_branch,
120
+ "working_branch": working_branch,
121
+ "title": title,
122
+ "instructions": instructions,
123
+ "inputs": inputs,
124
+ "metadata": metadata,
125
+ }
126
+ )
127
+ return await self.request(
128
+ "/runs/ensure",
129
+ method="POST",
130
+ body=body,
131
+ timeout=600.0,
132
+ )
133
+
134
+ async def list_runs(
135
+ self,
136
+ *,
137
+ status: Optional[str] = None,
138
+ group: Optional[str] = None,
139
+ repository: Optional[str] = None,
140
+ limit: Optional[int] = None,
141
+ offset: Optional[int] = None,
142
+ ) -> Dict[str, Any]:
143
+ qs = _query_string(
144
+ {
145
+ "status": status,
146
+ "group": group,
147
+ "repository": repository,
148
+ "limit": limit,
149
+ "offset": offset,
150
+ }
151
+ )
152
+ return await self.request(f"/runs{qs}", method="GET")
153
+
154
+ async def get_run(self, run_id: str) -> Dict[str, Any]:
155
+ return await self.request(f"/runs/{quote(run_id, safe='')}", method="GET")
156
+
157
+ async def cancel_run(
158
+ self,
159
+ run_id: str,
160
+ *,
161
+ reason: Optional[str] = None,
162
+ idempotency_key: Optional[str] = None,
163
+ metadata: Optional[Dict[str, Any]] = None,
164
+ ) -> Dict[str, Any]:
165
+ return await self.request(
166
+ f"/runs/{quote(run_id, safe='')}/cancel",
167
+ method="POST",
168
+ body=_mutation_body(
169
+ reason=reason,
170
+ idempotency_key=idempotency_key,
171
+ metadata=metadata,
172
+ ),
173
+ )
174
+
175
+ async def retry_run(
176
+ self,
177
+ run_id: str,
178
+ *,
179
+ reason: Optional[str] = None,
180
+ idempotency_key: Optional[str] = None,
181
+ metadata: Optional[Dict[str, Any]] = None,
182
+ ) -> Dict[str, Any]:
183
+ return await self.request(
184
+ f"/runs/{quote(run_id, safe='')}/retry",
185
+ method="POST",
186
+ body=_mutation_body(
187
+ reason=reason,
188
+ idempotency_key=idempotency_key,
189
+ metadata=metadata,
190
+ ),
191
+ )
192
+
193
+ async def nudge_run(
194
+ self,
195
+ run_id: str,
196
+ *,
197
+ reason: Optional[str] = None,
198
+ idempotency_key: Optional[str] = None,
199
+ metadata: Optional[Dict[str, Any]] = None,
200
+ ) -> Dict[str, Any]:
201
+ return await self.request(
202
+ f"/runs/{quote(run_id, safe='')}/nudge",
203
+ method="POST",
204
+ body=_mutation_body(
205
+ reason=reason,
206
+ idempotency_key=idempotency_key,
207
+ metadata=metadata,
208
+ ),
209
+ )
210
+
211
+ async def get_run_summary(self, run_id: str) -> Dict[str, Any]:
212
+ return await self.request(
213
+ f"/runs/{quote(run_id, safe='')}/summary",
214
+ method="GET",
215
+ )
216
+
217
+ async def get_run_artifacts(self, run_id: str) -> Dict[str, Any]:
218
+ return await self.request(
219
+ f"/runs/{quote(run_id, safe='')}/artifacts",
220
+ method="GET",
221
+ )
@@ -0,0 +1,130 @@
1
+ """Platform namespace.
2
+
3
+ Thin JSON-first helpers for the dominus-platform-worker Gateway surface.
4
+ Platform owns group/repository policy decisions; it does not execute coder work
5
+ or expose credentials.
6
+ """
7
+ from __future__ import annotations
8
+
9
+ from typing import Any, Dict, Optional, TYPE_CHECKING
10
+ from urllib.parse import quote
11
+
12
+ if TYPE_CHECKING:
13
+ from ..start import Dominus
14
+
15
+
16
+ def _normalize_platform_path(path: str) -> str:
17
+ trimmed = path.strip()
18
+ normalized = trimmed if trimmed.startswith("/") else f"/{trimmed}"
19
+ if normalized == "/svc/platform" or normalized.startswith("/svc/platform/"):
20
+ return normalized
21
+ if normalized == "/api/platform" or normalized.startswith("/api/platform/"):
22
+ return normalized.replace("/api/", "/svc/", 1)
23
+ return f"/svc/platform{normalized}"
24
+
25
+
26
+ class PlatformNamespace:
27
+ """Platform group/repository policy helpers."""
28
+
29
+ def __init__(self, client: "Dominus"):
30
+ self._client = client
31
+
32
+ async def request(
33
+ self,
34
+ path: str,
35
+ *,
36
+ method: str = "GET",
37
+ body: Optional[Dict[str, Any]] = None,
38
+ headers: Optional[Dict[str, str]] = None,
39
+ timeout: float = 30.0,
40
+ ) -> Any:
41
+ return await self._client.gateway_fetch(
42
+ _normalize_platform_path(path),
43
+ method=method,
44
+ body=body,
45
+ headers=headers,
46
+ timeout=timeout,
47
+ )
48
+
49
+ async def health(self) -> Dict[str, Any]:
50
+ return await self.request("/health", method="GET")
51
+
52
+ async def ready(self) -> Dict[str, Any]:
53
+ return await self.request("/ready", method="GET")
54
+
55
+ async def list_groups(self) -> Dict[str, Any]:
56
+ return await self.request("/groups", method="GET")
57
+
58
+ async def get_group(self, group_slug: str) -> Dict[str, Any]:
59
+ return await self.request(f"/groups/{quote(group_slug, safe='')}", method="GET")
60
+
61
+ async def upsert_group(self, group: Dict[str, Any]) -> Dict[str, Any]:
62
+ return await self.request("/groups", method="POST", body=group)
63
+
64
+ async def update_group(self, group_slug: str, group: Dict[str, Any]) -> Dict[str, Any]:
65
+ return await self.request(
66
+ f"/groups/{quote(group_slug, safe='')}",
67
+ method="PATCH",
68
+ body=group,
69
+ )
70
+
71
+ async def add_group_member(self, group_slug: str, subject_id: str) -> Dict[str, Any]:
72
+ return await self.request(
73
+ f"/groups/{quote(group_slug, safe='')}/members",
74
+ method="POST",
75
+ body={"subject_id": subject_id},
76
+ )
77
+
78
+ async def link_group_repository(self, group_slug: str, repository: str) -> Dict[str, Any]:
79
+ return await self.request(
80
+ f"/groups/{quote(group_slug, safe='')}/repositories",
81
+ method="POST",
82
+ body={"repository": repository},
83
+ )
84
+
85
+ async def list_repositories(self) -> Dict[str, Any]:
86
+ return await self.request("/repositories", method="GET")
87
+
88
+ async def get_repository(self, repository_id: str) -> Dict[str, Any]:
89
+ return await self.request(
90
+ f"/repositories/{quote(repository_id, safe='')}",
91
+ method="GET",
92
+ )
93
+
94
+ async def upsert_repository(self, repository: Dict[str, Any]) -> Dict[str, Any]:
95
+ return await self.request("/repositories", method="POST", body=repository)
96
+
97
+ async def update_repository(
98
+ self,
99
+ repository_id: str,
100
+ repository: Dict[str, Any],
101
+ ) -> Dict[str, Any]:
102
+ return await self.request(
103
+ f"/repositories/{quote(repository_id, safe='')}",
104
+ method="PATCH",
105
+ body=repository,
106
+ )
107
+
108
+ async def ensure_policy_decision(self, options: Dict[str, Any]) -> Dict[str, Any]:
109
+ return await self.request(
110
+ "/policy/decisions/ensure",
111
+ method="POST",
112
+ body=options,
113
+ )
114
+
115
+ async def get_policy_decision(self, decision_id: str) -> Dict[str, Any]:
116
+ return await self.request(
117
+ f"/policy/decisions/{quote(decision_id, safe='')}",
118
+ method="GET",
119
+ )
120
+
121
+ async def rehydrate_policy_decision(
122
+ self,
123
+ decision_id: str,
124
+ options: Optional[Dict[str, Any]] = None,
125
+ ) -> Dict[str, Any]:
126
+ return await self.request(
127
+ f"/policy/decisions/{quote(decision_id, safe='')}/rehydrate",
128
+ method="POST",
129
+ body=options or {},
130
+ )
@@ -179,8 +179,12 @@ class Dominus:
179
179
  # Thin operator control-plane namespaces (Mothership / MCP parity)
180
180
  from .namespaces.deployer import DeployerNamespace
181
181
  from .namespaces.warden import WardenNamespace
182
+ from .namespaces.platform import PlatformNamespace
183
+ from .namespaces.coder import CoderNamespace
182
184
  self.deployer = DeployerNamespace(self)
183
185
  self.warden = WardenNamespace(self)
186
+ self.platform = PlatformNamespace(self)
187
+ self.coder = CoderNamespace(self)
184
188
 
185
189
  # Stash worker (per-scope durable items: credentials + configs)
186
190
  from .namespaces.stash import StashNamespace
@@ -1,15 +1,14 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dominus-sdk-python
3
- Version: 6.0.0
3
+ Version: 6.0.1
4
4
  Summary: Python SDK for the Dominus gateway-first platform
5
5
  Author-email: CareBridge Systems <dev@carebridge.io>
6
- License: Proprietary
6
+ License-Expression: LicenseRef-Proprietary
7
7
  Project-URL: Homepage, https://github.com/carebridgesystems/dominus-sdk-python
8
8
  Project-URL: Repository, https://github.com/carebridgesystems/dominus-sdk-python
9
9
  Keywords: dominus,carebridge,sdk,gateway,api,async
10
10
  Classifier: Development Status :: 4 - Beta
11
11
  Classifier: Intended Audience :: Developers
12
- Classifier: License :: Other/Proprietary License
13
12
  Classifier: Operating System :: OS Independent
14
13
  Classifier: Programming Language :: Python :: 3
15
14
  Classifier: Programming Language :: Python :: 3.9
@@ -20,6 +20,7 @@ dominus/namespaces/artifacts.py
20
20
  dominus/namespaces/auth.py
21
21
  dominus/namespaces/authority.py
22
22
  dominus/namespaces/browser.py
23
+ dominus/namespaces/coder.py
23
24
  dominus/namespaces/courier.py
24
25
  dominus/namespaces/db.py
25
26
  dominus/namespaces/ddl.py
@@ -29,6 +30,7 @@ dominus/namespaces/files.py
29
30
  dominus/namespaces/health.py
30
31
  dominus/namespaces/jobs.py
31
32
  dominus/namespaces/logs.py
33
+ dominus/namespaces/platform.py
32
34
  dominus/namespaces/portal.py
33
35
  dominus/namespaces/processor.py
34
36
  dominus/namespaces/recipes.py
@@ -53,6 +55,7 @@ tests/test_errors.py
53
55
  tests/test_flat_commands.py
54
56
  tests/test_health.py
55
57
  tests/test_logs.py
58
+ tests/test_platform_coder_namespaces.py
56
59
  tests/test_provisioning_parity.py
57
60
  tests/test_public_exports.py
58
61
  tests/test_recipes_namespace.py
@@ -1,13 +1,13 @@
1
1
  [build-system]
2
- requires = ["setuptools>=61.0", "wheel"]
2
+ requires = ["setuptools>=77.0", "wheel"]
3
3
  build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "dominus-sdk-python"
7
- version = "6.0.0"
7
+ version = "6.0.1"
8
8
  description = "Python SDK for the Dominus gateway-first platform"
9
9
  readme = "README.md"
10
- license = {text = "Proprietary"}
10
+ license = "LicenseRef-Proprietary"
11
11
  requires-python = ">=3.9"
12
12
  authors = [
13
13
  {name = "CareBridge Systems", email = "dev@carebridge.io"}
@@ -16,7 +16,6 @@ keywords = ["dominus", "carebridge", "sdk", "gateway", "api", "async"]
16
16
  classifiers = [
17
17
  "Development Status :: 4 - Beta",
18
18
  "Intended Audience :: Developers",
19
- "License :: Other/Proprietary License",
20
19
  "Operating System :: OS Independent",
21
20
  "Programming Language :: Python :: 3",
22
21
  "Programming Language :: Python :: 3.9",
@@ -0,0 +1,118 @@
1
+ import pytest
2
+
3
+ from dominus.namespaces.coder import CoderNamespace
4
+ from dominus.namespaces.platform import PlatformNamespace
5
+
6
+
7
+ class MockGatewayClient:
8
+ def __init__(self):
9
+ self.calls = []
10
+
11
+ async def gateway_fetch(self, path, **kwargs):
12
+ self.calls.append((path, kwargs))
13
+ return {"ok": True}
14
+
15
+
16
+ @pytest.mark.asyncio
17
+ async def test_platform_namespace_normalizes_helpers_onto_service_routes():
18
+ client = MockGatewayClient()
19
+ platform = PlatformNamespace(client)
20
+
21
+ await platform.health()
22
+ await platform.list_groups()
23
+ await platform.upsert_group({"slug": "dominus", "name": "Dominus"})
24
+ await platform.add_group_member("dominus", "user-1")
25
+ await platform.link_group_repository(
26
+ "dominus",
27
+ "carebridgesystems/dominus-platform-worker",
28
+ )
29
+ await platform.ensure_policy_decision(
30
+ {
31
+ "group": "dominus",
32
+ "repository": "carebridgesystems/dominus-platform-worker",
33
+ "metadata": {"safe": "ok"},
34
+ }
35
+ )
36
+ await platform.rehydrate_policy_decision("pd_123", {"run_id": "run_123"})
37
+
38
+ assert [(path, kwargs["method"]) for path, kwargs in client.calls] == [
39
+ ("/svc/platform/health", "GET"),
40
+ ("/svc/platform/groups", "GET"),
41
+ ("/svc/platform/groups", "POST"),
42
+ ("/svc/platform/groups/dominus/members", "POST"),
43
+ ("/svc/platform/groups/dominus/repositories", "POST"),
44
+ ("/svc/platform/policy/decisions/ensure", "POST"),
45
+ ("/svc/platform/policy/decisions/pd_123/rehydrate", "POST"),
46
+ ]
47
+ assert client.calls[2][1]["body"] == {"slug": "dominus", "name": "Dominus"}
48
+ assert client.calls[3][1]["body"] == {"subject_id": "user-1"}
49
+ assert client.calls[5][1]["body"] == {
50
+ "group": "dominus",
51
+ "repository": "carebridgesystems/dominus-platform-worker",
52
+ "metadata": {"safe": "ok"},
53
+ }
54
+
55
+
56
+ @pytest.mark.asyncio
57
+ async def test_platform_request_preserves_explicit_platform_paths():
58
+ client = MockGatewayClient()
59
+ platform = PlatformNamespace(client)
60
+
61
+ await platform.request("/api/platform/groups", method="GET")
62
+ await platform.request("/svc/platform/policy/decisions/pd_123", method="GET")
63
+
64
+ assert [path for path, _ in client.calls] == [
65
+ "/svc/platform/groups",
66
+ "/svc/platform/policy/decisions/pd_123",
67
+ ]
68
+
69
+
70
+ @pytest.mark.asyncio
71
+ async def test_coder_namespace_exposes_lifecycle_helpers_but_no_raw_shell():
72
+ client = MockGatewayClient()
73
+ coder = CoderNamespace(client)
74
+
75
+ await coder.ensure_run(
76
+ policy_decision_id="pd_123",
77
+ mode="async",
78
+ task_recipe_ref="recipe://coder-task-recipe-v1/fix-tests@head",
79
+ repository="carebridgesystems/dominus-platform-worker",
80
+ instructions="Fix failing tests",
81
+ )
82
+ await coder.list_runs(status="running", limit=10)
83
+ await coder.cancel_run("run_123", reason="operator requested")
84
+ await coder.retry_run("run_123", idempotency_key="retry-1")
85
+ await coder.nudge_run("run_123", metadata={"hint": "continue"})
86
+ await coder.get_run_summary("run_123")
87
+ await coder.get_run_artifacts("run_123")
88
+
89
+ assert [(path, kwargs["method"]) for path, kwargs in client.calls] == [
90
+ ("/svc/coder/runs/ensure", "POST"),
91
+ ("/svc/coder/runs?status=running&limit=10", "GET"),
92
+ ("/svc/coder/runs/run_123/cancel", "POST"),
93
+ ("/svc/coder/runs/run_123/retry", "POST"),
94
+ ("/svc/coder/runs/run_123/nudge", "POST"),
95
+ ("/svc/coder/runs/run_123/summary", "GET"),
96
+ ("/svc/coder/runs/run_123/artifacts", "GET"),
97
+ ]
98
+ assert client.calls[0][1]["body"] == {
99
+ "policy_decision_id": "pd_123",
100
+ "mode": "async",
101
+ "task_recipe_ref": "recipe://coder-task-recipe-v1/fix-tests@head",
102
+ "repository": "carebridgesystems/dominus-platform-worker",
103
+ "instructions": "Fix failing tests",
104
+ }
105
+ assert not hasattr(coder, "shell")
106
+ assert not hasattr(coder, "exec")
107
+ assert not hasattr(coder, "command")
108
+
109
+
110
+ @pytest.mark.asyncio
111
+ async def test_coder_ensure_run_requires_policy_decision_id():
112
+ client = MockGatewayClient()
113
+ coder = CoderNamespace(client)
114
+
115
+ with pytest.raises(ValueError, match="policy_decision_id"):
116
+ await coder.ensure_run(policy_decision_id="")
117
+
118
+ assert client.calls == []
@@ -1,6 +1,8 @@
1
1
  from dominus import (
2
2
  BrowserNamespace,
3
+ CoderNamespace,
3
4
  DeployerNamespace,
5
+ PlatformNamespace,
4
6
  RecipesNamespace,
5
7
  WardenNamespace,
6
8
  gateway_circuit_breaker,
@@ -33,6 +35,8 @@ def test_top_level_exports_drop_delegate_alias():
33
35
  assert WardenNamespace is not None
34
36
  assert BrowserNamespace is not None
35
37
  assert RecipesNamespace is not None
38
+ assert PlatformNamespace is not None
39
+ assert CoderNamespace is not None
36
40
 
37
41
 
38
42
  def test_singleton_exposes_browser_namespace():
@@ -40,3 +44,5 @@ def test_singleton_exposes_browser_namespace():
40
44
 
41
45
  assert callable(dominus.browser.get_health)
42
46
  assert callable(dominus.recipes.list_types)
47
+ assert callable(dominus.platform.ensure_policy_decision)
48
+ assert callable(dominus.coder.ensure_run)