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.
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/PKG-INFO +2 -3
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/__init__.py +5 -1
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/namespaces/__init__.py +4 -0
- dominus_sdk_python-6.0.1/dominus/namespaces/coder.py +221 -0
- dominus_sdk_python-6.0.1/dominus/namespaces/platform.py +130 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/start.py +4 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus_sdk_python.egg-info/PKG-INFO +2 -3
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus_sdk_python.egg-info/SOURCES.txt +3 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/pyproject.toml +3 -4
- dominus_sdk_python-6.0.1/tests/test_platform_coder_namespaces.py +118 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/tests/test_public_exports.py +6 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/README.md +0 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/config/__init__.py +0 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/config/endpoints.py +0 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/errors.py +0 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/helpers/__init__.py +0 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/helpers/auth.py +0 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/helpers/cache.py +0 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/helpers/console_capture.py +0 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/helpers/core.py +0 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/helpers/crypto.py +0 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/helpers/sse.py +0 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/helpers/trace.py +0 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/namespaces/admin.py +0 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/namespaces/ai.py +0 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/namespaces/artifacts.py +0 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/namespaces/auth.py +0 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/namespaces/authority.py +0 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/namespaces/browser.py +0 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/namespaces/courier.py +0 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/namespaces/db.py +0 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/namespaces/ddl.py +0 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/namespaces/deployer.py +0 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/namespaces/fastapi.py +0 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/namespaces/files.py +0 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/namespaces/health.py +0 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/namespaces/jobs.py +0 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/namespaces/logs.py +0 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/namespaces/portal.py +0 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/namespaces/processor.py +0 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/namespaces/recipes.py +0 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/namespaces/redis.py +0 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/namespaces/secrets.py +0 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/namespaces/secure.py +0 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/namespaces/stash.py +0 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/namespaces/sync.py +0 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/namespaces/warden.py +0 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/namespaces/workflow.py +0 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus/services/__init__.py +0 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus_sdk_python.egg-info/dependency_links.txt +0 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus_sdk_python.egg-info/requires.txt +0 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus_sdk_python.egg-info/top_level.txt +0 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/setup.cfg +0 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/tests/test_auth.py +0 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/tests/test_authority_public_vocabulary.py +0 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/tests/test_browser_namespace.py +0 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/tests/test_control_plane_namespaces.py +0 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/tests/test_errors.py +0 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/tests/test_flat_commands.py +0 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/tests/test_health.py +0 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/tests/test_logs.py +0 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/tests/test_provisioning_parity.py +0 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/tests/test_recipes_namespace.py +0 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/tests/test_transport_compat.py +0 -0
- {dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/tests/test_workflow_lifecycle.py +0 -0
- {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.
|
|
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.
|
|
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.
|
|
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
|
{dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus_sdk_python.egg-info/SOURCES.txt
RENAMED
|
@@ -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>=
|
|
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.
|
|
7
|
+
version = "6.0.1"
|
|
8
8
|
description = "Python SDK for the Dominus gateway-first platform"
|
|
9
9
|
readme = "README.md"
|
|
10
|
-
license =
|
|
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)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus_sdk_python.egg-info/requires.txt
RENAMED
|
File without changes
|
{dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/dominus_sdk_python.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/tests/test_authority_public_vocabulary.py
RENAMED
|
File without changes
|
|
File without changes
|
{dominus_sdk_python-6.0.0 → dominus_sdk_python-6.0.1}/tests/test_control_plane_namespaces.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|