dominus-sdk-python 4.0.6__tar.gz → 4.0.8__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-4.0.6 → dominus_sdk_python-4.0.8}/PKG-INFO +33 -2
- {dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/README.md +32 -1
- {dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/dominus/__init__.py +3 -1
- {dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/dominus/namespaces/__init__.py +2 -0
- {dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/dominus/namespaces/auth.py +9 -2
- dominus_sdk_python-4.0.8/dominus/namespaces/browser.py +198 -0
- {dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/dominus/start.py +4 -0
- {dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/dominus_sdk_python.egg-info/PKG-INFO +33 -2
- {dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/dominus_sdk_python.egg-info/SOURCES.txt +2 -0
- {dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/pyproject.toml +1 -1
- {dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/tests/test_auth.py +47 -1
- dominus_sdk_python-4.0.8/tests/test_browser_namespace.py +107 -0
- {dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/tests/test_public_exports.py +8 -0
- {dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/dominus/config/__init__.py +0 -0
- {dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/dominus/config/endpoints.py +0 -0
- {dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/dominus/errors.py +0 -0
- {dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/dominus/helpers/__init__.py +0 -0
- {dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/dominus/helpers/auth.py +0 -0
- {dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/dominus/helpers/cache.py +0 -0
- {dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/dominus/helpers/console_capture.py +0 -0
- {dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/dominus/helpers/core.py +0 -0
- {dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/dominus/helpers/crypto.py +0 -0
- {dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/dominus/helpers/sse.py +0 -0
- {dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/dominus/helpers/trace.py +0 -0
- {dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/dominus/namespaces/admin.py +0 -0
- {dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/dominus/namespaces/ai.py +0 -0
- {dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/dominus/namespaces/artifacts.py +0 -0
- {dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/dominus/namespaces/authority.py +0 -0
- {dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/dominus/namespaces/courier.py +0 -0
- {dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/dominus/namespaces/db.py +0 -0
- {dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/dominus/namespaces/ddl.py +0 -0
- {dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/dominus/namespaces/deployer.py +0 -0
- {dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/dominus/namespaces/fastapi.py +0 -0
- {dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/dominus/namespaces/files.py +0 -0
- {dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/dominus/namespaces/health.py +0 -0
- {dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/dominus/namespaces/jobs.py +0 -0
- {dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/dominus/namespaces/logs.py +0 -0
- {dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/dominus/namespaces/portal.py +0 -0
- {dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/dominus/namespaces/processor.py +0 -0
- {dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/dominus/namespaces/redis.py +0 -0
- {dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/dominus/namespaces/secrets.py +0 -0
- {dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/dominus/namespaces/secure.py +0 -0
- {dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/dominus/namespaces/sync.py +0 -0
- {dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/dominus/namespaces/warden.py +0 -0
- {dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/dominus/namespaces/workflow.py +0 -0
- {dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/dominus/services/__init__.py +0 -0
- {dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/dominus_sdk_python.egg-info/dependency_links.txt +0 -0
- {dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/dominus_sdk_python.egg-info/requires.txt +0 -0
- {dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/dominus_sdk_python.egg-info/top_level.txt +0 -0
- {dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/setup.cfg +0 -0
- {dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/tests/test_authority_public_vocabulary.py +0 -0
- {dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/tests/test_control_plane_namespaces.py +0 -0
- {dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/tests/test_errors.py +0 -0
- {dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/tests/test_flat_commands.py +0 -0
- {dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/tests/test_health.py +0 -0
- {dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/tests/test_logs.py +0 -0
- {dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/tests/test_provisioning_parity.py +0 -0
- {dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/tests/test_transport_compat.py +0 -0
- {dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/tests/test_workflow_lifecycle.py +0 -0
- {dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/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: 4.0.
|
|
3
|
+
Version: 4.0.8
|
|
4
4
|
Summary: Python SDK for the Dominus gateway-first platform
|
|
5
5
|
Author-email: CareBridge Systems <dev@carebridge.io>
|
|
6
6
|
License: Proprietary
|
|
@@ -42,7 +42,7 @@ Async Python SDK for the Dominus gateway-first service plane.
|
|
|
42
42
|
- Gateway-scoped client mode for MCP and other user-JWT sessions
|
|
43
43
|
- Transport compatibility for wrapped `{success,data}` responses and unwrapped Warden/control-plane success objects
|
|
44
44
|
- Local helpers for JWT verification, trace propagation, retries, and console capture
|
|
45
|
-
- Current package version: `4.0.
|
|
45
|
+
- Current package version: `4.0.8`
|
|
46
46
|
|
|
47
47
|
## Install
|
|
48
48
|
|
|
@@ -115,6 +115,34 @@ buffer_cleanup = await dominus.logs.run_archive_buffer_maintenance(
|
|
|
115
115
|
schedules = await dominus.authority.list_schedules(all_scopes=True)
|
|
116
116
|
```
|
|
117
117
|
|
|
118
|
+
## Browser Automation
|
|
119
|
+
|
|
120
|
+
`dominus.browser` exposes the first-class Dominus browser automation primitive through authenticated gateway routes under `/svc/browser/*`. SDK methods use `/api/browser/*` internally with gateway routing enabled; worker-local routes remain `/health` and `/runs/*`.
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
health = await dominus.browser.get_health()
|
|
124
|
+
run = await dominus.browser.ensure_run(
|
|
125
|
+
idempotency_key="route-check-1",
|
|
126
|
+
target={"url": "https://example.com/dashboard"},
|
|
127
|
+
provider="auto",
|
|
128
|
+
mode="playwright",
|
|
129
|
+
capture_policy={
|
|
130
|
+
"screenshots": "never",
|
|
131
|
+
"trace": "never",
|
|
132
|
+
"har": "never",
|
|
133
|
+
"video": "never",
|
|
134
|
+
"dom_snapshot": "never",
|
|
135
|
+
"raw_response_bodies": "never",
|
|
136
|
+
"phi_risk": "possible",
|
|
137
|
+
},
|
|
138
|
+
assertions=[{"kind": "status_code", "expected": 200}],
|
|
139
|
+
)
|
|
140
|
+
await dominus.browser.start_run(run["run_id"])
|
|
141
|
+
status = await dominus.browser.get_run_status(run["run_id"])
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Cloudflare Browser Run is the default provider. Browserbase is the fallback for future persistent authenticated/HITL work. Browser run metadata is runtime state owned by the browser worker; Artifact V2 is only for sanitized result/capture payloads.
|
|
145
|
+
|
|
118
146
|
## Session-Scoped Clients
|
|
119
147
|
|
|
120
148
|
Production MCP and other user-session callers should instantiate `Dominus` with
|
|
@@ -169,6 +197,7 @@ JWT and selected scope headers directly through Gateway.
|
|
|
169
197
|
| `processor` | Processor | Batch and single-job processing |
|
|
170
198
|
| `sync` | Sync Worker | KV synchronization |
|
|
171
199
|
| `authority` | Dominus Authority | Runs, companies, deploys, managed clients, context |
|
|
200
|
+
| `browser` | Browser Worker | Browser run health, ensure/start/status/result/retry/nudge/cancel/timeline/dossier |
|
|
172
201
|
| `deployer` | Deployer | Thin operator control-plane request surface |
|
|
173
202
|
| `warden` | Warden | Thin operator control-plane request surface |
|
|
174
203
|
| `fastapi` | Local decorators | `@jwt`, `@psk`, `@scopes(...)` |
|
|
@@ -185,6 +214,8 @@ operations:
|
|
|
185
214
|
|
|
186
215
|
New code should prefer namespace APIs.
|
|
187
216
|
|
|
217
|
+
Guardian navigation helpers expose nav-row `path` on `create_nav_item()` and `update_nav_item()`. Use that field when a sidebar item must route to a concrete URL independent of, or more specific than, the linked Guardian page row.
|
|
218
|
+
|
|
188
219
|
## Documentation
|
|
189
220
|
|
|
190
221
|
- [Architecture](docs/architecture.md) - request flow, gateway routing, resilience
|
|
@@ -9,7 +9,7 @@ Async Python SDK for the Dominus gateway-first service plane.
|
|
|
9
9
|
- Gateway-scoped client mode for MCP and other user-JWT sessions
|
|
10
10
|
- Transport compatibility for wrapped `{success,data}` responses and unwrapped Warden/control-plane success objects
|
|
11
11
|
- Local helpers for JWT verification, trace propagation, retries, and console capture
|
|
12
|
-
- Current package version: `4.0.
|
|
12
|
+
- Current package version: `4.0.8`
|
|
13
13
|
|
|
14
14
|
## Install
|
|
15
15
|
|
|
@@ -82,6 +82,34 @@ buffer_cleanup = await dominus.logs.run_archive_buffer_maintenance(
|
|
|
82
82
|
schedules = await dominus.authority.list_schedules(all_scopes=True)
|
|
83
83
|
```
|
|
84
84
|
|
|
85
|
+
## Browser Automation
|
|
86
|
+
|
|
87
|
+
`dominus.browser` exposes the first-class Dominus browser automation primitive through authenticated gateway routes under `/svc/browser/*`. SDK methods use `/api/browser/*` internally with gateway routing enabled; worker-local routes remain `/health` and `/runs/*`.
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
health = await dominus.browser.get_health()
|
|
91
|
+
run = await dominus.browser.ensure_run(
|
|
92
|
+
idempotency_key="route-check-1",
|
|
93
|
+
target={"url": "https://example.com/dashboard"},
|
|
94
|
+
provider="auto",
|
|
95
|
+
mode="playwright",
|
|
96
|
+
capture_policy={
|
|
97
|
+
"screenshots": "never",
|
|
98
|
+
"trace": "never",
|
|
99
|
+
"har": "never",
|
|
100
|
+
"video": "never",
|
|
101
|
+
"dom_snapshot": "never",
|
|
102
|
+
"raw_response_bodies": "never",
|
|
103
|
+
"phi_risk": "possible",
|
|
104
|
+
},
|
|
105
|
+
assertions=[{"kind": "status_code", "expected": 200}],
|
|
106
|
+
)
|
|
107
|
+
await dominus.browser.start_run(run["run_id"])
|
|
108
|
+
status = await dominus.browser.get_run_status(run["run_id"])
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Cloudflare Browser Run is the default provider. Browserbase is the fallback for future persistent authenticated/HITL work. Browser run metadata is runtime state owned by the browser worker; Artifact V2 is only for sanitized result/capture payloads.
|
|
112
|
+
|
|
85
113
|
## Session-Scoped Clients
|
|
86
114
|
|
|
87
115
|
Production MCP and other user-session callers should instantiate `Dominus` with
|
|
@@ -136,6 +164,7 @@ JWT and selected scope headers directly through Gateway.
|
|
|
136
164
|
| `processor` | Processor | Batch and single-job processing |
|
|
137
165
|
| `sync` | Sync Worker | KV synchronization |
|
|
138
166
|
| `authority` | Dominus Authority | Runs, companies, deploys, managed clients, context |
|
|
167
|
+
| `browser` | Browser Worker | Browser run health, ensure/start/status/result/retry/nudge/cancel/timeline/dossier |
|
|
139
168
|
| `deployer` | Deployer | Thin operator control-plane request surface |
|
|
140
169
|
| `warden` | Warden | Thin operator control-plane request surface |
|
|
141
170
|
| `fastapi` | Local decorators | `@jwt`, `@psk`, `@scopes(...)` |
|
|
@@ -152,6 +181,8 @@ operations:
|
|
|
152
181
|
|
|
153
182
|
New code should prefer namespace APIs.
|
|
154
183
|
|
|
184
|
+
Guardian navigation helpers expose nav-row `path` on `create_nav_item()` and `update_nav_item()`. Use that field when a sidebar item must route to a concrete URL independent of, or more specific than, the linked Guardian page row.
|
|
185
|
+
|
|
155
186
|
## Documentation
|
|
156
187
|
|
|
157
188
|
- [Architecture](docs/architecture.md) - request flow, gateway routing, resilience
|
|
@@ -111,6 +111,7 @@ 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.browser import BrowserNamespace
|
|
114
115
|
from .namespaces.deployer import DeployerNamespace
|
|
115
116
|
from .namespaces.warden import WardenNamespace
|
|
116
117
|
|
|
@@ -163,7 +164,7 @@ from .errors import (
|
|
|
163
164
|
TimeoutError as DominusTimeoutError,
|
|
164
165
|
)
|
|
165
166
|
|
|
166
|
-
__version__ = "4.0.
|
|
167
|
+
__version__ = "4.0.8"
|
|
167
168
|
__all__ = [
|
|
168
169
|
# Main SDK instance
|
|
169
170
|
"dominus",
|
|
@@ -213,6 +214,7 @@ __all__ = [
|
|
|
213
214
|
"ProcessorNamespace",
|
|
214
215
|
"SyncNamespace",
|
|
215
216
|
"AuthorityNamespace",
|
|
217
|
+
"BrowserNamespace",
|
|
216
218
|
"DeployerNamespace",
|
|
217
219
|
"WardenNamespace",
|
|
218
220
|
# AI namespace for agent-runtime operations
|
|
@@ -15,6 +15,7 @@ from .jobs import JobsNamespace
|
|
|
15
15
|
from .processor import ProcessorNamespace
|
|
16
16
|
from .sync import SyncNamespace
|
|
17
17
|
from .authority import AuthorityNamespace
|
|
18
|
+
from .browser import BrowserNamespace
|
|
18
19
|
from .deployer import DeployerNamespace
|
|
19
20
|
from .warden import WardenNamespace
|
|
20
21
|
from .ai import (
|
|
@@ -42,6 +43,7 @@ __all__ = [
|
|
|
42
43
|
"ProcessorNamespace",
|
|
43
44
|
"SyncNamespace",
|
|
44
45
|
"AuthorityNamespace",
|
|
46
|
+
"BrowserNamespace",
|
|
45
47
|
"DeployerNamespace",
|
|
46
48
|
"WardenNamespace",
|
|
47
49
|
"AiNamespace",
|
|
@@ -1421,7 +1421,8 @@ class AuthNamespace:
|
|
|
1421
1421
|
item_type: str = "link",
|
|
1422
1422
|
is_active: bool = True,
|
|
1423
1423
|
sort_order: int = 0,
|
|
1424
|
-
is_expanded_default: bool = False
|
|
1424
|
+
is_expanded_default: bool = False,
|
|
1425
|
+
path: Optional[str] = None
|
|
1425
1426
|
) -> Dict[str, Any]:
|
|
1426
1427
|
"""
|
|
1427
1428
|
Create a new navigation item.
|
|
@@ -1436,6 +1437,7 @@ class AuthNamespace:
|
|
|
1436
1437
|
is_active: Whether item is active
|
|
1437
1438
|
sort_order: Sort position
|
|
1438
1439
|
is_expanded_default: Whether group is expanded by default
|
|
1440
|
+
path: Direct navigation path. When set, Portal may use it instead of the linked page path.
|
|
1439
1441
|
"""
|
|
1440
1442
|
body = {
|
|
1441
1443
|
"title": title,
|
|
@@ -1444,6 +1446,8 @@ class AuthNamespace:
|
|
|
1444
1446
|
"is_active": is_active,
|
|
1445
1447
|
"is_expanded_default": is_expanded_default
|
|
1446
1448
|
}
|
|
1449
|
+
if path:
|
|
1450
|
+
body["path"] = path
|
|
1447
1451
|
if icon:
|
|
1448
1452
|
body["icon"] = icon
|
|
1449
1453
|
if description:
|
|
@@ -1515,12 +1519,15 @@ class AuthNamespace:
|
|
|
1515
1519
|
item_type: Optional[str] = None,
|
|
1516
1520
|
is_active: Optional[bool] = None,
|
|
1517
1521
|
sort_order: Optional[int] = None,
|
|
1518
|
-
is_expanded_default: Optional[bool] = None
|
|
1522
|
+
is_expanded_default: Optional[bool] = None,
|
|
1523
|
+
path: Optional[str] = None
|
|
1519
1524
|
) -> Dict[str, Any]:
|
|
1520
1525
|
"""Update navigation item."""
|
|
1521
1526
|
body = {}
|
|
1522
1527
|
if title is not None:
|
|
1523
1528
|
body["title"] = title
|
|
1529
|
+
if path is not None:
|
|
1530
|
+
body["path"] = path
|
|
1524
1531
|
if icon is not None:
|
|
1525
1532
|
body["icon"] = icon
|
|
1526
1533
|
if description is not None:
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Browser Namespace - first-class Dominus browser automation primitive.
|
|
3
|
+
|
|
4
|
+
All methods route through gateway (``/api/browser/*`` -> ``/svc/browser/*``).
|
|
5
|
+
Worker-local routes remain ``/health`` and ``/runs/*``; application code should
|
|
6
|
+
use this namespace instead of constructing worker-local paths directly.
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from typing import Any, Dict, Mapping, Optional, TYPE_CHECKING
|
|
11
|
+
from urllib.parse import quote
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from ..start import Dominus
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _compact(payload: Mapping[str, Any]) -> Dict[str, Any]:
|
|
18
|
+
"""Drop None and empty-string values."""
|
|
19
|
+
out: Dict[str, Any] = {}
|
|
20
|
+
for key, value in payload.items():
|
|
21
|
+
if value is None:
|
|
22
|
+
continue
|
|
23
|
+
if isinstance(value, str) and value == "":
|
|
24
|
+
continue
|
|
25
|
+
out[key] = value
|
|
26
|
+
return out
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class BrowserNamespace:
|
|
30
|
+
"""
|
|
31
|
+
Browser automation namespace.
|
|
32
|
+
|
|
33
|
+
Usage::
|
|
34
|
+
|
|
35
|
+
health = await dominus.browser.get_health()
|
|
36
|
+
run = await dominus.browser.ensure_run(
|
|
37
|
+
target={"url": "https://example.com/dashboard"},
|
|
38
|
+
assertions=[{"kind": "status_code", "expected": 200}],
|
|
39
|
+
)
|
|
40
|
+
await dominus.browser.start_run(run["run_id"])
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
def __init__(self, client: "Dominus"):
|
|
44
|
+
self._client = client
|
|
45
|
+
|
|
46
|
+
async def _post(
|
|
47
|
+
self,
|
|
48
|
+
endpoint: str,
|
|
49
|
+
body: Optional[Dict[str, Any]] = None,
|
|
50
|
+
*,
|
|
51
|
+
timeout: float = 30.0,
|
|
52
|
+
) -> Dict[str, Any]:
|
|
53
|
+
return await self._client._request(
|
|
54
|
+
endpoint=endpoint,
|
|
55
|
+
method="POST",
|
|
56
|
+
body=body or {},
|
|
57
|
+
use_gateway=True,
|
|
58
|
+
timeout=timeout,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
async def _get(self, endpoint: str, *, timeout: float = 30.0) -> Dict[str, Any]:
|
|
62
|
+
return await self._client._request(
|
|
63
|
+
endpoint=endpoint,
|
|
64
|
+
method="GET",
|
|
65
|
+
use_gateway=True,
|
|
66
|
+
timeout=timeout,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
async def get_health(self, *, timeout: float = 15.0) -> Dict[str, Any]:
|
|
70
|
+
"""Authenticated browser worker health. ``GET /api/browser/health``."""
|
|
71
|
+
return await self._get("/api/browser/health", timeout=timeout)
|
|
72
|
+
|
|
73
|
+
async def health(self, *, timeout: float = 15.0) -> Dict[str, Any]:
|
|
74
|
+
"""Alias for :meth:`get_health`."""
|
|
75
|
+
return await self.get_health(timeout=timeout)
|
|
76
|
+
|
|
77
|
+
async def ensure_run(
|
|
78
|
+
self,
|
|
79
|
+
*,
|
|
80
|
+
target: Dict[str, Any],
|
|
81
|
+
idempotency_key: Optional[str] = None,
|
|
82
|
+
run_kind: Optional[str] = None,
|
|
83
|
+
route_label: Optional[str] = None,
|
|
84
|
+
route_policy_ref: Optional[str] = None,
|
|
85
|
+
provider: Optional[str] = None,
|
|
86
|
+
mode: Optional[str] = None,
|
|
87
|
+
auth_ref: Optional[Dict[str, Any]] = None,
|
|
88
|
+
capture_policy: Optional[Dict[str, Any]] = None,
|
|
89
|
+
assertions: Optional[list[Dict[str, Any]]] = None,
|
|
90
|
+
authority: Optional[Dict[str, Any]] = None,
|
|
91
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
92
|
+
org_id: Optional[str] = None,
|
|
93
|
+
app_slug: Optional[str] = None,
|
|
94
|
+
env: Optional[str] = None,
|
|
95
|
+
timeout: float = 30.0,
|
|
96
|
+
) -> Dict[str, Any]:
|
|
97
|
+
"""
|
|
98
|
+
Ensure a browser run. ``POST /api/browser/runs/ensure``.
|
|
99
|
+
|
|
100
|
+
Browser run metadata remains browser-worker runtime state. Artifact V2
|
|
101
|
+
is only used by the worker for sanitized result/capture payloads.
|
|
102
|
+
"""
|
|
103
|
+
if not target:
|
|
104
|
+
raise ValueError("target is required")
|
|
105
|
+
body = _compact({
|
|
106
|
+
"idempotency_key": idempotency_key,
|
|
107
|
+
"run_kind": run_kind,
|
|
108
|
+
"route_label": route_label,
|
|
109
|
+
"route_policy_ref": route_policy_ref,
|
|
110
|
+
"target": target,
|
|
111
|
+
"provider": provider,
|
|
112
|
+
"mode": mode,
|
|
113
|
+
"auth_ref": auth_ref,
|
|
114
|
+
"capture_policy": capture_policy,
|
|
115
|
+
"assertions": assertions,
|
|
116
|
+
"authority": authority,
|
|
117
|
+
"metadata": metadata,
|
|
118
|
+
"org_id": org_id,
|
|
119
|
+
"app_slug": app_slug,
|
|
120
|
+
"env": env,
|
|
121
|
+
})
|
|
122
|
+
return await self._post("/api/browser/runs/ensure", body, timeout=timeout)
|
|
123
|
+
|
|
124
|
+
async def start_run(self, run_id: str, *, timeout: float = 30.0) -> Dict[str, Any]:
|
|
125
|
+
"""Start a queued browser run. ``POST /api/browser/runs/{run_id}/start``."""
|
|
126
|
+
if not run_id:
|
|
127
|
+
raise ValueError("run_id is required")
|
|
128
|
+
return await self._post(
|
|
129
|
+
f"/api/browser/runs/{quote(run_id, safe='')}/start",
|
|
130
|
+
{},
|
|
131
|
+
timeout=timeout,
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
async def get_run_status(self, run_id: str, *, timeout: float = 30.0) -> Dict[str, Any]:
|
|
135
|
+
"""Read browser run runtime state. ``GET /api/browser/runs/{run_id}/status``."""
|
|
136
|
+
if not run_id:
|
|
137
|
+
raise ValueError("run_id is required")
|
|
138
|
+
return await self._get(
|
|
139
|
+
f"/api/browser/runs/{quote(run_id, safe='')}/status",
|
|
140
|
+
timeout=timeout,
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
async def get_run_result(self, run_id: str, *, timeout: float = 30.0) -> Dict[str, Any]:
|
|
144
|
+
"""Read browser run result availability. ``GET /api/browser/runs/{run_id}/result``."""
|
|
145
|
+
if not run_id:
|
|
146
|
+
raise ValueError("run_id is required")
|
|
147
|
+
return await self._get(
|
|
148
|
+
f"/api/browser/runs/{quote(run_id, safe='')}/result",
|
|
149
|
+
timeout=timeout,
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
async def retry_run(self, run_id: str, *, timeout: float = 30.0) -> Dict[str, Any]:
|
|
153
|
+
"""Retry a failed or expired browser run. ``POST /api/browser/runs/{run_id}/retry``."""
|
|
154
|
+
if not run_id:
|
|
155
|
+
raise ValueError("run_id is required")
|
|
156
|
+
return await self._post(
|
|
157
|
+
f"/api/browser/runs/{quote(run_id, safe='')}/retry",
|
|
158
|
+
{},
|
|
159
|
+
timeout=timeout,
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
async def nudge_run(self, run_id: str, *, timeout: float = 30.0) -> Dict[str, Any]:
|
|
163
|
+
"""Nudge a paused or waiting browser run. ``POST /api/browser/runs/{run_id}/nudge``."""
|
|
164
|
+
if not run_id:
|
|
165
|
+
raise ValueError("run_id is required")
|
|
166
|
+
return await self._post(
|
|
167
|
+
f"/api/browser/runs/{quote(run_id, safe='')}/nudge",
|
|
168
|
+
{},
|
|
169
|
+
timeout=timeout,
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
async def cancel_run(self, run_id: str, *, timeout: float = 30.0) -> Dict[str, Any]:
|
|
173
|
+
"""Cancel a non-terminal browser run. ``POST /api/browser/runs/{run_id}/cancel``."""
|
|
174
|
+
if not run_id:
|
|
175
|
+
raise ValueError("run_id is required")
|
|
176
|
+
return await self._post(
|
|
177
|
+
f"/api/browser/runs/{quote(run_id, safe='')}/cancel",
|
|
178
|
+
{},
|
|
179
|
+
timeout=timeout,
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
async def get_run_timeline(self, run_id: str, *, timeout: float = 30.0) -> Dict[str, Any]:
|
|
183
|
+
"""Read browser run timeline. ``GET /api/browser/runs/{run_id}/timeline``."""
|
|
184
|
+
if not run_id:
|
|
185
|
+
raise ValueError("run_id is required")
|
|
186
|
+
return await self._get(
|
|
187
|
+
f"/api/browser/runs/{quote(run_id, safe='')}/timeline",
|
|
188
|
+
timeout=timeout,
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
async def get_run_dossier(self, run_id: str, *, timeout: float = 30.0) -> Dict[str, Any]:
|
|
192
|
+
"""Read browser run dossier. ``GET /api/browser/runs/{run_id}/dossier``."""
|
|
193
|
+
if not run_id:
|
|
194
|
+
raise ValueError("run_id is required")
|
|
195
|
+
return await self._get(
|
|
196
|
+
f"/api/browser/runs/{quote(run_id, safe='')}/dossier",
|
|
197
|
+
timeout=timeout,
|
|
198
|
+
)
|
|
@@ -172,6 +172,10 @@ class Dominus:
|
|
|
172
172
|
from .namespaces.authority import AuthorityNamespace
|
|
173
173
|
self.authority = AuthorityNamespace(self)
|
|
174
174
|
|
|
175
|
+
# Browser automation primitive (Cloudflare Browser Run default)
|
|
176
|
+
from .namespaces.browser import BrowserNamespace
|
|
177
|
+
self.browser = BrowserNamespace(self)
|
|
178
|
+
|
|
175
179
|
# Thin operator control-plane namespaces (Mothership / MCP parity)
|
|
176
180
|
from .namespaces.deployer import DeployerNamespace
|
|
177
181
|
from .namespaces.warden import WardenNamespace
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dominus-sdk-python
|
|
3
|
-
Version: 4.0.
|
|
3
|
+
Version: 4.0.8
|
|
4
4
|
Summary: Python SDK for the Dominus gateway-first platform
|
|
5
5
|
Author-email: CareBridge Systems <dev@carebridge.io>
|
|
6
6
|
License: Proprietary
|
|
@@ -42,7 +42,7 @@ Async Python SDK for the Dominus gateway-first service plane.
|
|
|
42
42
|
- Gateway-scoped client mode for MCP and other user-JWT sessions
|
|
43
43
|
- Transport compatibility for wrapped `{success,data}` responses and unwrapped Warden/control-plane success objects
|
|
44
44
|
- Local helpers for JWT verification, trace propagation, retries, and console capture
|
|
45
|
-
- Current package version: `4.0.
|
|
45
|
+
- Current package version: `4.0.8`
|
|
46
46
|
|
|
47
47
|
## Install
|
|
48
48
|
|
|
@@ -115,6 +115,34 @@ buffer_cleanup = await dominus.logs.run_archive_buffer_maintenance(
|
|
|
115
115
|
schedules = await dominus.authority.list_schedules(all_scopes=True)
|
|
116
116
|
```
|
|
117
117
|
|
|
118
|
+
## Browser Automation
|
|
119
|
+
|
|
120
|
+
`dominus.browser` exposes the first-class Dominus browser automation primitive through authenticated gateway routes under `/svc/browser/*`. SDK methods use `/api/browser/*` internally with gateway routing enabled; worker-local routes remain `/health` and `/runs/*`.
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
health = await dominus.browser.get_health()
|
|
124
|
+
run = await dominus.browser.ensure_run(
|
|
125
|
+
idempotency_key="route-check-1",
|
|
126
|
+
target={"url": "https://example.com/dashboard"},
|
|
127
|
+
provider="auto",
|
|
128
|
+
mode="playwright",
|
|
129
|
+
capture_policy={
|
|
130
|
+
"screenshots": "never",
|
|
131
|
+
"trace": "never",
|
|
132
|
+
"har": "never",
|
|
133
|
+
"video": "never",
|
|
134
|
+
"dom_snapshot": "never",
|
|
135
|
+
"raw_response_bodies": "never",
|
|
136
|
+
"phi_risk": "possible",
|
|
137
|
+
},
|
|
138
|
+
assertions=[{"kind": "status_code", "expected": 200}],
|
|
139
|
+
)
|
|
140
|
+
await dominus.browser.start_run(run["run_id"])
|
|
141
|
+
status = await dominus.browser.get_run_status(run["run_id"])
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Cloudflare Browser Run is the default provider. Browserbase is the fallback for future persistent authenticated/HITL work. Browser run metadata is runtime state owned by the browser worker; Artifact V2 is only for sanitized result/capture payloads.
|
|
145
|
+
|
|
118
146
|
## Session-Scoped Clients
|
|
119
147
|
|
|
120
148
|
Production MCP and other user-session callers should instantiate `Dominus` with
|
|
@@ -169,6 +197,7 @@ JWT and selected scope headers directly through Gateway.
|
|
|
169
197
|
| `processor` | Processor | Batch and single-job processing |
|
|
170
198
|
| `sync` | Sync Worker | KV synchronization |
|
|
171
199
|
| `authority` | Dominus Authority | Runs, companies, deploys, managed clients, context |
|
|
200
|
+
| `browser` | Browser Worker | Browser run health, ensure/start/status/result/retry/nudge/cancel/timeline/dossier |
|
|
172
201
|
| `deployer` | Deployer | Thin operator control-plane request surface |
|
|
173
202
|
| `warden` | Warden | Thin operator control-plane request surface |
|
|
174
203
|
| `fastapi` | Local decorators | `@jwt`, `@psk`, `@scopes(...)` |
|
|
@@ -185,6 +214,8 @@ operations:
|
|
|
185
214
|
|
|
186
215
|
New code should prefer namespace APIs.
|
|
187
216
|
|
|
217
|
+
Guardian navigation helpers expose nav-row `path` on `create_nav_item()` and `update_nav_item()`. Use that field when a sidebar item must route to a concrete URL independent of, or more specific than, the linked Guardian page row.
|
|
218
|
+
|
|
188
219
|
## Documentation
|
|
189
220
|
|
|
190
221
|
- [Architecture](docs/architecture.md) - request flow, gateway routing, resilience
|
{dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/dominus_sdk_python.egg-info/SOURCES.txt
RENAMED
|
@@ -19,6 +19,7 @@ dominus/namespaces/ai.py
|
|
|
19
19
|
dominus/namespaces/artifacts.py
|
|
20
20
|
dominus/namespaces/auth.py
|
|
21
21
|
dominus/namespaces/authority.py
|
|
22
|
+
dominus/namespaces/browser.py
|
|
22
23
|
dominus/namespaces/courier.py
|
|
23
24
|
dominus/namespaces/db.py
|
|
24
25
|
dominus/namespaces/ddl.py
|
|
@@ -44,6 +45,7 @@ dominus_sdk_python.egg-info/requires.txt
|
|
|
44
45
|
dominus_sdk_python.egg-info/top_level.txt
|
|
45
46
|
tests/test_auth.py
|
|
46
47
|
tests/test_authority_public_vocabulary.py
|
|
48
|
+
tests/test_browser_namespace.py
|
|
47
49
|
tests/test_control_plane_namespaces.py
|
|
48
50
|
tests/test_errors.py
|
|
49
51
|
tests/test_flat_commands.py
|
|
@@ -40,7 +40,12 @@ class FakeAsyncClient:
|
|
|
40
40
|
|
|
41
41
|
|
|
42
42
|
class FakeClient:
|
|
43
|
-
|
|
43
|
+
def __init__(self):
|
|
44
|
+
self.calls = []
|
|
45
|
+
|
|
46
|
+
async def _request(self, **kwargs):
|
|
47
|
+
self.calls.append(kwargs)
|
|
48
|
+
return {"ok": True}
|
|
44
49
|
|
|
45
50
|
|
|
46
51
|
@pytest.mark.asyncio
|
|
@@ -59,3 +64,44 @@ async def test_get_jwks_uses_gateway_jwt_route_and_caches(monkeypatch):
|
|
|
59
64
|
assert second == first
|
|
60
65
|
assert len(FakeAsyncClient.calls) == 1
|
|
61
66
|
assert FakeAsyncClient.calls[0]["url"] == "https://gateway.example/jwt/jwks"
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@pytest.mark.asyncio
|
|
70
|
+
async def test_nav_item_helpers_write_guardian_navigation_path():
|
|
71
|
+
client = FakeClient()
|
|
72
|
+
namespace = AuthNamespace(client)
|
|
73
|
+
|
|
74
|
+
await namespace.create_nav_item(
|
|
75
|
+
title="Structured Reporting",
|
|
76
|
+
path="/dashboard/radiologist/structured-reporting",
|
|
77
|
+
page_id="page-1",
|
|
78
|
+
icon="ListChecks",
|
|
79
|
+
)
|
|
80
|
+
await namespace.update_nav_item(
|
|
81
|
+
"nav-1",
|
|
82
|
+
path="/dashboard/radiologist/procedure-settings",
|
|
83
|
+
page_id="page-2",
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
assert client.calls[0]["endpoint"] == "/api/guardian/nav-items"
|
|
87
|
+
assert client.calls[0]["body"]["path"] == "/dashboard/radiologist/structured-reporting"
|
|
88
|
+
assert client.calls[0]["body"]["page_id"] == "page-1"
|
|
89
|
+
assert client.calls[1]["endpoint"] == "/api/guardian/nav-items/nav-1"
|
|
90
|
+
assert client.calls[1]["method"] == "PUT"
|
|
91
|
+
assert client.calls[1]["body"]["path"] == "/dashboard/radiologist/procedure-settings"
|
|
92
|
+
assert client.calls[1]["body"]["page_id"] == "page-2"
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
@pytest.mark.asyncio
|
|
96
|
+
async def test_nav_item_helpers_preserve_positional_icon_compatibility():
|
|
97
|
+
client = FakeClient()
|
|
98
|
+
namespace = AuthNamespace(client)
|
|
99
|
+
|
|
100
|
+
await namespace.create_nav_item("Structured Reporting", "ListChecks")
|
|
101
|
+
await namespace.update_nav_item("nav-1", "Procedure Settings", "ClipboardList")
|
|
102
|
+
|
|
103
|
+
assert client.calls[0]["body"]["icon"] == "ListChecks"
|
|
104
|
+
assert "path" not in client.calls[0]["body"]
|
|
105
|
+
assert client.calls[1]["body"]["title"] == "Procedure Settings"
|
|
106
|
+
assert client.calls[1]["body"]["icon"] == "ClipboardList"
|
|
107
|
+
assert "path" not in client.calls[1]["body"]
|
|
@@ -0,0 +1,107 @@
|
|
|
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_browser_namespace_uses_gateway_routed_api_browser_paths(monkeypatch, sdk):
|
|
18
|
+
calls = []
|
|
19
|
+
|
|
20
|
+
async def fake_request(**kwargs):
|
|
21
|
+
calls.append(kwargs)
|
|
22
|
+
return {
|
|
23
|
+
"ok": True,
|
|
24
|
+
"run_id": "br_123",
|
|
25
|
+
"status": "queued",
|
|
26
|
+
"provider": "cloudflare_browser_run",
|
|
27
|
+
"mode": "playwright",
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
monkeypatch.setattr(sdk, "_request", fake_request)
|
|
31
|
+
|
|
32
|
+
await sdk.browser.get_health()
|
|
33
|
+
await sdk.browser.ensure_run(
|
|
34
|
+
idempotency_key="idem-1",
|
|
35
|
+
run_kind="browser.route_check",
|
|
36
|
+
route_label="summit-dashboard",
|
|
37
|
+
route_policy_ref="rrc.v1:project",
|
|
38
|
+
target={"url": "https://summit.example/dashboard?token=redacted"},
|
|
39
|
+
provider="auto",
|
|
40
|
+
mode="playwright",
|
|
41
|
+
auth_ref={
|
|
42
|
+
"kind": "dominus_secret",
|
|
43
|
+
"secret_ref": "warden://browser/summit/session",
|
|
44
|
+
"expected_project_id": "project-123",
|
|
45
|
+
"expected_scopes": ["browser.run"],
|
|
46
|
+
"expected_view": "dashboard",
|
|
47
|
+
"session_probe": "#app",
|
|
48
|
+
},
|
|
49
|
+
capture_policy={
|
|
50
|
+
"screenshots": "on_failure",
|
|
51
|
+
"dom_snapshot": "always",
|
|
52
|
+
"raw_response_bodies": "always",
|
|
53
|
+
"phi_risk": "likely",
|
|
54
|
+
"retention_seconds": 900,
|
|
55
|
+
},
|
|
56
|
+
assertions=[
|
|
57
|
+
{"kind": "status_code", "expected": 200},
|
|
58
|
+
{"kind": "selector_visible", "selector": "#app", "timeout_ms": 5000},
|
|
59
|
+
],
|
|
60
|
+
authority={"run_id": "auth-run-1", "artifact_observations": True},
|
|
61
|
+
metadata={"route": "dashboard"},
|
|
62
|
+
)
|
|
63
|
+
await sdk.browser.start_run("br_123")
|
|
64
|
+
await sdk.browser.get_run_status("br_123")
|
|
65
|
+
await sdk.browser.get_run_result("br_123")
|
|
66
|
+
await sdk.browser.retry_run("br_123")
|
|
67
|
+
await sdk.browser.nudge_run("br_123")
|
|
68
|
+
await sdk.browser.cancel_run("br_123")
|
|
69
|
+
await sdk.browser.get_run_timeline("br_123")
|
|
70
|
+
await sdk.browser.get_run_dossier("br_123")
|
|
71
|
+
|
|
72
|
+
assert [(call["method"], call["endpoint"], call["use_gateway"]) for call in calls] == [
|
|
73
|
+
("GET", "/api/browser/health", True),
|
|
74
|
+
("POST", "/api/browser/runs/ensure", True),
|
|
75
|
+
("POST", "/api/browser/runs/br_123/start", True),
|
|
76
|
+
("GET", "/api/browser/runs/br_123/status", True),
|
|
77
|
+
("GET", "/api/browser/runs/br_123/result", True),
|
|
78
|
+
("POST", "/api/browser/runs/br_123/retry", True),
|
|
79
|
+
("POST", "/api/browser/runs/br_123/nudge", True),
|
|
80
|
+
("POST", "/api/browser/runs/br_123/cancel", True),
|
|
81
|
+
("GET", "/api/browser/runs/br_123/timeline", True),
|
|
82
|
+
("GET", "/api/browser/runs/br_123/dossier", True),
|
|
83
|
+
]
|
|
84
|
+
|
|
85
|
+
ensure_body = calls[1]["body"]
|
|
86
|
+
assert ensure_body["idempotency_key"] == "idem-1"
|
|
87
|
+
assert ensure_body["run_kind"] == "browser.route_check"
|
|
88
|
+
assert ensure_body["route_label"] == "summit-dashboard"
|
|
89
|
+
assert ensure_body["route_policy_ref"] == "rrc.v1:project"
|
|
90
|
+
assert ensure_body["auth_ref"]["secret_ref"] == "warden://browser/summit/session"
|
|
91
|
+
assert ensure_body["capture_policy"]["dom_snapshot"] == "always"
|
|
92
|
+
assert ensure_body["capture_policy"]["raw_response_bodies"] == "always"
|
|
93
|
+
assert ensure_body["assertions"][1] == {
|
|
94
|
+
"kind": "selector_visible",
|
|
95
|
+
"selector": "#app",
|
|
96
|
+
"timeout_ms": 5000,
|
|
97
|
+
}
|
|
98
|
+
assert ensure_body["authority"] == {
|
|
99
|
+
"run_id": "auth-run-1",
|
|
100
|
+
"artifact_observations": True,
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
@pytest.mark.asyncio
|
|
105
|
+
async def test_browser_ensure_run_requires_target(sdk):
|
|
106
|
+
with pytest.raises(ValueError, match="target is required"):
|
|
107
|
+
await sdk.browser.ensure_run(target={})
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from dominus import (
|
|
2
|
+
BrowserNamespace,
|
|
2
3
|
DeployerNamespace,
|
|
3
4
|
WardenNamespace,
|
|
4
5
|
gateway_circuit_breaker,
|
|
@@ -29,3 +30,10 @@ def test_top_level_exports_drop_delegate_alias():
|
|
|
29
30
|
assert OpenNamespace is None
|
|
30
31
|
assert DeployerNamespace is not None
|
|
31
32
|
assert WardenNamespace is not None
|
|
33
|
+
assert BrowserNamespace is not None
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def test_singleton_exposes_browser_namespace():
|
|
37
|
+
from dominus import dominus
|
|
38
|
+
|
|
39
|
+
assert callable(dominus.browser.get_health)
|
|
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-4.0.6 → dominus_sdk_python-4.0.8}/dominus_sdk_python.egg-info/requires.txt
RENAMED
|
File without changes
|
{dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/dominus_sdk_python.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
{dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/tests/test_authority_public_vocabulary.py
RENAMED
|
File without changes
|
{dominus_sdk_python-4.0.6 → dominus_sdk_python-4.0.8}/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
|