dominus-sdk-python 3.0.3__tar.gz → 3.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-3.0.3 → dominus_sdk_python-3.0.4}/PKG-INFO +4 -2
- {dominus_sdk_python-3.0.3 → dominus_sdk_python-3.0.4}/README.md +3 -1
- {dominus_sdk_python-3.0.3 → dominus_sdk_python-3.0.4}/dominus/__init__.py +4 -0
- {dominus_sdk_python-3.0.3 → dominus_sdk_python-3.0.4}/dominus/namespaces/__init__.py +4 -0
- dominus_sdk_python-3.0.4/dominus/namespaces/deployer.py +39 -0
- {dominus_sdk_python-3.0.3 → dominus_sdk_python-3.0.4}/dominus/namespaces/sync.py +6 -6
- dominus_sdk_python-3.0.4/dominus/namespaces/warden.py +39 -0
- {dominus_sdk_python-3.0.3 → dominus_sdk_python-3.0.4}/dominus/start.py +131 -0
- {dominus_sdk_python-3.0.3 → dominus_sdk_python-3.0.4}/dominus_sdk_python.egg-info/PKG-INFO +4 -2
- {dominus_sdk_python-3.0.3 → dominus_sdk_python-3.0.4}/dominus_sdk_python.egg-info/SOURCES.txt +3 -0
- {dominus_sdk_python-3.0.3 → dominus_sdk_python-3.0.4}/pyproject.toml +1 -1
- dominus_sdk_python-3.0.4/tests/test_control_plane_namespaces.py +49 -0
- {dominus_sdk_python-3.0.3 → dominus_sdk_python-3.0.4}/tests/test_public_exports.py +8 -1
- {dominus_sdk_python-3.0.3 → dominus_sdk_python-3.0.4}/tests/test_workflow_lifecycle.py +2 -2
- {dominus_sdk_python-3.0.3 → dominus_sdk_python-3.0.4}/dominus/config/__init__.py +0 -0
- {dominus_sdk_python-3.0.3 → dominus_sdk_python-3.0.4}/dominus/config/endpoints.py +0 -0
- {dominus_sdk_python-3.0.3 → dominus_sdk_python-3.0.4}/dominus/errors.py +0 -0
- {dominus_sdk_python-3.0.3 → dominus_sdk_python-3.0.4}/dominus/helpers/__init__.py +0 -0
- {dominus_sdk_python-3.0.3 → dominus_sdk_python-3.0.4}/dominus/helpers/auth.py +0 -0
- {dominus_sdk_python-3.0.3 → dominus_sdk_python-3.0.4}/dominus/helpers/cache.py +0 -0
- {dominus_sdk_python-3.0.3 → dominus_sdk_python-3.0.4}/dominus/helpers/console_capture.py +0 -0
- {dominus_sdk_python-3.0.3 → dominus_sdk_python-3.0.4}/dominus/helpers/core.py +0 -0
- {dominus_sdk_python-3.0.3 → dominus_sdk_python-3.0.4}/dominus/helpers/crypto.py +0 -0
- {dominus_sdk_python-3.0.3 → dominus_sdk_python-3.0.4}/dominus/helpers/sse.py +0 -0
- {dominus_sdk_python-3.0.3 → dominus_sdk_python-3.0.4}/dominus/helpers/trace.py +0 -0
- {dominus_sdk_python-3.0.3 → dominus_sdk_python-3.0.4}/dominus/namespaces/admin.py +0 -0
- {dominus_sdk_python-3.0.3 → dominus_sdk_python-3.0.4}/dominus/namespaces/ai.py +0 -0
- {dominus_sdk_python-3.0.3 → dominus_sdk_python-3.0.4}/dominus/namespaces/artifacts.py +0 -0
- {dominus_sdk_python-3.0.3 → dominus_sdk_python-3.0.4}/dominus/namespaces/auth.py +0 -0
- {dominus_sdk_python-3.0.3 → dominus_sdk_python-3.0.4}/dominus/namespaces/authority.py +0 -0
- {dominus_sdk_python-3.0.3 → dominus_sdk_python-3.0.4}/dominus/namespaces/courier.py +0 -0
- {dominus_sdk_python-3.0.3 → dominus_sdk_python-3.0.4}/dominus/namespaces/db.py +0 -0
- {dominus_sdk_python-3.0.3 → dominus_sdk_python-3.0.4}/dominus/namespaces/ddl.py +0 -0
- {dominus_sdk_python-3.0.3 → dominus_sdk_python-3.0.4}/dominus/namespaces/fastapi.py +0 -0
- {dominus_sdk_python-3.0.3 → dominus_sdk_python-3.0.4}/dominus/namespaces/files.py +0 -0
- {dominus_sdk_python-3.0.3 → dominus_sdk_python-3.0.4}/dominus/namespaces/health.py +0 -0
- {dominus_sdk_python-3.0.3 → dominus_sdk_python-3.0.4}/dominus/namespaces/jobs.py +0 -0
- {dominus_sdk_python-3.0.3 → dominus_sdk_python-3.0.4}/dominus/namespaces/logs.py +0 -0
- {dominus_sdk_python-3.0.3 → dominus_sdk_python-3.0.4}/dominus/namespaces/portal.py +0 -0
- {dominus_sdk_python-3.0.3 → dominus_sdk_python-3.0.4}/dominus/namespaces/processor.py +0 -0
- {dominus_sdk_python-3.0.3 → dominus_sdk_python-3.0.4}/dominus/namespaces/redis.py +0 -0
- {dominus_sdk_python-3.0.3 → dominus_sdk_python-3.0.4}/dominus/namespaces/secrets.py +0 -0
- {dominus_sdk_python-3.0.3 → dominus_sdk_python-3.0.4}/dominus/namespaces/secure.py +0 -0
- {dominus_sdk_python-3.0.3 → dominus_sdk_python-3.0.4}/dominus/namespaces/workflow.py +0 -0
- {dominus_sdk_python-3.0.3 → dominus_sdk_python-3.0.4}/dominus/services/__init__.py +0 -0
- {dominus_sdk_python-3.0.3 → dominus_sdk_python-3.0.4}/dominus_sdk_python.egg-info/dependency_links.txt +0 -0
- {dominus_sdk_python-3.0.3 → dominus_sdk_python-3.0.4}/dominus_sdk_python.egg-info/requires.txt +0 -0
- {dominus_sdk_python-3.0.3 → dominus_sdk_python-3.0.4}/dominus_sdk_python.egg-info/top_level.txt +0 -0
- {dominus_sdk_python-3.0.3 → dominus_sdk_python-3.0.4}/setup.cfg +0 -0
- {dominus_sdk_python-3.0.3 → dominus_sdk_python-3.0.4}/tests/test_auth.py +0 -0
- {dominus_sdk_python-3.0.3 → dominus_sdk_python-3.0.4}/tests/test_authority_public_vocabulary.py +0 -0
- {dominus_sdk_python-3.0.3 → dominus_sdk_python-3.0.4}/tests/test_errors.py +0 -0
- {dominus_sdk_python-3.0.3 → dominus_sdk_python-3.0.4}/tests/test_flat_commands.py +0 -0
- {dominus_sdk_python-3.0.3 → dominus_sdk_python-3.0.4}/tests/test_health.py +0 -0
- {dominus_sdk_python-3.0.3 → dominus_sdk_python-3.0.4}/tests/test_logs.py +0 -0
- {dominus_sdk_python-3.0.3 → dominus_sdk_python-3.0.4}/tests/test_provisioning_parity.py +0 -0
- {dominus_sdk_python-3.0.3 → dominus_sdk_python-3.0.4}/tests/test_transport_compat.py +0 -0
- {dominus_sdk_python-3.0.3 → dominus_sdk_python-3.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: 3.0.
|
|
3
|
+
Version: 3.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: Proprietary
|
|
@@ -41,7 +41,7 @@ Async Python SDK for the Dominus gateway-first service plane.
|
|
|
41
41
|
- Namespace-first API with a small root shortcut surface
|
|
42
42
|
- Gateway-scoped client mode for MCP and other user-JWT sessions
|
|
43
43
|
- Local helpers for JWT verification, trace propagation, retries, and console capture
|
|
44
|
-
- Current package version: `3.0.
|
|
44
|
+
- Current package version: `3.0.4`
|
|
45
45
|
|
|
46
46
|
## Install
|
|
47
47
|
|
|
@@ -126,6 +126,8 @@ JWT and selected scope headers directly through Gateway.
|
|
|
126
126
|
| `processor` | Processor | Batch and single-job processing |
|
|
127
127
|
| `sync` | Sync Worker | KV synchronization |
|
|
128
128
|
| `authority` | Dominus Authority | Runs, companies, deploys, managed clients, context |
|
|
129
|
+
| `deployer` | Deployer | Thin operator control-plane request surface |
|
|
130
|
+
| `warden` | Warden | Thin operator control-plane request surface |
|
|
129
131
|
| `fastapi` | Local decorators | `@jwt`, `@psk`, `@scopes(...)` |
|
|
130
132
|
|
|
131
133
|
## Root Shortcuts
|
|
@@ -8,7 +8,7 @@ Async Python SDK for the Dominus gateway-first service plane.
|
|
|
8
8
|
- Namespace-first API with a small root shortcut surface
|
|
9
9
|
- Gateway-scoped client mode for MCP and other user-JWT sessions
|
|
10
10
|
- Local helpers for JWT verification, trace propagation, retries, and console capture
|
|
11
|
-
- Current package version: `3.0.
|
|
11
|
+
- Current package version: `3.0.4`
|
|
12
12
|
|
|
13
13
|
## Install
|
|
14
14
|
|
|
@@ -93,6 +93,8 @@ JWT and selected scope headers directly through Gateway.
|
|
|
93
93
|
| `processor` | Processor | Batch and single-job processing |
|
|
94
94
|
| `sync` | Sync Worker | KV synchronization |
|
|
95
95
|
| `authority` | Dominus Authority | Runs, companies, deploys, managed clients, context |
|
|
96
|
+
| `deployer` | Deployer | Thin operator control-plane request surface |
|
|
97
|
+
| `warden` | Warden | Thin operator control-plane request surface |
|
|
96
98
|
| `fastapi` | Local decorators | `@jwt`, `@psk`, `@scopes(...)` |
|
|
97
99
|
|
|
98
100
|
## Root Shortcuts
|
|
@@ -111,6 +111,8 @@ from .namespaces.jobs import JobsNamespace
|
|
|
111
111
|
from .namespaces.processor import ProcessorNamespace
|
|
112
112
|
from .namespaces.sync import SyncNamespace
|
|
113
113
|
from .namespaces.authority import AuthorityNamespace
|
|
114
|
+
from .namespaces.deployer import DeployerNamespace
|
|
115
|
+
from .namespaces.warden import WardenNamespace
|
|
114
116
|
|
|
115
117
|
# Export AI namespace for agent-runtime operations
|
|
116
118
|
from .namespaces.ai import (
|
|
@@ -211,6 +213,8 @@ __all__ = [
|
|
|
211
213
|
"ProcessorNamespace",
|
|
212
214
|
"SyncNamespace",
|
|
213
215
|
"AuthorityNamespace",
|
|
216
|
+
"DeployerNamespace",
|
|
217
|
+
"WardenNamespace",
|
|
214
218
|
# AI namespace for agent-runtime operations
|
|
215
219
|
"AiNamespace",
|
|
216
220
|
"RagSubNamespace",
|
|
@@ -15,6 +15,8 @@ from .jobs import JobsNamespace
|
|
|
15
15
|
from .processor import ProcessorNamespace
|
|
16
16
|
from .sync import SyncNamespace
|
|
17
17
|
from .authority import AuthorityNamespace
|
|
18
|
+
from .deployer import DeployerNamespace
|
|
19
|
+
from .warden import WardenNamespace
|
|
18
20
|
from .ai import (
|
|
19
21
|
AiNamespace,
|
|
20
22
|
RagSubNamespace,
|
|
@@ -40,6 +42,8 @@ __all__ = [
|
|
|
40
42
|
"ProcessorNamespace",
|
|
41
43
|
"SyncNamespace",
|
|
42
44
|
"AuthorityNamespace",
|
|
45
|
+
"DeployerNamespace",
|
|
46
|
+
"WardenNamespace",
|
|
43
47
|
"AiNamespace",
|
|
44
48
|
"RagSubNamespace",
|
|
45
49
|
"ArtifactsSubNamespace",
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""Thin Deployer control-plane namespace."""
|
|
2
|
+
from typing import Any, Dict, Optional, TYPE_CHECKING
|
|
3
|
+
|
|
4
|
+
if TYPE_CHECKING:
|
|
5
|
+
from ..start import Dominus
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _normalize_deployer_path(path: str) -> str:
|
|
9
|
+
trimmed = path.strip()
|
|
10
|
+
normalized = trimmed if trimmed.startswith("/") else f"/{trimmed}"
|
|
11
|
+
if normalized == "/svc/deployer" or normalized.startswith("/svc/deployer/"):
|
|
12
|
+
return normalized
|
|
13
|
+
if normalized == "/api/deployer" or normalized.startswith("/api/deployer/"):
|
|
14
|
+
return normalized
|
|
15
|
+
return f"/svc/deployer{normalized}"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class DeployerNamespace:
|
|
19
|
+
"""Deployer operator control-plane routes used by Mothership and MCP."""
|
|
20
|
+
|
|
21
|
+
def __init__(self, client: "Dominus"):
|
|
22
|
+
self._client = client
|
|
23
|
+
|
|
24
|
+
async def request(
|
|
25
|
+
self,
|
|
26
|
+
path: str,
|
|
27
|
+
*,
|
|
28
|
+
method: str = "GET",
|
|
29
|
+
body: Any = None,
|
|
30
|
+
headers: Optional[Dict[str, str]] = None,
|
|
31
|
+
timeout: float = 30.0,
|
|
32
|
+
) -> Any:
|
|
33
|
+
return await self._client.gateway_fetch(
|
|
34
|
+
_normalize_deployer_path(path),
|
|
35
|
+
method=method,
|
|
36
|
+
body=body,
|
|
37
|
+
headers=headers,
|
|
38
|
+
timeout=timeout,
|
|
39
|
+
)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Sync Namespace - KV synchronization operations.
|
|
3
3
|
|
|
4
|
-
Provides manual trigger for KV sync (token +
|
|
4
|
+
Provides manual trigger for KV sync (token + app metadata)
|
|
5
5
|
from database to Cloudflare KV. Routes through gateway to sync-worker.
|
|
6
6
|
|
|
7
7
|
The sync worker also runs automatically at 4AM UTC via cron.
|
|
@@ -11,7 +11,7 @@ Usage:
|
|
|
11
11
|
|
|
12
12
|
# Trigger manual sync (admin only)
|
|
13
13
|
result = await dominus.sync.trigger_sync()
|
|
14
|
-
print(f"Synced {result['tokens_synced']} tokens, {result['
|
|
14
|
+
print(f"Synced {result['tokens_synced']} tokens, {result['apps_synced']} apps")
|
|
15
15
|
|
|
16
16
|
# Health check
|
|
17
17
|
health = await dominus.sync.get_health()
|
|
@@ -26,7 +26,7 @@ class SyncNamespace:
|
|
|
26
26
|
"""
|
|
27
27
|
KV sync namespace.
|
|
28
28
|
|
|
29
|
-
Admin-only operations that synchronize token hashes and
|
|
29
|
+
Admin-only operations that synchronize token hashes and app
|
|
30
30
|
metadata from the database into Cloudflare KV. Purges stale
|
|
31
31
|
entries and re-seeds current data.
|
|
32
32
|
"""
|
|
@@ -54,15 +54,15 @@ class SyncNamespace:
|
|
|
54
54
|
"""
|
|
55
55
|
Trigger a manual KV sync. Requires admin system level.
|
|
56
56
|
|
|
57
|
-
Synchronizes token hashes and
|
|
57
|
+
Synchronizes token hashes and app metadata from the database
|
|
58
58
|
into Cloudflare KV. Purges stale entries and re-seeds current data.
|
|
59
59
|
|
|
60
60
|
Args:
|
|
61
61
|
timeout: Request timeout in seconds (default 60; Mothership uses 180).
|
|
62
62
|
|
|
63
63
|
Returns:
|
|
64
|
-
{tokens_synced,
|
|
65
|
-
|
|
64
|
+
{tokens_synced, apps_synced, tokens_deleted,
|
|
65
|
+
apps_deleted, errors, duration_ms, triggered_at}
|
|
66
66
|
"""
|
|
67
67
|
return await self._api(
|
|
68
68
|
"/api/sync/",
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""Thin Warden control-plane namespace."""
|
|
2
|
+
from typing import Any, Dict, Optional, TYPE_CHECKING
|
|
3
|
+
|
|
4
|
+
if TYPE_CHECKING:
|
|
5
|
+
from ..start import Dominus
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _normalize_warden_path(path: str) -> str:
|
|
9
|
+
trimmed = path.strip()
|
|
10
|
+
normalized = trimmed if trimmed.startswith("/") else f"/{trimmed}"
|
|
11
|
+
if normalized == "/svc/warden" or normalized.startswith("/svc/warden/"):
|
|
12
|
+
return normalized
|
|
13
|
+
if normalized == "/api/warden" or normalized.startswith("/api/warden/"):
|
|
14
|
+
return normalized
|
|
15
|
+
return f"/svc/warden{normalized}"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class WardenNamespace:
|
|
19
|
+
"""Warden operator control-plane routes used by Mothership and MCP."""
|
|
20
|
+
|
|
21
|
+
def __init__(self, client: "Dominus"):
|
|
22
|
+
self._client = client
|
|
23
|
+
|
|
24
|
+
async def request(
|
|
25
|
+
self,
|
|
26
|
+
path: str,
|
|
27
|
+
*,
|
|
28
|
+
method: str = "GET",
|
|
29
|
+
body: Any = None,
|
|
30
|
+
headers: Optional[Dict[str, str]] = None,
|
|
31
|
+
timeout: float = 30.0,
|
|
32
|
+
) -> Any:
|
|
33
|
+
return await self._client.gateway_fetch(
|
|
34
|
+
_normalize_warden_path(path),
|
|
35
|
+
method=method,
|
|
36
|
+
body=body,
|
|
37
|
+
headers=headers,
|
|
38
|
+
timeout=timeout,
|
|
39
|
+
)
|
|
@@ -172,6 +172,12 @@ class Dominus:
|
|
|
172
172
|
from .namespaces.authority import AuthorityNamespace
|
|
173
173
|
self.authority = AuthorityNamespace(self)
|
|
174
174
|
|
|
175
|
+
# Thin operator control-plane namespaces (Mothership / MCP parity)
|
|
176
|
+
from .namespaces.deployer import DeployerNamespace
|
|
177
|
+
from .namespaces.warden import WardenNamespace
|
|
178
|
+
self.deployer = DeployerNamespace(self)
|
|
179
|
+
self.warden = WardenNamespace(self)
|
|
180
|
+
|
|
175
181
|
# Cache for JWT public key
|
|
176
182
|
self._public_key_cache = None
|
|
177
183
|
|
|
@@ -475,6 +481,131 @@ class Dominus:
|
|
|
475
481
|
|
|
476
482
|
return result.get("data", {})
|
|
477
483
|
|
|
484
|
+
async def gateway_fetch(
|
|
485
|
+
self,
|
|
486
|
+
path: str,
|
|
487
|
+
*,
|
|
488
|
+
method: str = "GET",
|
|
489
|
+
body: Optional[Dict[str, Any]] = None,
|
|
490
|
+
headers: Optional[Dict[str, str]] = None,
|
|
491
|
+
timeout: float = 30.0,
|
|
492
|
+
) -> Any:
|
|
493
|
+
"""Gateway `/svc/*` fetch with mixed JSON/base64 wire handling.
|
|
494
|
+
|
|
495
|
+
Mirrors the Node SDK `DominusClient.gatewayFetch()` contract so Python
|
|
496
|
+
callers can use the same remaining operator control-plane surfaces.
|
|
497
|
+
"""
|
|
498
|
+
await self._ensure_ready()
|
|
499
|
+
from .helpers.core import _ensure_valid_jwt
|
|
500
|
+
jwt = await _ensure_valid_jwt(_TOKEN, _BASE_URL)
|
|
501
|
+
|
|
502
|
+
path = path.strip()
|
|
503
|
+
if not path.startswith("/"):
|
|
504
|
+
path = f"/{path}"
|
|
505
|
+
|
|
506
|
+
is_base64 = (
|
|
507
|
+
"/svc/warden/secrets" in path
|
|
508
|
+
or "/svc/secrets/" in path
|
|
509
|
+
or "/svc/admin/" in path
|
|
510
|
+
)
|
|
511
|
+
decode_base64 = (
|
|
512
|
+
is_base64
|
|
513
|
+
or "/svc/database/" in path
|
|
514
|
+
or "/svc/sync/" in path
|
|
515
|
+
or "/svc/authority/" in path
|
|
516
|
+
)
|
|
517
|
+
is_get = method.upper() in {"GET", "HEAD"}
|
|
518
|
+
|
|
519
|
+
from .helpers.trace import get_current_trace_id, get_current_span_id
|
|
520
|
+
|
|
521
|
+
req_headers = {
|
|
522
|
+
"Authorization": f"Bearer {jwt}",
|
|
523
|
+
"X-Trace-Id": get_current_trace_id(),
|
|
524
|
+
"X-Parent-Span-Id": get_current_span_id(),
|
|
525
|
+
}
|
|
526
|
+
if headers:
|
|
527
|
+
req_headers.update(headers)
|
|
528
|
+
|
|
529
|
+
request_content = None
|
|
530
|
+
request_json = None
|
|
531
|
+
if not is_get and body is not None:
|
|
532
|
+
if is_base64:
|
|
533
|
+
req_headers["Content-Type"] = "text/plain"
|
|
534
|
+
request_content = base64.b64encode(json.dumps(body).encode()).decode()
|
|
535
|
+
else:
|
|
536
|
+
req_headers["Content-Type"] = "application/json"
|
|
537
|
+
request_json = body
|
|
538
|
+
|
|
539
|
+
try:
|
|
540
|
+
async with httpx.AsyncClient(base_url=_GATEWAY_URL, headers=req_headers, timeout=timeout) as client:
|
|
541
|
+
response = await client.request(
|
|
542
|
+
method.upper(),
|
|
543
|
+
path,
|
|
544
|
+
content=request_content,
|
|
545
|
+
json=request_json,
|
|
546
|
+
)
|
|
547
|
+
except httpx.TimeoutException as exc:
|
|
548
|
+
raise DominusTimeoutError("Request timed out", endpoint=path) from exc
|
|
549
|
+
except httpx.NetworkError as exc:
|
|
550
|
+
raise DominusConnectionError(f"Request failed: {exc}", endpoint=path) from exc
|
|
551
|
+
|
|
552
|
+
def _decode_error_payload(text: str) -> Dict[str, Any]:
|
|
553
|
+
if not text:
|
|
554
|
+
return {}
|
|
555
|
+
try:
|
|
556
|
+
from .helpers.core import _decode_json_or_b64_json
|
|
557
|
+
|
|
558
|
+
decoded = _decode_json_or_b64_json(text)
|
|
559
|
+
return decoded if isinstance(decoded, dict) else {"detail": decoded}
|
|
560
|
+
except Exception:
|
|
561
|
+
return {"detail": text}
|
|
562
|
+
|
|
563
|
+
if response.is_error:
|
|
564
|
+
error_payload = _decode_error_payload(response.text)
|
|
565
|
+
nested = error_payload.get("error")
|
|
566
|
+
nested_err: Dict[str, Any] = nested if isinstance(nested, dict) else {}
|
|
567
|
+
error_message = (
|
|
568
|
+
error_payload.get("message")
|
|
569
|
+
or (nested_err.get("message") if nested_err else None)
|
|
570
|
+
or (nested if isinstance(nested, str) else None)
|
|
571
|
+
or error_payload.get("error")
|
|
572
|
+
or error_payload.get("detail")
|
|
573
|
+
or f"HTTP {response.status_code}"
|
|
574
|
+
)
|
|
575
|
+
error_details = error_payload.get("details")
|
|
576
|
+
if not isinstance(error_details, dict):
|
|
577
|
+
error_details = {}
|
|
578
|
+
for src in (error_payload, nested_err):
|
|
579
|
+
for key in ("code", "category", "retryable", "trace_id", "request_id"):
|
|
580
|
+
if key in src and key not in error_details:
|
|
581
|
+
error_details[key] = src[key]
|
|
582
|
+
raise_for_status(response.status_code, str(error_message), error_details, path)
|
|
583
|
+
|
|
584
|
+
text = response.text.strip()
|
|
585
|
+
if not text:
|
|
586
|
+
return {}
|
|
587
|
+
|
|
588
|
+
if decode_base64:
|
|
589
|
+
decoded = base64.b64decode(text.encode("utf-8")).decode("utf-8")
|
|
590
|
+
result = json.loads(decoded)
|
|
591
|
+
else:
|
|
592
|
+
try:
|
|
593
|
+
result = json.loads(text)
|
|
594
|
+
except json.JSONDecodeError:
|
|
595
|
+
return text
|
|
596
|
+
|
|
597
|
+
if isinstance(result, dict) and result.get("success") is False:
|
|
598
|
+
error_msg = result.get("error") or result.get("message") or "Unknown error"
|
|
599
|
+
error_details = result.get("details")
|
|
600
|
+
if not isinstance(error_details, dict):
|
|
601
|
+
error_details = {}
|
|
602
|
+
for key in ("code", "category", "retryable", "trace_id", "request_id"):
|
|
603
|
+
if key in result:
|
|
604
|
+
error_details[key] = result[key]
|
|
605
|
+
raise DominusError(str(error_msg), details=error_details, endpoint=path)
|
|
606
|
+
|
|
607
|
+
return result
|
|
608
|
+
|
|
478
609
|
async def _stream_request(
|
|
479
610
|
self,
|
|
480
611
|
endpoint: str,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dominus-sdk-python
|
|
3
|
-
Version: 3.0.
|
|
3
|
+
Version: 3.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: Proprietary
|
|
@@ -41,7 +41,7 @@ Async Python SDK for the Dominus gateway-first service plane.
|
|
|
41
41
|
- Namespace-first API with a small root shortcut surface
|
|
42
42
|
- Gateway-scoped client mode for MCP and other user-JWT sessions
|
|
43
43
|
- Local helpers for JWT verification, trace propagation, retries, and console capture
|
|
44
|
-
- Current package version: `3.0.
|
|
44
|
+
- Current package version: `3.0.4`
|
|
45
45
|
|
|
46
46
|
## Install
|
|
47
47
|
|
|
@@ -126,6 +126,8 @@ JWT and selected scope headers directly through Gateway.
|
|
|
126
126
|
| `processor` | Processor | Batch and single-job processing |
|
|
127
127
|
| `sync` | Sync Worker | KV synchronization |
|
|
128
128
|
| `authority` | Dominus Authority | Runs, companies, deploys, managed clients, context |
|
|
129
|
+
| `deployer` | Deployer | Thin operator control-plane request surface |
|
|
130
|
+
| `warden` | Warden | Thin operator control-plane request surface |
|
|
129
131
|
| `fastapi` | Local decorators | `@jwt`, `@psk`, `@scopes(...)` |
|
|
130
132
|
|
|
131
133
|
## Root Shortcuts
|
{dominus_sdk_python-3.0.3 → dominus_sdk_python-3.0.4}/dominus_sdk_python.egg-info/SOURCES.txt
RENAMED
|
@@ -22,6 +22,7 @@ dominus/namespaces/authority.py
|
|
|
22
22
|
dominus/namespaces/courier.py
|
|
23
23
|
dominus/namespaces/db.py
|
|
24
24
|
dominus/namespaces/ddl.py
|
|
25
|
+
dominus/namespaces/deployer.py
|
|
25
26
|
dominus/namespaces/fastapi.py
|
|
26
27
|
dominus/namespaces/files.py
|
|
27
28
|
dominus/namespaces/health.py
|
|
@@ -33,6 +34,7 @@ dominus/namespaces/redis.py
|
|
|
33
34
|
dominus/namespaces/secrets.py
|
|
34
35
|
dominus/namespaces/secure.py
|
|
35
36
|
dominus/namespaces/sync.py
|
|
37
|
+
dominus/namespaces/warden.py
|
|
36
38
|
dominus/namespaces/workflow.py
|
|
37
39
|
dominus/services/__init__.py
|
|
38
40
|
dominus_sdk_python.egg-info/PKG-INFO
|
|
@@ -42,6 +44,7 @@ dominus_sdk_python.egg-info/requires.txt
|
|
|
42
44
|
dominus_sdk_python.egg-info/top_level.txt
|
|
43
45
|
tests/test_auth.py
|
|
44
46
|
tests/test_authority_public_vocabulary.py
|
|
47
|
+
tests/test_control_plane_namespaces.py
|
|
45
48
|
tests/test_errors.py
|
|
46
49
|
tests/test_flat_commands.py
|
|
47
50
|
tests/test_health.py
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
import dominus.start as start_module
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@pytest.fixture()
|
|
7
|
+
def sdk(monkeypatch):
|
|
8
|
+
monkeypatch.setattr(start_module, "_VALIDATION_ERROR", None)
|
|
9
|
+
monkeypatch.setattr(start_module, "_TOKEN", "a" * 64)
|
|
10
|
+
monkeypatch.setattr(start_module, "_VALIDATED", False)
|
|
11
|
+
monkeypatch.setattr(start_module, "_BASE_URL", "https://gateway.example")
|
|
12
|
+
monkeypatch.setattr(start_module, "_GATEWAY_URL", "https://gateway.example")
|
|
13
|
+
return start_module.Dominus()
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@pytest.mark.asyncio
|
|
17
|
+
async def test_deployer_namespace_uses_gateway_fetch(monkeypatch, sdk):
|
|
18
|
+
calls = []
|
|
19
|
+
|
|
20
|
+
async def fake_gateway_fetch(path, **kwargs):
|
|
21
|
+
calls.append((path, kwargs))
|
|
22
|
+
return {"ok": True}
|
|
23
|
+
|
|
24
|
+
monkeypatch.setattr(sdk, "gateway_fetch", fake_gateway_fetch)
|
|
25
|
+
|
|
26
|
+
result = await sdk.deployer.request("/deployments/active", method="GET")
|
|
27
|
+
|
|
28
|
+
assert result == {"ok": True}
|
|
29
|
+
assert calls == [
|
|
30
|
+
("/svc/deployer/deployments/active", {"method": "GET", "body": None, "headers": None, "timeout": 30.0})
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@pytest.mark.asyncio
|
|
35
|
+
async def test_warden_namespace_preserves_explicit_service_path(monkeypatch, sdk):
|
|
36
|
+
calls = []
|
|
37
|
+
|
|
38
|
+
async def fake_gateway_fetch(path, **kwargs):
|
|
39
|
+
calls.append((path, kwargs))
|
|
40
|
+
return {"ok": True}
|
|
41
|
+
|
|
42
|
+
monkeypatch.setattr(sdk, "gateway_fetch", fake_gateway_fetch)
|
|
43
|
+
|
|
44
|
+
result = await sdk.warden.request("/svc/warden/users/example", method="DELETE")
|
|
45
|
+
|
|
46
|
+
assert result == {"ok": True}
|
|
47
|
+
assert calls == [
|
|
48
|
+
("/svc/warden/users/example", {"method": "DELETE", "body": None, "headers": None, "timeout": 30.0})
|
|
49
|
+
]
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
from dominus import
|
|
1
|
+
from dominus import (
|
|
2
|
+
DeployerNamespace,
|
|
3
|
+
WardenNamespace,
|
|
4
|
+
gateway_circuit_breaker,
|
|
5
|
+
mint_selected_scope_jwt,
|
|
6
|
+
)
|
|
2
7
|
|
|
3
8
|
|
|
4
9
|
def test_top_level_exports_drop_delegate_alias():
|
|
@@ -22,3 +27,5 @@ def test_top_level_exports_drop_delegate_alias():
|
|
|
22
27
|
OpenNamespace = None
|
|
23
28
|
|
|
24
29
|
assert OpenNamespace is None
|
|
30
|
+
assert DeployerNamespace is not None
|
|
31
|
+
assert WardenNamespace is not None
|
|
@@ -269,8 +269,8 @@ async def test_workflow_ensure_supports_authority_one_call_lifecycle():
|
|
|
269
269
|
assert body["subject"] == "PCM47474562"
|
|
270
270
|
assert body["company"] == "summit-radiology"
|
|
271
271
|
assert body["inputs"] == {"report_snapshot": "ar://artifact"}
|
|
272
|
-
assert body["
|
|
273
|
-
assert body["
|
|
272
|
+
assert body["target_org_id"] == "proj-1"
|
|
273
|
+
assert body["target_env"] == "production"
|
|
274
274
|
assert isinstance(body["idempotency_key"], str) and body["idempotency_key"]
|
|
275
275
|
|
|
276
276
|
|
|
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-3.0.3 → dominus_sdk_python-3.0.4}/dominus_sdk_python.egg-info/requires.txt
RENAMED
|
File without changes
|
{dominus_sdk_python-3.0.3 → dominus_sdk_python-3.0.4}/dominus_sdk_python.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dominus_sdk_python-3.0.3 → dominus_sdk_python-3.0.4}/tests/test_authority_public_vocabulary.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
|