dominus-sdk-python 9.0.2__tar.gz → 9.0.4__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-9.0.2 → dominus_sdk_python-9.0.4}/PKG-INFO +3 -20
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/README.md +2 -19
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/dominus/__init__.py +1 -1
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/dominus/errors.py +227 -58
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/dominus/helpers/auth.py +1 -1
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/dominus_sdk_python.egg-info/PKG-INFO +3 -20
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/dominus_sdk_python.egg-info/SOURCES.txt +1 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/pyproject.toml +1 -1
- dominus_sdk_python-9.0.4/tests/test_error_recovery_action.py +31 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/tests/test_readme_quickstart_catalog.py +45 -27
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/dominus/config/__init__.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/dominus/config/endpoints.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/dominus/helpers/__init__.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/dominus/helpers/cache.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/dominus/helpers/console_capture.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/dominus/helpers/core.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/dominus/helpers/crypto.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/dominus/helpers/sse.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/dominus/helpers/trace.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/dominus/namespaces/__init__.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/dominus/namespaces/admin.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/dominus/namespaces/ai.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/dominus/namespaces/artifacts.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/dominus/namespaces/auth.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/dominus/namespaces/authority.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/dominus/namespaces/browser.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/dominus/namespaces/coder.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/dominus/namespaces/courier.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/dominus/namespaces/db.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/dominus/namespaces/ddl.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/dominus/namespaces/deployer.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/dominus/namespaces/fastapi.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/dominus/namespaces/files.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/dominus/namespaces/health.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/dominus/namespaces/jobs.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/dominus/namespaces/logs.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/dominus/namespaces/platform.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/dominus/namespaces/portal.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/dominus/namespaces/processor.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/dominus/namespaces/publisher.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/dominus/namespaces/recipes.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/dominus/namespaces/redis.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/dominus/namespaces/secrets.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/dominus/namespaces/secure.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/dominus/namespaces/stash.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/dominus/namespaces/sync.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/dominus/namespaces/warden.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/dominus/namespaces/workflow.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/dominus/py.typed +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/dominus/services/__init__.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/dominus/start.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/dominus_sdk_python.egg-info/dependency_links.txt +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/dominus_sdk_python.egg-info/requires.txt +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/dominus_sdk_python.egg-info/top_level.txt +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/setup.cfg +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/tests/test_auth.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/tests/test_authority_public_vocabulary.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/tests/test_browser_namespace.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/tests/test_control_plane_namespaces.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/tests/test_errors.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/tests/test_flat_commands.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/tests/test_health.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/tests/test_logs.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/tests/test_namespace_barrel_exports.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/tests/test_platform_coder_namespaces.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/tests/test_provisioning_parity.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/tests/test_public_exports.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/tests/test_publisher_namespace.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/tests/test_recipes_namespace.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/tests/test_recipes_stash_routing.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/tests/test_secrets_presence_contract.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/tests/test_stash_artifact_facade.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/tests/test_stash_tables_namespace.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/tests/test_transport_compat.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/tests/test_workflow_lifecycle.py +0 -0
- {dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/tests/test_workflow_refs.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dominus-sdk-python
|
|
3
|
-
Version: 9.0.
|
|
3
|
+
Version: 9.0.4
|
|
4
4
|
Summary: Python SDK for the Dominus gateway-first platform
|
|
5
5
|
Author-email: CareBridge Systems <dev@carebridge.io>
|
|
6
6
|
License-Expression: LicenseRef-Proprietary
|
|
@@ -48,7 +48,7 @@ cleanup audit is `docs/janitor/2026-08-04-sdk-python-cleanup-audit.md`.
|
|
|
48
48
|
- Gateway-scoped client mode for MCP and other user-JWT sessions
|
|
49
49
|
- Transport compatibility for wrapped `{success,data}` responses and unwrapped Warden/control-plane success objects
|
|
50
50
|
- Local helpers for JWT verification, trace propagation, retries, and console capture
|
|
51
|
-
- Current package version: `9.0.
|
|
51
|
+
- Current package version: `9.0.4`
|
|
52
52
|
|
|
53
53
|
## Install
|
|
54
54
|
|
|
@@ -67,13 +67,12 @@ for operators and advanced use; this path is the stable teaching surface.
|
|
|
67
67
|
Set both values before the Python process imports the SDK singleton:
|
|
68
68
|
|
|
69
69
|
```bash
|
|
70
|
-
export
|
|
70
|
+
export DOMINUS_USER_TOKEN="your-user-token"
|
|
71
71
|
export DOMINUS_PROJECT="your-project-slug"
|
|
72
72
|
```
|
|
73
73
|
|
|
74
74
|
```python
|
|
75
75
|
import asyncio
|
|
76
|
-
import base64
|
|
77
76
|
|
|
78
77
|
from dominus import dominus
|
|
79
78
|
|
|
@@ -83,22 +82,6 @@ async def main() -> None:
|
|
|
83
82
|
workflow_recipe_ref="recipe://workflow-recipe-v1/hello@v1",
|
|
84
83
|
)
|
|
85
84
|
|
|
86
|
-
await dominus.artifacts.store_v2(
|
|
87
|
-
group="your-group",
|
|
88
|
-
owner="your-project-slug",
|
|
89
|
-
environment="production",
|
|
90
|
-
kind="blob",
|
|
91
|
-
artifact_key="hello",
|
|
92
|
-
data=base64.b64encode(b"hello dominus").decode("ascii"),
|
|
93
|
-
)
|
|
94
|
-
artifact = await dominus.artifacts.retrieve_v2(
|
|
95
|
-
group="your-group",
|
|
96
|
-
owner="your-project-slug",
|
|
97
|
-
environment="production",
|
|
98
|
-
kind="blob",
|
|
99
|
-
artifact_key="hello",
|
|
100
|
-
)
|
|
101
|
-
|
|
102
85
|
stash_result = await dominus.stash.upsert(
|
|
103
86
|
kind="config",
|
|
104
87
|
scope={"env": "production"},
|
|
@@ -15,7 +15,7 @@ cleanup audit is `docs/janitor/2026-08-04-sdk-python-cleanup-audit.md`.
|
|
|
15
15
|
- Gateway-scoped client mode for MCP and other user-JWT sessions
|
|
16
16
|
- Transport compatibility for wrapped `{success,data}` responses and unwrapped Warden/control-plane success objects
|
|
17
17
|
- Local helpers for JWT verification, trace propagation, retries, and console capture
|
|
18
|
-
- Current package version: `9.0.
|
|
18
|
+
- Current package version: `9.0.4`
|
|
19
19
|
|
|
20
20
|
## Install
|
|
21
21
|
|
|
@@ -34,13 +34,12 @@ for operators and advanced use; this path is the stable teaching surface.
|
|
|
34
34
|
Set both values before the Python process imports the SDK singleton:
|
|
35
35
|
|
|
36
36
|
```bash
|
|
37
|
-
export
|
|
37
|
+
export DOMINUS_USER_TOKEN="your-user-token"
|
|
38
38
|
export DOMINUS_PROJECT="your-project-slug"
|
|
39
39
|
```
|
|
40
40
|
|
|
41
41
|
```python
|
|
42
42
|
import asyncio
|
|
43
|
-
import base64
|
|
44
43
|
|
|
45
44
|
from dominus import dominus
|
|
46
45
|
|
|
@@ -50,22 +49,6 @@ async def main() -> None:
|
|
|
50
49
|
workflow_recipe_ref="recipe://workflow-recipe-v1/hello@v1",
|
|
51
50
|
)
|
|
52
51
|
|
|
53
|
-
await dominus.artifacts.store_v2(
|
|
54
|
-
group="your-group",
|
|
55
|
-
owner="your-project-slug",
|
|
56
|
-
environment="production",
|
|
57
|
-
kind="blob",
|
|
58
|
-
artifact_key="hello",
|
|
59
|
-
data=base64.b64encode(b"hello dominus").decode("ascii"),
|
|
60
|
-
)
|
|
61
|
-
artifact = await dominus.artifacts.retrieve_v2(
|
|
62
|
-
group="your-group",
|
|
63
|
-
owner="your-project-slug",
|
|
64
|
-
environment="production",
|
|
65
|
-
kind="blob",
|
|
66
|
-
artifact_key="hello",
|
|
67
|
-
)
|
|
68
|
-
|
|
69
52
|
stash_result = await dominus.stash.upsert(
|
|
70
53
|
kind="config",
|
|
71
54
|
scope={"env": "production"},
|
|
@@ -4,6 +4,45 @@ Dominus SDK Error Classes
|
|
|
4
4
|
Custom exceptions for the Dominus SDK with structured error information.
|
|
5
5
|
"""
|
|
6
6
|
from typing import Any, Dict, Optional
|
|
7
|
+
from uuid import uuid4
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
CANONICAL_ERROR_KINDS = (
|
|
11
|
+
"unauthorized",
|
|
12
|
+
"forbidden",
|
|
13
|
+
"not_found",
|
|
14
|
+
"validation_failed",
|
|
15
|
+
"conflict",
|
|
16
|
+
"rate_limited",
|
|
17
|
+
"upstream_unavailable",
|
|
18
|
+
"internal",
|
|
19
|
+
"timeout",
|
|
20
|
+
"canceled",
|
|
21
|
+
"unsupported_version",
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
RECOVERY_ACTIONS = ("retry", "reauth", "escalate", "fix_config")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def recovery_action_for_kind(kind: str, retryable: Optional[bool] = None) -> str:
|
|
28
|
+
if kind == "unauthorized":
|
|
29
|
+
return "reauth"
|
|
30
|
+
if kind in {"rate_limited", "timeout"}:
|
|
31
|
+
return "retry"
|
|
32
|
+
if kind == "upstream_unavailable":
|
|
33
|
+
return "retry" if retryable is not False else "escalate"
|
|
34
|
+
if kind == "internal":
|
|
35
|
+
return "escalate"
|
|
36
|
+
if kind in {
|
|
37
|
+
"validation_failed",
|
|
38
|
+
"unsupported_version",
|
|
39
|
+
"not_found",
|
|
40
|
+
"forbidden",
|
|
41
|
+
"conflict",
|
|
42
|
+
"canceled",
|
|
43
|
+
}:
|
|
44
|
+
return "fix_config"
|
|
45
|
+
return "escalate"
|
|
7
46
|
|
|
8
47
|
|
|
9
48
|
def _first_string(*values: Any) -> Optional[str]:
|
|
@@ -59,6 +98,38 @@ def _normalize_kernel_details(
|
|
|
59
98
|
return normalized
|
|
60
99
|
|
|
61
100
|
|
|
101
|
+
def _kind_from_details(status_code: Optional[int], details: Dict[str, Any]) -> str:
|
|
102
|
+
explicit = _first_string(details.get("kind"))
|
|
103
|
+
if explicit in CANONICAL_ERROR_KINDS:
|
|
104
|
+
return explicit
|
|
105
|
+
code = (_first_string(details.get("code")) or "").lower()
|
|
106
|
+
if "unsupported_version" in code:
|
|
107
|
+
return "unsupported_version"
|
|
108
|
+
if "timeout" in code or status_code in {408, 504}:
|
|
109
|
+
return "timeout"
|
|
110
|
+
if "cancel" in code or status_code == 499:
|
|
111
|
+
return "canceled"
|
|
112
|
+
if status_code == 401:
|
|
113
|
+
return "unauthorized"
|
|
114
|
+
if status_code == 403:
|
|
115
|
+
return "forbidden"
|
|
116
|
+
if status_code == 404:
|
|
117
|
+
return "not_found"
|
|
118
|
+
if status_code in {400, 422}:
|
|
119
|
+
return "validation_failed"
|
|
120
|
+
if status_code == 409:
|
|
121
|
+
return "conflict"
|
|
122
|
+
if status_code == 429:
|
|
123
|
+
return "rate_limited"
|
|
124
|
+
if status_code and status_code >= 500:
|
|
125
|
+
return "upstream_unavailable"
|
|
126
|
+
return "internal"
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _default_retryable(kind: str) -> bool:
|
|
130
|
+
return kind in {"rate_limited", "upstream_unavailable", "timeout"}
|
|
131
|
+
|
|
132
|
+
|
|
62
133
|
class DominusError(Exception):
|
|
63
134
|
"""
|
|
64
135
|
Base exception for all Dominus SDK errors.
|
|
@@ -70,17 +141,21 @@ class DominusError(Exception):
|
|
|
70
141
|
message: Human-readable error message
|
|
71
142
|
status_code: HTTP status code (if applicable)
|
|
72
143
|
code: Stable machine-readable error code
|
|
144
|
+
kind: Canonical error kind from the closed-sum taxonomy (errors.spec.json)
|
|
73
145
|
category: Error category from the kernel contract
|
|
74
146
|
details: Additional error details from backend
|
|
75
147
|
endpoint: The endpoint that was called (if applicable)
|
|
76
148
|
"""
|
|
77
149
|
|
|
150
|
+
kind: str = "internal"
|
|
151
|
+
|
|
78
152
|
def __init__(
|
|
79
153
|
self,
|
|
80
154
|
message: str,
|
|
81
155
|
status_code: Optional[int] = None,
|
|
82
156
|
details: Optional[Dict[str, Any]] = None,
|
|
83
|
-
endpoint: Optional[str] = None
|
|
157
|
+
endpoint: Optional[str] = None,
|
|
158
|
+
kind: Optional[str] = None,
|
|
84
159
|
):
|
|
85
160
|
self.message = message
|
|
86
161
|
self.status_code = status_code
|
|
@@ -91,6 +166,11 @@ class DominusError(Exception):
|
|
|
91
166
|
self.trace_id = _first_string(self.details.get("trace_id"), self.details.get("traceId"))
|
|
92
167
|
self.request_id = _first_string(self.details.get("request_id"), self.details.get("requestId"))
|
|
93
168
|
self.retryable = _first_bool(self.details.get("retryable"))
|
|
169
|
+
self.kind = kind or _kind_from_details(status_code, self.details)
|
|
170
|
+
self.correlation_id = _first_string(self.details.get("correlation_id"), self.details.get("correlationId"))
|
|
171
|
+
self.cause = self.details.get("cause")
|
|
172
|
+
self.recovery_action = recovery_action_for_kind(self.kind, self.retryable)
|
|
173
|
+
self.details.setdefault("recovery_action", self.recovery_action)
|
|
94
174
|
super().__init__(self.message)
|
|
95
175
|
|
|
96
176
|
def __str__(self) -> str:
|
|
@@ -107,6 +187,7 @@ class DominusError(Exception):
|
|
|
107
187
|
return (
|
|
108
188
|
f"DominusError(message={self.message!r}, "
|
|
109
189
|
f"status_code={self.status_code}, "
|
|
190
|
+
f"kind={self.kind!r}, "
|
|
110
191
|
f"code={self.code!r}, "
|
|
111
192
|
f"category={self.category!r}, "
|
|
112
193
|
f"details={self.details!r}, "
|
|
@@ -114,21 +195,40 @@ class DominusError(Exception):
|
|
|
114
195
|
)
|
|
115
196
|
|
|
116
197
|
def to_dict(self) -> Dict[str, Any]:
|
|
198
|
+
request_id = self.request_id or str(uuid4())
|
|
199
|
+
self.request_id = request_id
|
|
200
|
+
self.details.setdefault("request_id", request_id)
|
|
201
|
+
http_status = self.status_code or 500
|
|
202
|
+
retryable = self.retryable if self.retryable is not None else _default_retryable(self.kind)
|
|
117
203
|
return {
|
|
118
204
|
"message": self.message,
|
|
119
205
|
"status_code": self.status_code,
|
|
120
206
|
"code": self.code,
|
|
207
|
+
"kind": self.kind,
|
|
121
208
|
"category": self.category,
|
|
122
209
|
"details": self.details,
|
|
123
210
|
"endpoint": self.endpoint,
|
|
124
211
|
"trace_id": self.trace_id,
|
|
125
|
-
"request_id":
|
|
126
|
-
"
|
|
212
|
+
"request_id": request_id,
|
|
213
|
+
"correlation_id": self.correlation_id,
|
|
214
|
+
"retryable": retryable,
|
|
215
|
+
"recovery_action": self.recovery_action,
|
|
216
|
+
"http_status": http_status,
|
|
217
|
+
"envelope_version": "1",
|
|
218
|
+
**({"cause": self.cause} if self.cause is not None else {}),
|
|
127
219
|
}
|
|
128
220
|
|
|
129
221
|
|
|
130
|
-
|
|
222
|
+
# ---------------------------------------------------------------------------
|
|
223
|
+
# Per-kind canonical subclasses (Seams fork 6, ADR 0010).
|
|
224
|
+
# Retired kind codes: dominus-cli/docs/deprecation-table.json (M-1).
|
|
225
|
+
# One derived class per canonical `kind` from errors.spec.json.
|
|
226
|
+
# Existing class names are preserved as aliases (v1); deprecated in v2.
|
|
227
|
+
# ---------------------------------------------------------------------------
|
|
228
|
+
|
|
229
|
+
class UnauthorizedError(DominusError):
|
|
131
230
|
"""Raised when authentication fails (invalid token, expired JWT, etc.)."""
|
|
231
|
+
kind = "unauthorized"
|
|
132
232
|
|
|
133
233
|
def __init__(
|
|
134
234
|
self,
|
|
@@ -137,11 +237,12 @@ class AuthenticationError(DominusError):
|
|
|
137
237
|
details: Optional[Dict[str, Any]] = None,
|
|
138
238
|
endpoint: Optional[str] = None
|
|
139
239
|
):
|
|
140
|
-
super().__init__(message, status_code, details, endpoint)
|
|
240
|
+
super().__init__(message, status_code, details, endpoint, kind="unauthorized")
|
|
141
241
|
|
|
142
242
|
|
|
143
|
-
class
|
|
243
|
+
class ForbiddenError(DominusError):
|
|
144
244
|
"""Raised when authorization fails (insufficient permissions)."""
|
|
245
|
+
kind = "forbidden"
|
|
145
246
|
|
|
146
247
|
def __init__(
|
|
147
248
|
self,
|
|
@@ -150,11 +251,12 @@ class AuthorizationError(DominusError):
|
|
|
150
251
|
details: Optional[Dict[str, Any]] = None,
|
|
151
252
|
endpoint: Optional[str] = None
|
|
152
253
|
):
|
|
153
|
-
super().__init__(message, status_code, details, endpoint)
|
|
254
|
+
super().__init__(message, status_code, details, endpoint, kind="forbidden")
|
|
154
255
|
|
|
155
256
|
|
|
156
257
|
class NotFoundError(DominusError):
|
|
157
258
|
"""Raised when a requested resource is not found."""
|
|
259
|
+
kind = "not_found"
|
|
158
260
|
|
|
159
261
|
def __init__(
|
|
160
262
|
self,
|
|
@@ -163,11 +265,12 @@ class NotFoundError(DominusError):
|
|
|
163
265
|
details: Optional[Dict[str, Any]] = None,
|
|
164
266
|
endpoint: Optional[str] = None
|
|
165
267
|
):
|
|
166
|
-
super().__init__(message, status_code, details, endpoint)
|
|
268
|
+
super().__init__(message, status_code, details, endpoint, kind="not_found")
|
|
167
269
|
|
|
168
270
|
|
|
169
|
-
class
|
|
271
|
+
class ValidationFailedError(DominusError):
|
|
170
272
|
"""Raised when request validation fails."""
|
|
273
|
+
kind = "validation_failed"
|
|
171
274
|
|
|
172
275
|
def __init__(
|
|
173
276
|
self,
|
|
@@ -176,11 +279,12 @@ class ValidationError(DominusError):
|
|
|
176
279
|
details: Optional[Dict[str, Any]] = None,
|
|
177
280
|
endpoint: Optional[str] = None
|
|
178
281
|
):
|
|
179
|
-
super().__init__(message, status_code, details, endpoint)
|
|
282
|
+
super().__init__(message, status_code, details, endpoint, kind="validation_failed")
|
|
180
283
|
|
|
181
284
|
|
|
182
285
|
class ConflictError(DominusError):
|
|
183
286
|
"""Raised when there's a conflict (duplicate key, version mismatch, etc.)."""
|
|
287
|
+
kind = "conflict"
|
|
184
288
|
|
|
185
289
|
def __init__(
|
|
186
290
|
self,
|
|
@@ -189,87 +293,134 @@ class ConflictError(DominusError):
|
|
|
189
293
|
details: Optional[Dict[str, Any]] = None,
|
|
190
294
|
endpoint: Optional[str] = None
|
|
191
295
|
):
|
|
192
|
-
super().__init__(message, status_code, details, endpoint)
|
|
296
|
+
super().__init__(message, status_code, details, endpoint, kind="conflict")
|
|
193
297
|
|
|
194
298
|
|
|
195
|
-
class
|
|
196
|
-
"""Raised when a
|
|
299
|
+
class RateLimitedError(DominusError):
|
|
300
|
+
"""Raised when a request is rate-limited (429)."""
|
|
301
|
+
kind = "rate_limited"
|
|
197
302
|
|
|
198
303
|
def __init__(
|
|
199
304
|
self,
|
|
200
|
-
message: str = "
|
|
201
|
-
status_code: int =
|
|
305
|
+
message: str = "Rate limit exceeded",
|
|
306
|
+
status_code: int = 429,
|
|
202
307
|
details: Optional[Dict[str, Any]] = None,
|
|
203
308
|
endpoint: Optional[str] = None
|
|
204
309
|
):
|
|
205
|
-
super().__init__(message, status_code, details, endpoint)
|
|
310
|
+
super().__init__(message, status_code, details, endpoint, kind="rate_limited")
|
|
206
311
|
|
|
207
312
|
|
|
208
|
-
class
|
|
209
|
-
"""Raised when a
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
class StashBookmarkInvalidVersionRef(ValidationError):
|
|
213
|
-
"""Raised when a stash artifact bookmark version_ref is invalid."""
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
class StashBookmarkDuplicateName(ConflictError):
|
|
217
|
-
"""Raised when a stash artifact bookmark name is already in use."""
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
class StashBookmarkUpstreamRejected(ServiceError):
|
|
221
|
-
"""Raised when the artifact backend rejects a stash bookmark operation."""
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
class StashWatchInvalidWebhookUrl(ValidationError):
|
|
225
|
-
"""Raised when a stash artifact watcher webhook_url is invalid."""
|
|
313
|
+
class UpstreamUnavailableError(DominusError):
|
|
314
|
+
"""Raised when a backend service is unavailable (5xx)."""
|
|
315
|
+
kind = "upstream_unavailable"
|
|
226
316
|
|
|
317
|
+
def __init__(
|
|
318
|
+
self,
|
|
319
|
+
message: str = "Service unavailable",
|
|
320
|
+
status_code: int = 500,
|
|
321
|
+
details: Optional[Dict[str, Any]] = None,
|
|
322
|
+
endpoint: Optional[str] = None
|
|
323
|
+
):
|
|
324
|
+
super().__init__(message, status_code, details, endpoint, kind="upstream_unavailable")
|
|
227
325
|
|
|
228
|
-
class StashWatchUpstreamRejected(ServiceError):
|
|
229
|
-
"""Raised when the artifact backend rejects a stash watch operation."""
|
|
230
326
|
|
|
327
|
+
class InternalError(DominusError):
|
|
328
|
+
"""Raised for unexpected internal errors."""
|
|
329
|
+
kind = "internal"
|
|
231
330
|
|
|
232
|
-
|
|
233
|
-
|
|
331
|
+
def __init__(
|
|
332
|
+
self,
|
|
333
|
+
message: str = "Internal error",
|
|
334
|
+
status_code: int = 500,
|
|
335
|
+
details: Optional[Dict[str, Any]] = None,
|
|
336
|
+
endpoint: Optional[str] = None
|
|
337
|
+
):
|
|
338
|
+
super().__init__(message, status_code, details, endpoint, kind="internal")
|
|
234
339
|
|
|
235
340
|
|
|
236
|
-
class
|
|
237
|
-
"""Raised when
|
|
341
|
+
class TimeoutError(DominusError):
|
|
342
|
+
"""Raised when a request times out."""
|
|
343
|
+
kind = "timeout"
|
|
238
344
|
|
|
239
345
|
def __init__(
|
|
240
346
|
self,
|
|
241
|
-
message: str = "
|
|
347
|
+
message: str = "Request timed out",
|
|
242
348
|
status_code: Optional[int] = None,
|
|
243
349
|
details: Optional[Dict[str, Any]] = None,
|
|
244
350
|
endpoint: Optional[str] = None
|
|
245
351
|
):
|
|
246
|
-
super().__init__(message, status_code, details, endpoint)
|
|
352
|
+
super().__init__(message, status_code, details, endpoint, kind="timeout")
|
|
247
353
|
|
|
248
354
|
|
|
249
|
-
class
|
|
250
|
-
"""Raised when
|
|
355
|
+
class CanceledError(DominusError):
|
|
356
|
+
"""Raised when an operation is canceled."""
|
|
357
|
+
kind = "canceled"
|
|
251
358
|
|
|
252
359
|
def __init__(
|
|
253
360
|
self,
|
|
254
|
-
message: str = "
|
|
361
|
+
message: str = "Operation canceled",
|
|
255
362
|
status_code: Optional[int] = None,
|
|
256
363
|
details: Optional[Dict[str, Any]] = None,
|
|
257
364
|
endpoint: Optional[str] = None
|
|
258
365
|
):
|
|
259
|
-
super().__init__(message, status_code, details, endpoint)
|
|
366
|
+
super().__init__(message, status_code, details, endpoint, kind="canceled")
|
|
260
367
|
|
|
261
368
|
|
|
262
|
-
class
|
|
263
|
-
"""Raised when
|
|
369
|
+
class UnsupportedVersionError(DominusError):
|
|
370
|
+
"""Raised when an unsupported API or schema version is requested."""
|
|
371
|
+
kind = "unsupported_version"
|
|
264
372
|
|
|
265
373
|
def __init__(
|
|
266
374
|
self,
|
|
267
|
-
message: str = "
|
|
268
|
-
status_code: int =
|
|
375
|
+
message: str = "Unsupported version",
|
|
376
|
+
status_code: int = 400,
|
|
269
377
|
details: Optional[Dict[str, Any]] = None,
|
|
270
378
|
endpoint: Optional[str] = None
|
|
271
379
|
):
|
|
272
|
-
super().__init__(message, status_code, details, endpoint)
|
|
380
|
+
super().__init__(message, status_code, details, endpoint, kind="unsupported_version")
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
# ---------------------------------------------------------------------------
|
|
384
|
+
# v1 aliases — existing public class names preserved.
|
|
385
|
+
# Every existing name maps to exactly one canonical per-kind subclass.
|
|
386
|
+
# Deprecation warnings fire in v2; removal in v3 (Seams fork 6, ADR 0010).
|
|
387
|
+
# ---------------------------------------------------------------------------
|
|
388
|
+
|
|
389
|
+
AuthenticationError = UnauthorizedError
|
|
390
|
+
AuthorizationError = ForbiddenError
|
|
391
|
+
ValidationError = ValidationFailedError
|
|
392
|
+
ServiceError = UpstreamUnavailableError
|
|
393
|
+
ConnectionError = UpstreamUnavailableError
|
|
394
|
+
SecureTableError = ForbiddenError
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
# Stash subclass aliases — map to the canonical per-kind base.
|
|
398
|
+
class StashBookmarkArtifactNotFound(NotFoundError):
|
|
399
|
+
"""Raised when a stash artifact bookmark target cannot be found."""
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
class StashBookmarkInvalidVersionRef(ValidationFailedError):
|
|
403
|
+
"""Raised when a stash artifact bookmark version_ref is invalid."""
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
class StashBookmarkDuplicateName(ConflictError):
|
|
407
|
+
"""Raised when a stash artifact bookmark name is already in use."""
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
class StashBookmarkUpstreamRejected(UpstreamUnavailableError):
|
|
411
|
+
"""Raised when the artifact backend rejects a stash bookmark operation."""
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
class StashWatchInvalidWebhookUrl(ValidationFailedError):
|
|
415
|
+
"""Raised when a stash artifact watcher webhook_url is invalid."""
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
class StashWatchUpstreamRejected(UpstreamUnavailableError):
|
|
419
|
+
"""Raised when the artifact backend rejects a stash watch operation."""
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
class StashWatchNotFound(NotFoundError):
|
|
423
|
+
"""Raised when a stash artifact watcher cannot be found."""
|
|
273
424
|
|
|
274
425
|
|
|
275
426
|
STASH_ERROR_CLASSES = {
|
|
@@ -303,16 +454,34 @@ def raise_for_status(
|
|
|
303
454
|
"""
|
|
304
455
|
code = _first_string((details or {}).get("code"))
|
|
305
456
|
error_classes = {
|
|
306
|
-
400:
|
|
307
|
-
401:
|
|
308
|
-
403:
|
|
457
|
+
400: ValidationFailedError,
|
|
458
|
+
401: UnauthorizedError,
|
|
459
|
+
403: ForbiddenError,
|
|
309
460
|
404: NotFoundError,
|
|
310
461
|
409: ConflictError,
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
462
|
+
429: RateLimitedError,
|
|
463
|
+
500: UpstreamUnavailableError,
|
|
464
|
+
502: UpstreamUnavailableError,
|
|
465
|
+
503: UpstreamUnavailableError,
|
|
314
466
|
504: TimeoutError,
|
|
315
467
|
}
|
|
316
468
|
|
|
317
|
-
|
|
469
|
+
kind_classes = {
|
|
470
|
+
kind: error_class for kind, error_class in (
|
|
471
|
+
("unauthorized", UnauthorizedError),
|
|
472
|
+
("forbidden", ForbiddenError),
|
|
473
|
+
("not_found", NotFoundError),
|
|
474
|
+
("validation_failed", ValidationFailedError),
|
|
475
|
+
("conflict", ConflictError),
|
|
476
|
+
("rate_limited", RateLimitedError),
|
|
477
|
+
("upstream_unavailable", UpstreamUnavailableError),
|
|
478
|
+
("internal", InternalError),
|
|
479
|
+
("timeout", TimeoutError),
|
|
480
|
+
("canceled", CanceledError),
|
|
481
|
+
("unsupported_version", UnsupportedVersionError),
|
|
482
|
+
)
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
kind = _first_string((details or {}).get("kind"))
|
|
486
|
+
error_class = STASH_ERROR_CLASSES.get(code) or kind_classes.get(kind) or error_classes.get(status_code, DominusError)
|
|
318
487
|
raise error_class(message, status_code, details, endpoint)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dominus-sdk-python
|
|
3
|
-
Version: 9.0.
|
|
3
|
+
Version: 9.0.4
|
|
4
4
|
Summary: Python SDK for the Dominus gateway-first platform
|
|
5
5
|
Author-email: CareBridge Systems <dev@carebridge.io>
|
|
6
6
|
License-Expression: LicenseRef-Proprietary
|
|
@@ -48,7 +48,7 @@ cleanup audit is `docs/janitor/2026-08-04-sdk-python-cleanup-audit.md`.
|
|
|
48
48
|
- Gateway-scoped client mode for MCP and other user-JWT sessions
|
|
49
49
|
- Transport compatibility for wrapped `{success,data}` responses and unwrapped Warden/control-plane success objects
|
|
50
50
|
- Local helpers for JWT verification, trace propagation, retries, and console capture
|
|
51
|
-
- Current package version: `9.0.
|
|
51
|
+
- Current package version: `9.0.4`
|
|
52
52
|
|
|
53
53
|
## Install
|
|
54
54
|
|
|
@@ -67,13 +67,12 @@ for operators and advanced use; this path is the stable teaching surface.
|
|
|
67
67
|
Set both values before the Python process imports the SDK singleton:
|
|
68
68
|
|
|
69
69
|
```bash
|
|
70
|
-
export
|
|
70
|
+
export DOMINUS_USER_TOKEN="your-user-token"
|
|
71
71
|
export DOMINUS_PROJECT="your-project-slug"
|
|
72
72
|
```
|
|
73
73
|
|
|
74
74
|
```python
|
|
75
75
|
import asyncio
|
|
76
|
-
import base64
|
|
77
76
|
|
|
78
77
|
from dominus import dominus
|
|
79
78
|
|
|
@@ -83,22 +82,6 @@ async def main() -> None:
|
|
|
83
82
|
workflow_recipe_ref="recipe://workflow-recipe-v1/hello@v1",
|
|
84
83
|
)
|
|
85
84
|
|
|
86
|
-
await dominus.artifacts.store_v2(
|
|
87
|
-
group="your-group",
|
|
88
|
-
owner="your-project-slug",
|
|
89
|
-
environment="production",
|
|
90
|
-
kind="blob",
|
|
91
|
-
artifact_key="hello",
|
|
92
|
-
data=base64.b64encode(b"hello dominus").decode("ascii"),
|
|
93
|
-
)
|
|
94
|
-
artifact = await dominus.artifacts.retrieve_v2(
|
|
95
|
-
group="your-group",
|
|
96
|
-
owner="your-project-slug",
|
|
97
|
-
environment="production",
|
|
98
|
-
kind="blob",
|
|
99
|
-
artifact_key="hello",
|
|
100
|
-
)
|
|
101
|
-
|
|
102
85
|
stash_result = await dominus.stash.upsert(
|
|
103
86
|
kind="config",
|
|
104
87
|
scope={"env": "production"},
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from dominus.errors import CANONICAL_ERROR_KINDS, DominusError, raise_for_status, recovery_action_for_kind
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@pytest.mark.parametrize("kind", CANONICAL_ERROR_KINDS)
|
|
7
|
+
def test_recovery_action_for_kind_is_closed_sum(kind: str) -> None:
|
|
8
|
+
retryable = False if kind == "upstream_unavailable" else None
|
|
9
|
+
action = recovery_action_for_kind(kind, retryable)
|
|
10
|
+
assert action in {"retry", "reauth", "escalate", "fix_config"}
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def test_dominus_error_recovery_action_accessor() -> None:
|
|
14
|
+
error = DominusError(
|
|
15
|
+
"Auth failed",
|
|
16
|
+
status_code=401,
|
|
17
|
+
details={"code": "kernel.auth.token_invalid", "kind": "unauthorized", "request_id": "req-auth"},
|
|
18
|
+
)
|
|
19
|
+
assert error.recovery_action == "reauth"
|
|
20
|
+
assert error.to_dict()["recovery_action"] == "reauth"
|
|
21
|
+
assert error.details["recovery_action"] == "reauth"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def test_raise_for_status_surfaces_recovery_action() -> None:
|
|
25
|
+
with pytest.raises(DominusError) as exc:
|
|
26
|
+
raise_for_status(
|
|
27
|
+
404,
|
|
28
|
+
"Missing",
|
|
29
|
+
{"code": "kernel.resource.resource_not_found", "kind": "not_found"},
|
|
30
|
+
)
|
|
31
|
+
assert exc.value.recovery_action == "fix_config"
|
{dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/tests/test_readme_quickstart_catalog.py
RENAMED
|
@@ -15,8 +15,6 @@ README = ROOT / "README.md"
|
|
|
15
15
|
SDK_PYTHON_CATALOG = frozenset(
|
|
16
16
|
{
|
|
17
17
|
"workflow.ensure",
|
|
18
|
-
"artifacts.store_v2",
|
|
19
|
-
"artifacts.retrieve_v2",
|
|
20
18
|
"stash.upsert",
|
|
21
19
|
"stash.get",
|
|
22
20
|
"authority.get_run_timeline",
|
|
@@ -24,7 +22,7 @@ SDK_PYTHON_CATALOG = frozenset(
|
|
|
24
22
|
}
|
|
25
23
|
)
|
|
26
24
|
|
|
27
|
-
FORBIDDEN = re.compile(r"summit|carebridge|PCM47474562|report-cycle", re.IGNORECASE)
|
|
25
|
+
FORBIDDEN = re.compile(r"summit|carebridge|PCM47474562|report-cycle|artifact", re.IGNORECASE)
|
|
28
26
|
|
|
29
27
|
|
|
30
28
|
def _quick_start_section(readme: str) -> str:
|
|
@@ -48,11 +46,11 @@ def test_quick_start_scope_precedes_import_and_methods_match_catalog() -> None:
|
|
|
48
46
|
python_index, body = _fenced(section, "python")
|
|
49
47
|
|
|
50
48
|
assert shell_index < python_index
|
|
51
|
-
assert 'export
|
|
49
|
+
assert 'export DOMINUS_USER_TOKEN="your-user-token"' in shell
|
|
52
50
|
assert 'export DOMINUS_PROJECT="your-project-slug"' in shell
|
|
53
51
|
assert "os.environ" not in body
|
|
54
52
|
assert "from dominus import dominus" in body
|
|
55
|
-
assert not FORBIDDEN.search(body), "Quick Start still has
|
|
53
|
+
assert not FORBIDDEN.search(body), "Quick Start still has forbidden catalog nouns"
|
|
56
54
|
|
|
57
55
|
found = set(re.findall(r"\bdominus\.([a-z0-9_]+)\.([a-z0-9_]+)\b", body))
|
|
58
56
|
identifiers = {f"{namespace}.{method}" for namespace, method in found}
|
|
@@ -64,9 +62,12 @@ def test_quick_start_executes_in_fresh_process_with_live_transport_shapes() -> N
|
|
|
64
62
|
r'''
|
|
65
63
|
import asyncio
|
|
66
64
|
import base64
|
|
65
|
+
import json
|
|
67
66
|
import sys
|
|
68
67
|
from pathlib import Path
|
|
69
68
|
|
|
69
|
+
import httpx
|
|
70
|
+
|
|
70
71
|
readme = Path(sys.argv[1]).read_text(encoding="utf-8")
|
|
71
72
|
section = readme[readme.index("## Quick Start"):]
|
|
72
73
|
marker = "```python\n"
|
|
@@ -74,48 +75,65 @@ def test_quick_start_executes_in_fresh_process_with_live_transport_shapes() -> N
|
|
|
74
75
|
body = section[start:section.index("```", start)]
|
|
75
76
|
|
|
76
77
|
from dominus import dominus
|
|
78
|
+
import dominus.helpers.core as core
|
|
77
79
|
|
|
78
80
|
asyncio.run(dominus._ensure_ready())
|
|
79
81
|
calls = []
|
|
80
82
|
|
|
81
|
-
async def
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
83
|
+
async def fake_jwt(*_args, **_kwargs):
|
|
84
|
+
return "header.payload.signature"
|
|
85
|
+
|
|
86
|
+
core._ensure_valid_jwt = fake_jwt
|
|
87
|
+
|
|
88
|
+
def handle(request):
|
|
89
|
+
body = json.loads(base64.b64decode(request.content)) if request.content else {}
|
|
90
|
+
calls.append(
|
|
91
|
+
{
|
|
92
|
+
"endpoint": request.url.path,
|
|
93
|
+
"body": body,
|
|
94
|
+
"headers": dict(request.headers),
|
|
95
|
+
}
|
|
96
|
+
)
|
|
97
|
+
if request.url.path == "/svc/authority/runs/ensure":
|
|
98
|
+
payload = {"run_id": "run-1"}
|
|
99
|
+
elif request.url.path == "/svc/stash/items/upsert":
|
|
100
|
+
payload = {"item": {"id": "stash-1"}, "scope_origin": "own"}
|
|
101
|
+
elif request.url.path == "/svc/stash/items/get":
|
|
102
|
+
payload = {"item": {"id": "stash-1"}}
|
|
103
|
+
else:
|
|
104
|
+
payload = {}
|
|
105
|
+
return httpx.Response(200, json=payload)
|
|
106
|
+
|
|
107
|
+
transport = httpx.MockTransport(handle)
|
|
108
|
+
real_async_client = httpx.AsyncClient
|
|
109
|
+
|
|
110
|
+
def mock_async_client(*args, **kwargs):
|
|
111
|
+
kwargs["transport"] = transport
|
|
112
|
+
return real_async_client(*args, **kwargs)
|
|
113
|
+
|
|
114
|
+
httpx.AsyncClient = mock_async_client
|
|
93
115
|
exec(compile(body, "README.md#quick-start", "exec"), {"__name__": "__main__"})
|
|
94
116
|
|
|
95
117
|
endpoints = [call["endpoint"] for call in calls]
|
|
96
118
|
assert endpoints == [
|
|
97
|
-
"/
|
|
98
|
-
"/api/artifact/v2/store",
|
|
99
|
-
"/api/artifact/v2/retrieve",
|
|
119
|
+
"/svc/authority/runs/ensure",
|
|
100
120
|
"/svc/stash/items/upsert",
|
|
101
121
|
"/svc/stash/items/get",
|
|
102
|
-
"/
|
|
103
|
-
"/
|
|
122
|
+
"/svc/authority/runs/run-1/timeline",
|
|
123
|
+
"/svc/authority/runs/run-1/verdict",
|
|
104
124
|
], endpoints
|
|
105
125
|
assert calls[0]["body"]["workflow_recipe_ref"] == "recipe://workflow-recipe-v1/hello@v1"
|
|
106
|
-
assert calls[1]["body"]
|
|
107
|
-
assert base64.b64decode(calls[1]["body"]["data"], validate=True) == b"hello dominus"
|
|
108
|
-
assert calls[3]["body"] == {
|
|
126
|
+
assert calls[1]["body"] == {
|
|
109
127
|
"kind": "config",
|
|
110
128
|
"scope": {"env": "production"},
|
|
111
129
|
"value": {"greeting": "hello"},
|
|
112
130
|
"item_key": "hello",
|
|
113
131
|
}
|
|
114
|
-
assert calls[
|
|
132
|
+
assert calls[2]["body"] == {"id": "stash-1"}
|
|
115
133
|
'''
|
|
116
134
|
)
|
|
117
135
|
env = os.environ.copy()
|
|
118
|
-
env["
|
|
136
|
+
env["DOMINUS_USER_TOKEN"] = "a" * 64
|
|
119
137
|
env["DOMINUS_PROJECT"] = "your-project-slug"
|
|
120
138
|
result = subprocess.run(
|
|
121
139
|
[sys.executable, "-c", child, str(README)],
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/dominus_sdk_python.egg-info/requires.txt
RENAMED
|
File without changes
|
{dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/dominus_sdk_python.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/tests/test_authority_public_vocabulary.py
RENAMED
|
File without changes
|
|
File without changes
|
{dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/tests/test_control_plane_namespaces.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/tests/test_namespace_barrel_exports.py
RENAMED
|
File without changes
|
{dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/tests/test_platform_coder_namespaces.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dominus_sdk_python-9.0.2 → dominus_sdk_python-9.0.4}/tests/test_secrets_presence_contract.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|