dominus-sdk-python 2.17.0__tar.gz → 2.18.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {dominus_sdk_python-2.17.0 → dominus_sdk_python-2.18.1}/PKG-INFO +11 -3
- {dominus_sdk_python-2.17.0 → dominus_sdk_python-2.18.1}/README.md +10 -2
- {dominus_sdk_python-2.17.0 → dominus_sdk_python-2.18.1}/dominus/__init__.py +5 -1
- {dominus_sdk_python-2.17.0 → dominus_sdk_python-2.18.1}/dominus/helpers/core.py +36 -15
- {dominus_sdk_python-2.17.0 → dominus_sdk_python-2.18.1}/dominus/namespaces/artifacts.py +84 -60
- {dominus_sdk_python-2.17.0 → dominus_sdk_python-2.18.1}/dominus/namespaces/logs.py +35 -6
- {dominus_sdk_python-2.17.0 → dominus_sdk_python-2.18.1}/dominus/namespaces/workflow.py +299 -107
- {dominus_sdk_python-2.17.0 → dominus_sdk_python-2.18.1}/dominus_sdk_python.egg-info/PKG-INFO +11 -3
- {dominus_sdk_python-2.17.0 → dominus_sdk_python-2.18.1}/dominus_sdk_python.egg-info/SOURCES.txt +1 -0
- {dominus_sdk_python-2.17.0 → dominus_sdk_python-2.18.1}/pyproject.toml +1 -1
- dominus_sdk_python-2.18.1/tests/test_logs.py +65 -0
- {dominus_sdk_python-2.17.0 → dominus_sdk_python-2.18.1}/tests/test_workflow_lifecycle.py +101 -87
- {dominus_sdk_python-2.17.0 → dominus_sdk_python-2.18.1}/tests/test_workflow_refs.py +12 -10
- {dominus_sdk_python-2.17.0 → dominus_sdk_python-2.18.1}/dominus/config/__init__.py +0 -0
- {dominus_sdk_python-2.17.0 → dominus_sdk_python-2.18.1}/dominus/config/endpoints.py +0 -0
- {dominus_sdk_python-2.17.0 → dominus_sdk_python-2.18.1}/dominus/errors.py +0 -0
- {dominus_sdk_python-2.17.0 → dominus_sdk_python-2.18.1}/dominus/helpers/__init__.py +0 -0
- {dominus_sdk_python-2.17.0 → dominus_sdk_python-2.18.1}/dominus/helpers/auth.py +0 -0
- {dominus_sdk_python-2.17.0 → dominus_sdk_python-2.18.1}/dominus/helpers/cache.py +0 -0
- {dominus_sdk_python-2.17.0 → dominus_sdk_python-2.18.1}/dominus/helpers/console_capture.py +0 -0
- {dominus_sdk_python-2.17.0 → dominus_sdk_python-2.18.1}/dominus/helpers/crypto.py +0 -0
- {dominus_sdk_python-2.17.0 → dominus_sdk_python-2.18.1}/dominus/helpers/sse.py +0 -0
- {dominus_sdk_python-2.17.0 → dominus_sdk_python-2.18.1}/dominus/helpers/trace.py +0 -0
- {dominus_sdk_python-2.17.0 → dominus_sdk_python-2.18.1}/dominus/namespaces/__init__.py +0 -0
- {dominus_sdk_python-2.17.0 → dominus_sdk_python-2.18.1}/dominus/namespaces/admin.py +0 -0
- {dominus_sdk_python-2.17.0 → dominus_sdk_python-2.18.1}/dominus/namespaces/ai.py +0 -0
- {dominus_sdk_python-2.17.0 → dominus_sdk_python-2.18.1}/dominus/namespaces/auth.py +0 -0
- {dominus_sdk_python-2.17.0 → dominus_sdk_python-2.18.1}/dominus/namespaces/courier.py +0 -0
- {dominus_sdk_python-2.17.0 → dominus_sdk_python-2.18.1}/dominus/namespaces/db.py +0 -0
- {dominus_sdk_python-2.17.0 → dominus_sdk_python-2.18.1}/dominus/namespaces/ddl.py +0 -0
- {dominus_sdk_python-2.17.0 → dominus_sdk_python-2.18.1}/dominus/namespaces/fastapi.py +0 -0
- {dominus_sdk_python-2.17.0 → dominus_sdk_python-2.18.1}/dominus/namespaces/files.py +0 -0
- {dominus_sdk_python-2.17.0 → dominus_sdk_python-2.18.1}/dominus/namespaces/health.py +0 -0
- {dominus_sdk_python-2.17.0 → dominus_sdk_python-2.18.1}/dominus/namespaces/jobs.py +0 -0
- {dominus_sdk_python-2.17.0 → dominus_sdk_python-2.18.1}/dominus/namespaces/open.py +0 -0
- {dominus_sdk_python-2.17.0 → dominus_sdk_python-2.18.1}/dominus/namespaces/oracle/__init__.py +0 -0
- {dominus_sdk_python-2.17.0 → dominus_sdk_python-2.18.1}/dominus/namespaces/oracle/audio_capture.py +0 -0
- {dominus_sdk_python-2.17.0 → dominus_sdk_python-2.18.1}/dominus/namespaces/oracle/oracle_websocket.py +0 -0
- {dominus_sdk_python-2.17.0 → dominus_sdk_python-2.18.1}/dominus/namespaces/oracle/session.py +0 -0
- {dominus_sdk_python-2.17.0 → dominus_sdk_python-2.18.1}/dominus/namespaces/oracle/types.py +0 -0
- {dominus_sdk_python-2.17.0 → dominus_sdk_python-2.18.1}/dominus/namespaces/oracle/vad_gate.py +0 -0
- {dominus_sdk_python-2.17.0 → dominus_sdk_python-2.18.1}/dominus/namespaces/portal.py +0 -0
- {dominus_sdk_python-2.17.0 → dominus_sdk_python-2.18.1}/dominus/namespaces/processor.py +0 -0
- {dominus_sdk_python-2.17.0 → dominus_sdk_python-2.18.1}/dominus/namespaces/redis.py +0 -0
- {dominus_sdk_python-2.17.0 → dominus_sdk_python-2.18.1}/dominus/namespaces/secrets.py +0 -0
- {dominus_sdk_python-2.17.0 → dominus_sdk_python-2.18.1}/dominus/namespaces/secure.py +0 -0
- {dominus_sdk_python-2.17.0 → dominus_sdk_python-2.18.1}/dominus/namespaces/sync.py +0 -0
- {dominus_sdk_python-2.17.0 → dominus_sdk_python-2.18.1}/dominus/services/__init__.py +0 -0
- {dominus_sdk_python-2.17.0 → dominus_sdk_python-2.18.1}/dominus/start.py +0 -0
- {dominus_sdk_python-2.17.0 → dominus_sdk_python-2.18.1}/dominus_sdk_python.egg-info/dependency_links.txt +0 -0
- {dominus_sdk_python-2.17.0 → dominus_sdk_python-2.18.1}/dominus_sdk_python.egg-info/requires.txt +0 -0
- {dominus_sdk_python-2.17.0 → dominus_sdk_python-2.18.1}/dominus_sdk_python.egg-info/top_level.txt +0 -0
- {dominus_sdk_python-2.17.0 → dominus_sdk_python-2.18.1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dominus-sdk-python
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.18.1
|
|
4
4
|
Summary: Python SDK for the Dominus Orchestrator Platform
|
|
5
5
|
Author-email: CareBridge Systems <dev@carebridge.io>
|
|
6
6
|
License: Proprietary
|
|
@@ -49,7 +49,7 @@ Async Python SDK for CareBridge Dominus platform services. Routes calls through
|
|
|
49
49
|
- Server-side, asyncio-first Python SDK (3.9+)
|
|
50
50
|
- Namespace API with root-level shortcuts for common operations
|
|
51
51
|
- Targets production Cloudflare Workers (gateway, JWT, logs) and Cloud Run (orchestrator)
|
|
52
|
-
- Version: 2.
|
|
52
|
+
- Version: 2.18.1
|
|
53
53
|
|
|
54
54
|
## Quick Start
|
|
55
55
|
|
|
@@ -89,7 +89,15 @@ async for chunk in dominus.ai.stream_agent(
|
|
|
89
89
|
):
|
|
90
90
|
print(chunk.get("content", ""), end="", flush=True)
|
|
91
91
|
|
|
92
|
-
#
|
|
92
|
+
# Preferred Authority-backed one-call lifecycle
|
|
93
|
+
execution = await dominus.workflow.ensure(
|
|
94
|
+
"wf://carebridge/report-cycle",
|
|
95
|
+
subject="PCM47474562",
|
|
96
|
+
company="summit-radiology",
|
|
97
|
+
inputs={"report_snapshot": "ar://carebridge/summit-radiology/production/snapshot/report-1"},
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
# Saved workflow lifecycle through workflow-manager when you need explicit artifacts
|
|
93
101
|
run = await dominus.workflow.create_run(
|
|
94
102
|
workflow_id="wf_saved_123",
|
|
95
103
|
context={"report_ref": {"report_id": "r-1", "accession": "a-1", "session_number": "2"}},
|
|
@@ -7,7 +7,7 @@ Async Python SDK for CareBridge Dominus platform services. Routes calls through
|
|
|
7
7
|
- Server-side, asyncio-first Python SDK (3.9+)
|
|
8
8
|
- Namespace API with root-level shortcuts for common operations
|
|
9
9
|
- Targets production Cloudflare Workers (gateway, JWT, logs) and Cloud Run (orchestrator)
|
|
10
|
-
- Version: 2.
|
|
10
|
+
- Version: 2.18.1
|
|
11
11
|
|
|
12
12
|
## Quick Start
|
|
13
13
|
|
|
@@ -47,7 +47,15 @@ async for chunk in dominus.ai.stream_agent(
|
|
|
47
47
|
):
|
|
48
48
|
print(chunk.get("content", ""), end="", flush=True)
|
|
49
49
|
|
|
50
|
-
#
|
|
50
|
+
# Preferred Authority-backed one-call lifecycle
|
|
51
|
+
execution = await dominus.workflow.ensure(
|
|
52
|
+
"wf://carebridge/report-cycle",
|
|
53
|
+
subject="PCM47474562",
|
|
54
|
+
company="summit-radiology",
|
|
55
|
+
inputs={"report_snapshot": "ar://carebridge/summit-radiology/production/snapshot/report-1"},
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
# Saved workflow lifecycle through workflow-manager when you need explicit artifacts
|
|
51
59
|
run = await dominus.workflow.create_run(
|
|
52
60
|
workflow_id="wf_saved_123",
|
|
53
61
|
context={"report_ref": {"report_id": "r-1", "accession": "a-1", "session_number": "2"}},
|
|
@@ -154,6 +154,8 @@ from .helpers.cache import (
|
|
|
154
154
|
from .helpers.core import (
|
|
155
155
|
verify_jwt_locally,
|
|
156
156
|
is_jwt_valid,
|
|
157
|
+
mint_selected_project_jwt,
|
|
158
|
+
delegate_jwt,
|
|
157
159
|
)
|
|
158
160
|
|
|
159
161
|
# Export trace utilities for distributed tracing
|
|
@@ -180,7 +182,7 @@ from .errors import (
|
|
|
180
182
|
TimeoutError as DominusTimeoutError,
|
|
181
183
|
)
|
|
182
184
|
|
|
183
|
-
__version__ = "2.
|
|
185
|
+
__version__ = "2.18.1"
|
|
184
186
|
__all__ = [
|
|
185
187
|
# Main SDK instance
|
|
186
188
|
"dominus",
|
|
@@ -250,6 +252,8 @@ __all__ = [
|
|
|
250
252
|
# JWT verification utilities
|
|
251
253
|
"verify_jwt_locally",
|
|
252
254
|
"is_jwt_valid",
|
|
255
|
+
"mint_selected_project_jwt",
|
|
256
|
+
"delegate_jwt",
|
|
253
257
|
# Trace utilities
|
|
254
258
|
"generate_trace_id",
|
|
255
259
|
"get_current_trace_id",
|
|
@@ -521,29 +521,31 @@ async def _ensure_valid_jwt(psk_token: str, sovereign_url: str) -> str:
|
|
|
521
521
|
return jwt
|
|
522
522
|
|
|
523
523
|
|
|
524
|
-
async def
|
|
524
|
+
async def mint_selected_project_jwt(
|
|
525
525
|
psk_token: str,
|
|
526
526
|
target_project_id: str,
|
|
527
527
|
target_environment: str,
|
|
528
528
|
use_shared: bool = False
|
|
529
529
|
) -> str:
|
|
530
530
|
"""
|
|
531
|
-
Mint a
|
|
531
|
+
Mint a selected-project JWT for a target project.
|
|
532
532
|
|
|
533
|
-
This is used by admin-level services
|
|
534
|
-
|
|
533
|
+
This is used by admin-level services to act within a selected project
|
|
534
|
+
context. The compatibility route name remains `/jwt/delegate`, but
|
|
535
|
+
the canonical contract is selected-project targeting, not a separate
|
|
536
|
+
delegated token family.
|
|
535
537
|
|
|
536
538
|
Args:
|
|
537
539
|
psk_token: Admin PSK token (DOMINUS_TOKEN for admin project)
|
|
538
540
|
target_project_id: UUID of the target project
|
|
539
541
|
target_environment: Environment (development, staging, production)
|
|
540
|
-
use_shared:
|
|
542
|
+
use_shared: Deprecated compatibility flag; selected-project JWTs infer shared context from the target project
|
|
541
543
|
|
|
542
544
|
Returns:
|
|
543
|
-
|
|
545
|
+
Selected-project JWT string
|
|
544
546
|
|
|
545
547
|
Raises:
|
|
546
|
-
RuntimeError: If
|
|
548
|
+
RuntimeError: If selected-project minting fails
|
|
547
549
|
"""
|
|
548
550
|
from ..config.endpoints import get_gateway_url, get_proxy_config
|
|
549
551
|
gateway_url = get_gateway_url()
|
|
@@ -557,13 +559,11 @@ async def delegate_jwt(
|
|
|
557
559
|
"X-Parent-Span-Id": get_current_span_id(),
|
|
558
560
|
}
|
|
559
561
|
|
|
560
|
-
# Body format for /jwt/delegate endpoint
|
|
561
562
|
body_json = {
|
|
562
563
|
"target_project_id": target_project_id,
|
|
563
564
|
"target_environment": target_environment
|
|
564
565
|
}
|
|
565
|
-
|
|
566
|
-
body_json["use_shared"] = True
|
|
566
|
+
_ = use_shared
|
|
567
567
|
|
|
568
568
|
body_b64 = _b64_encode(body_json)
|
|
569
569
|
|
|
@@ -576,13 +576,13 @@ async def delegate_jwt(
|
|
|
576
576
|
result = _b64_decode(response.text)
|
|
577
577
|
|
|
578
578
|
if not result.get("success"):
|
|
579
|
-
error_msg = result.get("error", "Unknown
|
|
580
|
-
raise RuntimeError(f"
|
|
579
|
+
error_msg = result.get("error", "Unknown selected-project mint error")
|
|
580
|
+
raise RuntimeError(f"Selected-project mint error: {error_msg}")
|
|
581
581
|
|
|
582
582
|
data = result.get("data", {})
|
|
583
583
|
jwt = data.get("token") or data.get("access_token")
|
|
584
584
|
if not jwt:
|
|
585
|
-
raise RuntimeError("No JWT token in
|
|
585
|
+
raise RuntimeError("No JWT token in selected-project mint response")
|
|
586
586
|
|
|
587
587
|
return jwt
|
|
588
588
|
|
|
@@ -594,10 +594,31 @@ async def delegate_jwt(
|
|
|
594
594
|
error_detail = error_body.get("error") or str(error_body)
|
|
595
595
|
except Exception:
|
|
596
596
|
error_detail = e.response.text[:200] if e.response.text else str(e)
|
|
597
|
-
raise RuntimeError(f"
|
|
597
|
+
raise RuntimeError(f"Selected-project JWT mint failed: {error_detail}") from e
|
|
598
598
|
|
|
599
599
|
except Exception as e:
|
|
600
|
-
raise RuntimeError(f"
|
|
600
|
+
raise RuntimeError(f"Selected-project JWT mint failed: {e}") from e
|
|
601
|
+
|
|
602
|
+
|
|
603
|
+
async def delegate_jwt(
|
|
604
|
+
psk_token: str,
|
|
605
|
+
target_project_id: str,
|
|
606
|
+
target_environment: str,
|
|
607
|
+
use_shared: bool = False
|
|
608
|
+
) -> str:
|
|
609
|
+
"""
|
|
610
|
+
Compatibility alias for :func:`mint_selected_project_jwt`.
|
|
611
|
+
|
|
612
|
+
.. deprecated::
|
|
613
|
+
Prefer :func:`mint_selected_project_jwt` for new code; "delegated" naming is
|
|
614
|
+
legacy terminology only.
|
|
615
|
+
"""
|
|
616
|
+
return await mint_selected_project_jwt(
|
|
617
|
+
psk_token=psk_token,
|
|
618
|
+
target_project_id=target_project_id,
|
|
619
|
+
target_environment=target_environment,
|
|
620
|
+
use_shared=use_shared,
|
|
621
|
+
)
|
|
601
622
|
|
|
602
623
|
|
|
603
624
|
def verify_token_format(token: str) -> bool:
|
|
@@ -4,10 +4,14 @@ Artifacts Namespace - Temporary artifact storage.
|
|
|
4
4
|
Provides auto-tiered artifact storage (Redis < 1MB, B2 >= 1MB)
|
|
5
5
|
with TTL-based expiration. Routes through gateway to artifact-worker.
|
|
6
6
|
|
|
7
|
+
The artifact worker returns HTTP 410 for legacy paths (/store, /retrieve, /list, /delete).
|
|
8
|
+
This SDK only calls /api/artifact/v2/*; convenience ``store``/``retrieve``/``delete`` map
|
|
9
|
+
those shapes to V2 compatibility addressing (see ``store_v2`` for canonical addressed writes).
|
|
10
|
+
|
|
7
11
|
Usage:
|
|
8
12
|
from dominus import dominus
|
|
9
13
|
|
|
10
|
-
#
|
|
14
|
+
# Prefer store_v2 / addressed refs for new code; legacy-shaped store maps to V2 internally
|
|
11
15
|
result = await dominus.artifacts.store(data="base64data...", ttl_seconds=7200)
|
|
12
16
|
|
|
13
17
|
# Retrieve
|
|
@@ -271,6 +275,21 @@ class ArtifactsNamespace:
|
|
|
271
275
|
def __init__(self, client: "Dominus"):
|
|
272
276
|
self._client = client
|
|
273
277
|
|
|
278
|
+
async def _legacy_project_context(self) -> tuple[str, str]:
|
|
279
|
+
"""project_id and env from the cached service JWT (for V1-shaped -> V2 compat calls)."""
|
|
280
|
+
from ..helpers.core import _ensure_valid_jwt
|
|
281
|
+
from ..start import _BASE_URL, _TOKEN
|
|
282
|
+
|
|
283
|
+
jwt = await _ensure_valid_jwt(_TOKEN, _BASE_URL)
|
|
284
|
+
claims = await self._client.validate_jwt(jwt)
|
|
285
|
+
pid = _safe_string(claims.get("project_id"))
|
|
286
|
+
env = _safe_string(claims.get("env")) or "production"
|
|
287
|
+
if not pid:
|
|
288
|
+
raise RuntimeError(
|
|
289
|
+
"dominus.artifacts legacy methods require project_id on the service JWT",
|
|
290
|
+
)
|
|
291
|
+
return pid, env
|
|
292
|
+
|
|
274
293
|
async def _api(
|
|
275
294
|
self,
|
|
276
295
|
endpoint: str,
|
|
@@ -293,19 +312,27 @@ class ArtifactsNamespace:
|
|
|
293
312
|
category: Optional[str] = None,
|
|
294
313
|
) -> Dict[str, Any]:
|
|
295
314
|
"""
|
|
296
|
-
List
|
|
315
|
+
List artifact heads (V2 / legacy kind) for the service JWT project.
|
|
297
316
|
|
|
298
|
-
|
|
299
|
-
limit: Max items to return (default 100, max 500)
|
|
300
|
-
category: Optional category filter
|
|
301
|
-
|
|
302
|
-
Returns:
|
|
303
|
-
{artifacts: [...], count: int, total_size_bytes: int}
|
|
317
|
+
Category filtering is client-side when rows include ``category``.
|
|
304
318
|
"""
|
|
305
|
-
|
|
319
|
+
project_id, environment = await self._legacy_project_context()
|
|
320
|
+
lim = max(1, min(int(limit or 100), 500))
|
|
321
|
+
raw = await self.list_v2(
|
|
322
|
+
lim,
|
|
323
|
+
group="dominus",
|
|
324
|
+
owner=f"project:{project_id}",
|
|
325
|
+
environment=environment,
|
|
326
|
+
kind="legacy",
|
|
327
|
+
)
|
|
328
|
+
artifacts = list(raw.get("artifacts") or [])
|
|
306
329
|
if category:
|
|
307
|
-
|
|
308
|
-
|
|
330
|
+
artifacts = [a for a in artifacts if (a or {}).get("category") == category]
|
|
331
|
+
count = raw.get("count", len(artifacts))
|
|
332
|
+
total = raw.get("total_size_bytes")
|
|
333
|
+
if total is None:
|
|
334
|
+
total = sum(int((a or {}).get("size_bytes") or 0) for a in artifacts)
|
|
335
|
+
return {"artifacts": artifacts, "count": count, "total_size_bytes": total}
|
|
309
336
|
|
|
310
337
|
async def get_health(self) -> Dict[str, Any]:
|
|
311
338
|
"""Check artifact worker health."""
|
|
@@ -320,52 +347,56 @@ class ArtifactsNamespace:
|
|
|
320
347
|
content_type: Optional[str] = None,
|
|
321
348
|
) -> Dict[str, Any]:
|
|
322
349
|
"""
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
Args:
|
|
326
|
-
data: Base64-encoded data
|
|
327
|
-
key: Optional caller-provided key (UUID generated if not provided)
|
|
328
|
-
ttl_seconds: Time-to-live in seconds (default 3600)
|
|
329
|
-
category: Optional category for organization
|
|
330
|
-
|
|
331
|
-
Returns:
|
|
332
|
-
{key, ref, storage_type, size_bytes, expires_at}
|
|
350
|
+
Legacy-shaped store mapped to V2 (dominus / project:{id} / legacy / encoded key).
|
|
333
351
|
"""
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
352
|
+
import uuid
|
|
353
|
+
|
|
354
|
+
project_id, environment = await self._legacy_project_context()
|
|
355
|
+
use_key = key or str(uuid.uuid4())
|
|
356
|
+
v2 = await self.store_v2(
|
|
357
|
+
group="dominus",
|
|
358
|
+
owner=f"project:{project_id}",
|
|
359
|
+
environment=environment,
|
|
360
|
+
kind="legacy",
|
|
361
|
+
artifact_key=quote(use_key, safe=""),
|
|
362
|
+
data=data,
|
|
363
|
+
ttl_seconds=ttl_seconds,
|
|
364
|
+
category=category,
|
|
365
|
+
content_type=content_type,
|
|
366
|
+
)
|
|
367
|
+
disp = build_display_artifact_ref(use_key)
|
|
368
|
+
return {
|
|
369
|
+
"key": use_key,
|
|
370
|
+
"ref": v2.get("compatibility_ref") or v2.get("head_ref") or disp,
|
|
371
|
+
"storage_type": v2.get("storage_type") or "redis",
|
|
372
|
+
"size_bytes": v2.get("size_bytes", 0),
|
|
373
|
+
"expires_at": v2.get("expires_at") or "",
|
|
337
374
|
}
|
|
338
|
-
if key:
|
|
339
|
-
body["key"] = key
|
|
340
|
-
if category:
|
|
341
|
-
body["category"] = category
|
|
342
|
-
if content_type:
|
|
343
|
-
body["content_type"] = content_type
|
|
344
|
-
return await self._api("/api/artifact/store", body=body)
|
|
345
375
|
|
|
346
376
|
async def retrieve(self, key: str) -> Dict[str, Any]:
|
|
347
377
|
"""
|
|
348
|
-
Retrieve
|
|
349
|
-
|
|
350
|
-
Args:
|
|
351
|
-
key: Artifact key
|
|
352
|
-
|
|
353
|
-
Returns:
|
|
354
|
-
{key, data, storage_type, size_bytes, expires_at}
|
|
378
|
+
Retrieve by key via V2 display ref + target_project_id (compat with legacy data).
|
|
355
379
|
"""
|
|
356
|
-
|
|
380
|
+
project_id, _environment = await self._legacy_project_context()
|
|
381
|
+
return await self._api(
|
|
382
|
+
"/api/artifact/v2/retrieve",
|
|
383
|
+
body={
|
|
384
|
+
"ref": f"{DISPLAY_REF_PREFIX}{key}",
|
|
385
|
+
"target_project_id": project_id,
|
|
386
|
+
},
|
|
387
|
+
)
|
|
357
388
|
|
|
358
389
|
async def delete(self, key: str) -> Dict[str, Any]:
|
|
359
|
-
"""
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
return
|
|
390
|
+
"""Delete legacy compatibility head via V2 selector."""
|
|
391
|
+
project_id, environment = await self._legacy_project_context()
|
|
392
|
+
await self.delete_v2(
|
|
393
|
+
group="dominus",
|
|
394
|
+
owner=f"project:{project_id}",
|
|
395
|
+
environment=environment,
|
|
396
|
+
kind="legacy",
|
|
397
|
+
artifact_key=quote(key, safe=""),
|
|
398
|
+
)
|
|
399
|
+
return {"deleted": True, "key": key}
|
|
369
400
|
|
|
370
401
|
async def cleanup(
|
|
371
402
|
self,
|
|
@@ -373,19 +404,12 @@ class ArtifactsNamespace:
|
|
|
373
404
|
limit: int = 100,
|
|
374
405
|
) -> Dict[str, Any]:
|
|
375
406
|
"""
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
Args:
|
|
379
|
-
force: Skip throttle check
|
|
380
|
-
limit: Max items to clean (default 100)
|
|
407
|
+
HTTP /cleanup is not exposed on the worker; opportunistic cleanup runs after v2 store.
|
|
381
408
|
|
|
382
|
-
Returns
|
|
383
|
-
{cleaned: int, skipped: bool}
|
|
409
|
+
Returns a skipped sentinel for API compatibility.
|
|
384
410
|
"""
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
body["force"] = True
|
|
388
|
-
return await self._api("/api/artifact/cleanup", body=body)
|
|
411
|
+
del force, limit
|
|
412
|
+
return {"cleaned": 0, "skipped": True}
|
|
389
413
|
|
|
390
414
|
def _selector_body(self, options: Dict[str, Any]) -> Dict[str, Any]:
|
|
391
415
|
body: Dict[str, Any] = {}
|
|
@@ -11,6 +11,7 @@ import sys
|
|
|
11
11
|
import time
|
|
12
12
|
from datetime import datetime, timezone
|
|
13
13
|
from typing import Any, Dict, List, Optional, Tuple, TYPE_CHECKING
|
|
14
|
+
from urllib.parse import urlencode
|
|
14
15
|
|
|
15
16
|
if TYPE_CHECKING:
|
|
16
17
|
from ..start import Dominus
|
|
@@ -321,6 +322,13 @@ class LogsNamespace:
|
|
|
321
322
|
minutes: int = 10,
|
|
322
323
|
limit: int = 100,
|
|
323
324
|
trace_id: Optional[str] = None,
|
|
325
|
+
since: Optional[str] = None,
|
|
326
|
+
company: Optional[str] = None,
|
|
327
|
+
subject: Optional[str] = None,
|
|
328
|
+
run_id: Optional[str] = None,
|
|
329
|
+
deploy_id: Optional[str] = None,
|
|
330
|
+
installation_id: Optional[str] = None,
|
|
331
|
+
artifact_ref: Optional[str] = None,
|
|
324
332
|
) -> List[Dict[str, Any]]:
|
|
325
333
|
"""Tail recent logs from the Logs Worker.
|
|
326
334
|
|
|
@@ -330,18 +338,39 @@ class LogsNamespace:
|
|
|
330
338
|
minutes: Time window — 1, 10, or 60 (default: 10)
|
|
331
339
|
limit: Maximum results, 1-1000 (default: 100)
|
|
332
340
|
trace_id: Optional trace_id filter to return only events from a specific trace
|
|
341
|
+
since: Optional ISO timestamp lower bound
|
|
342
|
+
company: Optional canonical company slug filter
|
|
343
|
+
subject: Optional domain subject filter
|
|
344
|
+
run_id: Optional workflow or Authority run id filter
|
|
345
|
+
deploy_id: Optional deploy authority id filter
|
|
346
|
+
installation_id: Optional managed-client installation id filter
|
|
347
|
+
artifact_ref: Optional artifact ref filter
|
|
333
348
|
|
|
334
349
|
Returns:
|
|
335
350
|
List of log event dicts
|
|
336
351
|
"""
|
|
337
|
-
params =
|
|
352
|
+
params: Dict[str, Any] = {}
|
|
338
353
|
if minutes != 10:
|
|
339
|
-
params
|
|
354
|
+
params["minutes"] = minutes
|
|
340
355
|
if limit != 100:
|
|
341
|
-
params
|
|
356
|
+
params["limit"] = limit
|
|
342
357
|
if trace_id:
|
|
343
|
-
params
|
|
344
|
-
|
|
358
|
+
params["trace_id"] = trace_id
|
|
359
|
+
if since:
|
|
360
|
+
params["since"] = since
|
|
361
|
+
if company:
|
|
362
|
+
params["company"] = company
|
|
363
|
+
if subject:
|
|
364
|
+
params["subject"] = subject
|
|
365
|
+
if run_id:
|
|
366
|
+
params["run_id"] = run_id
|
|
367
|
+
if deploy_id:
|
|
368
|
+
params["deploy_id"] = deploy_id
|
|
369
|
+
if installation_id:
|
|
370
|
+
params["installation_id"] = installation_id
|
|
371
|
+
if artifact_ref:
|
|
372
|
+
params["artifact_ref"] = artifact_ref
|
|
373
|
+
qs = urlencode(params)
|
|
345
374
|
endpoint = f"/api/logs/tail?{qs}" if qs else "/api/logs/tail"
|
|
346
375
|
|
|
347
376
|
try:
|
|
@@ -372,7 +401,7 @@ class LogsNamespace:
|
|
|
372
401
|
Returns:
|
|
373
402
|
List of log event dicts
|
|
374
403
|
"""
|
|
375
|
-
return await self.tail(minutes=minutes, limit=limit)
|
|
404
|
+
return await self.tail(minutes=minutes, limit=limit, **kwargs)
|
|
376
405
|
|
|
377
406
|
async def batch(
|
|
378
407
|
self,
|