dominus-sdk-python 4.0.4__tar.gz → 4.0.6__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.
Files changed (58) hide show
  1. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/PKG-INFO +17 -2
  2. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/README.md +16 -1
  3. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/dominus/__init__.py +1 -1
  4. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/dominus/namespaces/authority.py +300 -0
  5. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/dominus/namespaces/logs.py +51 -5
  6. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/dominus_sdk_python.egg-info/PKG-INFO +17 -2
  7. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/pyproject.toml +1 -1
  8. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/tests/test_authority_public_vocabulary.py +56 -5
  9. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/tests/test_logs.py +32 -0
  10. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/dominus/config/__init__.py +0 -0
  11. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/dominus/config/endpoints.py +0 -0
  12. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/dominus/errors.py +0 -0
  13. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/dominus/helpers/__init__.py +0 -0
  14. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/dominus/helpers/auth.py +0 -0
  15. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/dominus/helpers/cache.py +0 -0
  16. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/dominus/helpers/console_capture.py +0 -0
  17. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/dominus/helpers/core.py +0 -0
  18. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/dominus/helpers/crypto.py +0 -0
  19. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/dominus/helpers/sse.py +0 -0
  20. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/dominus/helpers/trace.py +0 -0
  21. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/dominus/namespaces/__init__.py +0 -0
  22. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/dominus/namespaces/admin.py +0 -0
  23. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/dominus/namespaces/ai.py +0 -0
  24. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/dominus/namespaces/artifacts.py +0 -0
  25. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/dominus/namespaces/auth.py +0 -0
  26. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/dominus/namespaces/courier.py +0 -0
  27. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/dominus/namespaces/db.py +0 -0
  28. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/dominus/namespaces/ddl.py +0 -0
  29. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/dominus/namespaces/deployer.py +0 -0
  30. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/dominus/namespaces/fastapi.py +0 -0
  31. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/dominus/namespaces/files.py +0 -0
  32. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/dominus/namespaces/health.py +0 -0
  33. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/dominus/namespaces/jobs.py +0 -0
  34. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/dominus/namespaces/portal.py +0 -0
  35. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/dominus/namespaces/processor.py +0 -0
  36. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/dominus/namespaces/redis.py +0 -0
  37. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/dominus/namespaces/secrets.py +0 -0
  38. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/dominus/namespaces/secure.py +0 -0
  39. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/dominus/namespaces/sync.py +0 -0
  40. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/dominus/namespaces/warden.py +0 -0
  41. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/dominus/namespaces/workflow.py +0 -0
  42. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/dominus/services/__init__.py +0 -0
  43. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/dominus/start.py +0 -0
  44. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/dominus_sdk_python.egg-info/SOURCES.txt +0 -0
  45. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/dominus_sdk_python.egg-info/dependency_links.txt +0 -0
  46. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/dominus_sdk_python.egg-info/requires.txt +0 -0
  47. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/dominus_sdk_python.egg-info/top_level.txt +0 -0
  48. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/setup.cfg +0 -0
  49. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/tests/test_auth.py +0 -0
  50. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/tests/test_control_plane_namespaces.py +0 -0
  51. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/tests/test_errors.py +0 -0
  52. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/tests/test_flat_commands.py +0 -0
  53. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/tests/test_health.py +0 -0
  54. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/tests/test_provisioning_parity.py +0 -0
  55. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/tests/test_public_exports.py +0 -0
  56. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/tests/test_transport_compat.py +0 -0
  57. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/tests/test_workflow_lifecycle.py +0 -0
  58. {dominus_sdk_python-4.0.4 → dominus_sdk_python-4.0.6}/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.4
3
+ Version: 4.0.6
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.4`
45
+ - Current package version: `4.0.6`
46
46
 
47
47
  ## Install
48
48
 
@@ -80,6 +80,10 @@ timeline_archive = await dominus.authority.get_timeline_archive_status(
80
80
  app_slug="carebridge-summit",
81
81
  env="production",
82
82
  )
83
+ archive_batch = await dominus.authority.archive_timelines(
84
+ max_hours=24,
85
+ max_runtime_ms=600000,
86
+ )
83
87
  logs_archive = await dominus.logs.get_archive_status(
84
88
  all_scopes=True,
85
89
  include_buffer=True,
@@ -98,6 +102,17 @@ prune = await dominus.logs.prune_archive_retention(
98
102
  buffer_proof = await dominus.logs.dry_run_archive_buffer_maintenance(
99
103
  sample_limit=100,
100
104
  )
105
+ buffer_cleanup = await dominus.logs.run_archive_buffer_maintenance(
106
+ dry_run=False,
107
+ confirm="DELETE_ARCHIVE_BUFFER_RESIDUE",
108
+ salvage_missing_archive=True,
109
+ sample_limit=500,
110
+ max_archive_buckets=24,
111
+ batch_runs=5,
112
+ max_runtime_ms=8000,
113
+ )
114
+
115
+ schedules = await dominus.authority.list_schedules(all_scopes=True)
101
116
  ```
102
117
 
103
118
  ## Session-Scoped Clients
@@ -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.4`
12
+ - Current package version: `4.0.6`
13
13
 
14
14
  ## Install
15
15
 
@@ -47,6 +47,10 @@ timeline_archive = await dominus.authority.get_timeline_archive_status(
47
47
  app_slug="carebridge-summit",
48
48
  env="production",
49
49
  )
50
+ archive_batch = await dominus.authority.archive_timelines(
51
+ max_hours=24,
52
+ max_runtime_ms=600000,
53
+ )
50
54
  logs_archive = await dominus.logs.get_archive_status(
51
55
  all_scopes=True,
52
56
  include_buffer=True,
@@ -65,6 +69,17 @@ prune = await dominus.logs.prune_archive_retention(
65
69
  buffer_proof = await dominus.logs.dry_run_archive_buffer_maintenance(
66
70
  sample_limit=100,
67
71
  )
72
+ buffer_cleanup = await dominus.logs.run_archive_buffer_maintenance(
73
+ dry_run=False,
74
+ confirm="DELETE_ARCHIVE_BUFFER_RESIDUE",
75
+ salvage_missing_archive=True,
76
+ sample_limit=500,
77
+ max_archive_buckets=24,
78
+ batch_runs=5,
79
+ max_runtime_ms=8000,
80
+ )
81
+
82
+ schedules = await dominus.authority.list_schedules(all_scopes=True)
68
83
  ```
69
84
 
70
85
  ## Session-Scoped Clients
@@ -163,7 +163,7 @@ from .errors import (
163
163
  TimeoutError as DominusTimeoutError,
164
164
  )
165
165
 
166
- __version__ = "4.0.4"
166
+ __version__ = "4.0.5"
167
167
  __all__ = [
168
168
  # Main SDK instance
169
169
  "dominus",
@@ -1574,6 +1574,24 @@ class AuthorityNamespace:
1574
1574
  timeout=self._http_timeout(timeout, 15.0),
1575
1575
  )
1576
1576
 
1577
+ async def archive_timelines(
1578
+ self,
1579
+ *,
1580
+ max_hours: Optional[int] = None,
1581
+ max_runtime_ms: Optional[int] = None,
1582
+ timeout: Optional[float] = None,
1583
+ ) -> Dict[str, Any]:
1584
+ """Run a bounded Authority timeline archive batch. ``POST /api/authority/timelines/archive``."""
1585
+ body = _compact({
1586
+ "max_hours": max_hours,
1587
+ "max_runtime_ms": max_runtime_ms,
1588
+ })
1589
+ return await self._post(
1590
+ "/api/authority/timelines/archive",
1591
+ body,
1592
+ timeout=self._http_timeout(timeout, 30.0),
1593
+ )
1594
+
1577
1595
  async def repair_timeline_archive_manifests(
1578
1596
  self,
1579
1597
  *,
@@ -1634,6 +1652,288 @@ class AuthorityNamespace:
1634
1652
  timeout=self._http_timeout(timeout, 30.0),
1635
1653
  )
1636
1654
 
1655
+ # ==================================================================
1656
+ # Schedules
1657
+ # ==================================================================
1658
+
1659
+ @staticmethod
1660
+ def _schedule_body(
1661
+ *,
1662
+ schedule_key: Optional[str] = None,
1663
+ name: Optional[str] = None,
1664
+ description: Optional[str] = None,
1665
+ owner_app_slug: Optional[str] = None,
1666
+ owner_env: Optional[str] = None,
1667
+ target_app_slug: Optional[str] = None,
1668
+ target_env: Optional[str] = None,
1669
+ target_url: Optional[str] = None,
1670
+ target_path: Optional[str] = None,
1671
+ gateway_route: Optional[str] = None,
1672
+ method: Optional[str] = None,
1673
+ body: Optional[Dict[str, Any]] = None,
1674
+ headers: Optional[Dict[str, Any]] = None,
1675
+ auth_mode: Optional[str] = None,
1676
+ secret_ref: Optional[str] = None,
1677
+ frequency_kind: Optional[str] = None,
1678
+ frequency_value: Optional[str] = None,
1679
+ timezone: Optional[str] = None,
1680
+ enabled: Optional[bool] = None,
1681
+ next_due_at: Optional[str] = None,
1682
+ timeout_ms: Optional[int] = None,
1683
+ max_runtime_ms: Optional[int] = None,
1684
+ max_retries: Optional[int] = None,
1685
+ jitter_seconds: Optional[int] = None,
1686
+ app_slug: Optional[str] = None,
1687
+ env: Optional[str] = None,
1688
+ ) -> Dict[str, Any]:
1689
+ return _compact({
1690
+ "schedule_key": schedule_key,
1691
+ "name": name,
1692
+ "description": description,
1693
+ "owner_app_slug": owner_app_slug or app_slug,
1694
+ "owner_env": owner_env or env,
1695
+ "target_app_slug": target_app_slug,
1696
+ "target_env": target_env,
1697
+ "target_url": target_url,
1698
+ "target_path": target_path,
1699
+ "gateway_route": gateway_route,
1700
+ "method": method,
1701
+ "body_json": body,
1702
+ "headers_json": headers,
1703
+ "auth_mode": auth_mode,
1704
+ "secret_ref": secret_ref,
1705
+ "frequency_kind": frequency_kind,
1706
+ "frequency_value": frequency_value,
1707
+ "timezone": timezone,
1708
+ "enabled": enabled,
1709
+ "next_due_at": next_due_at,
1710
+ "timeout_ms": timeout_ms,
1711
+ "max_runtime_ms": max_runtime_ms,
1712
+ "max_retries": max_retries,
1713
+ "jitter_seconds": jitter_seconds,
1714
+ })
1715
+
1716
+ async def list_schedules(
1717
+ self,
1718
+ *,
1719
+ all_scopes: bool = False,
1720
+ app_slug: Optional[str] = None,
1721
+ env: Optional[str] = None,
1722
+ enabled: Optional[bool] = None,
1723
+ limit: Optional[int] = None,
1724
+ timeout: Optional[float] = None,
1725
+ ) -> Dict[str, Any]:
1726
+ """List Authority scheduled route definitions. ``GET /api/authority/schedules``."""
1727
+ qs = _query_string({
1728
+ "all_scopes": "true" if all_scopes else None,
1729
+ "app_slug": app_slug,
1730
+ "env": env,
1731
+ "enabled": str(enabled).lower() if enabled is not None else None,
1732
+ "limit": limit,
1733
+ })
1734
+ return await self._get(
1735
+ f"/api/authority/schedules{qs}",
1736
+ timeout=self._http_timeout(timeout, 15.0),
1737
+ )
1738
+
1739
+ async def create_schedule(
1740
+ self,
1741
+ *,
1742
+ schedule_key: Optional[str] = None,
1743
+ name: Optional[str] = None,
1744
+ description: Optional[str] = None,
1745
+ owner_app_slug: Optional[str] = None,
1746
+ owner_env: Optional[str] = None,
1747
+ target_app_slug: Optional[str] = None,
1748
+ target_env: Optional[str] = None,
1749
+ target_url: Optional[str] = None,
1750
+ target_path: Optional[str] = None,
1751
+ gateway_route: Optional[str] = None,
1752
+ method: Optional[str] = None,
1753
+ body: Optional[Dict[str, Any]] = None,
1754
+ headers: Optional[Dict[str, Any]] = None,
1755
+ auth_mode: Optional[str] = None,
1756
+ secret_ref: Optional[str] = None,
1757
+ frequency_kind: Optional[str] = None,
1758
+ frequency_value: Optional[str] = None,
1759
+ timezone: Optional[str] = None,
1760
+ enabled: Optional[bool] = None,
1761
+ next_due_at: Optional[str] = None,
1762
+ timeout_ms: Optional[int] = None,
1763
+ max_runtime_ms: Optional[int] = None,
1764
+ max_retries: Optional[int] = None,
1765
+ jitter_seconds: Optional[int] = None,
1766
+ app_slug: Optional[str] = None,
1767
+ env: Optional[str] = None,
1768
+ timeout: Optional[float] = None,
1769
+ ) -> Dict[str, Any]:
1770
+ """Create a scheduled route. ``POST /api/authority/schedules``."""
1771
+ return await self._post(
1772
+ "/api/authority/schedules",
1773
+ self._schedule_body(
1774
+ schedule_key=schedule_key,
1775
+ name=name,
1776
+ description=description,
1777
+ owner_app_slug=owner_app_slug,
1778
+ owner_env=owner_env,
1779
+ target_app_slug=target_app_slug,
1780
+ target_env=target_env,
1781
+ target_url=target_url,
1782
+ target_path=target_path,
1783
+ gateway_route=gateway_route,
1784
+ method=method,
1785
+ body=body,
1786
+ headers=headers,
1787
+ auth_mode=auth_mode,
1788
+ secret_ref=secret_ref,
1789
+ frequency_kind=frequency_kind,
1790
+ frequency_value=frequency_value,
1791
+ timezone=timezone,
1792
+ enabled=enabled,
1793
+ next_due_at=next_due_at,
1794
+ timeout_ms=timeout_ms,
1795
+ max_runtime_ms=max_runtime_ms,
1796
+ max_retries=max_retries,
1797
+ jitter_seconds=jitter_seconds,
1798
+ app_slug=app_slug,
1799
+ env=env,
1800
+ ),
1801
+ timeout=self._http_timeout(timeout, 30.0),
1802
+ )
1803
+
1804
+ async def update_schedule(
1805
+ self,
1806
+ schedule_id: str,
1807
+ *,
1808
+ schedule_key: Optional[str] = None,
1809
+ name: Optional[str] = None,
1810
+ description: Optional[str] = None,
1811
+ owner_app_slug: Optional[str] = None,
1812
+ owner_env: Optional[str] = None,
1813
+ target_app_slug: Optional[str] = None,
1814
+ target_env: Optional[str] = None,
1815
+ target_url: Optional[str] = None,
1816
+ target_path: Optional[str] = None,
1817
+ gateway_route: Optional[str] = None,
1818
+ method: Optional[str] = None,
1819
+ body: Optional[Dict[str, Any]] = None,
1820
+ headers: Optional[Dict[str, Any]] = None,
1821
+ auth_mode: Optional[str] = None,
1822
+ secret_ref: Optional[str] = None,
1823
+ frequency_kind: Optional[str] = None,
1824
+ frequency_value: Optional[str] = None,
1825
+ timezone: Optional[str] = None,
1826
+ enabled: Optional[bool] = None,
1827
+ next_due_at: Optional[str] = None,
1828
+ timeout_ms: Optional[int] = None,
1829
+ max_runtime_ms: Optional[int] = None,
1830
+ max_retries: Optional[int] = None,
1831
+ jitter_seconds: Optional[int] = None,
1832
+ app_slug: Optional[str] = None,
1833
+ env: Optional[str] = None,
1834
+ timeout: Optional[float] = None,
1835
+ ) -> Dict[str, Any]:
1836
+ """Update a scheduled route. ``PATCH /api/authority/schedules/{schedule_id}``."""
1837
+ return await self._client._request(
1838
+ endpoint=f"/api/authority/schedules/{quote(schedule_id, safe='')}",
1839
+ method="PATCH",
1840
+ body=self._schedule_body(
1841
+ schedule_key=schedule_key,
1842
+ name=name,
1843
+ description=description,
1844
+ owner_app_slug=owner_app_slug,
1845
+ owner_env=owner_env,
1846
+ target_app_slug=target_app_slug,
1847
+ target_env=target_env,
1848
+ target_url=target_url,
1849
+ target_path=target_path,
1850
+ gateway_route=gateway_route,
1851
+ method=method,
1852
+ body=body,
1853
+ headers=headers,
1854
+ auth_mode=auth_mode,
1855
+ secret_ref=secret_ref,
1856
+ frequency_kind=frequency_kind,
1857
+ frequency_value=frequency_value,
1858
+ timezone=timezone,
1859
+ enabled=enabled,
1860
+ next_due_at=next_due_at,
1861
+ timeout_ms=timeout_ms,
1862
+ max_runtime_ms=max_runtime_ms,
1863
+ max_retries=max_retries,
1864
+ jitter_seconds=jitter_seconds,
1865
+ app_slug=app_slug,
1866
+ env=env,
1867
+ ),
1868
+ use_gateway=True,
1869
+ timeout=self._http_timeout(timeout, 30.0),
1870
+ )
1871
+
1872
+ async def pause_schedule(self, schedule_id: str, *, timeout: Optional[float] = None) -> Dict[str, Any]:
1873
+ """Pause a scheduled route. ``POST /api/authority/schedules/{schedule_id}/pause``."""
1874
+ return await self._post(
1875
+ f"/api/authority/schedules/{quote(schedule_id, safe='')}/pause",
1876
+ {},
1877
+ timeout=self._http_timeout(timeout, 30.0),
1878
+ )
1879
+
1880
+ async def resume_schedule(self, schedule_id: str, *, timeout: Optional[float] = None) -> Dict[str, Any]:
1881
+ """Resume a scheduled route. ``POST /api/authority/schedules/{schedule_id}/resume``."""
1882
+ return await self._post(
1883
+ f"/api/authority/schedules/{quote(schedule_id, safe='')}/resume",
1884
+ {},
1885
+ timeout=self._http_timeout(timeout, 30.0),
1886
+ )
1887
+
1888
+ async def claim_due_schedules(
1889
+ self,
1890
+ *,
1891
+ limit: Optional[int] = None,
1892
+ lock_seconds: Optional[int] = None,
1893
+ timeout: Optional[float] = None,
1894
+ ) -> Dict[str, Any]:
1895
+ """Claim due scheduled routes for worker execution. ``POST /api/authority/schedules/claim-due``."""
1896
+ body = _compact({
1897
+ "limit": limit,
1898
+ "lock_seconds": lock_seconds,
1899
+ })
1900
+ return await self._post(
1901
+ "/api/authority/schedules/claim-due",
1902
+ body,
1903
+ timeout=self._http_timeout(timeout, 30.0),
1904
+ )
1905
+
1906
+ async def complete_schedule_run(
1907
+ self,
1908
+ schedule_id: str,
1909
+ run_id: str,
1910
+ *,
1911
+ result: Optional[Dict[str, Any]] = None,
1912
+ timeout: Optional[float] = None,
1913
+ ) -> Dict[str, Any]:
1914
+ """Mark a scheduled route run complete."""
1915
+ return await self._post(
1916
+ f"/api/authority/schedules/{quote(schedule_id, safe='')}/runs/{quote(run_id, safe='')}/complete",
1917
+ {"result": result or {}},
1918
+ timeout=self._http_timeout(timeout, 30.0),
1919
+ )
1920
+
1921
+ async def fail_schedule_run(
1922
+ self,
1923
+ schedule_id: str,
1924
+ run_id: str,
1925
+ *,
1926
+ error: str,
1927
+ result: Optional[Dict[str, Any]] = None,
1928
+ timeout: Optional[float] = None,
1929
+ ) -> Dict[str, Any]:
1930
+ """Mark a scheduled route run failed."""
1931
+ return await self._post(
1932
+ f"/api/authority/schedules/{quote(schedule_id, safe='')}/runs/{quote(run_id, safe='')}/fail",
1933
+ {"error": error, "result": result or {}},
1934
+ timeout=self._http_timeout(timeout, 30.0),
1935
+ )
1936
+
1637
1937
  # ==================================================================
1638
1938
  # Context
1639
1939
  # ==================================================================
@@ -513,24 +513,41 @@ class LogsNamespace:
513
513
  use_gateway=True,
514
514
  )
515
515
 
516
- async def dry_run_archive_buffer_maintenance(
516
+ async def run_archive_buffer_maintenance(
517
517
  self,
518
518
  *,
519
+ dry_run: bool = True,
520
+ confirm: Optional[str] = None,
521
+ all_scopes: Optional[bool] = None,
522
+ max_scopes: Optional[int] = None,
523
+ salvage_missing_archive: Optional[bool] = None,
519
524
  sample_limit: Optional[int] = None,
520
525
  max_archive_buckets: Optional[int] = None,
526
+ batch_runs: Optional[int] = None,
527
+ stop_when_payload_fields_at_or_below: Optional[int] = None,
528
+ max_runtime_ms: Optional[int] = None,
521
529
  target_org_id: Optional[str] = None,
522
530
  target_env: Optional[str] = None,
523
531
  use_shared: Optional[bool] = None,
524
532
  ) -> Dict[str, Any]:
525
- """Dry-run Redis archive buffer/payload residue cleanup proof. ``POST /api/logs/archive-buffer-maintenance``.
533
+ """Dry-run or enforce Redis archive buffer/payload residue cleanup. ``POST /api/logs/archive-buffer-maintenance``.
526
534
 
527
- This is intentionally read-only. The Logs Worker rejects destructive
528
- cleanup attempts on this route.
535
+ Destructive cleanup requires ``dry_run=False`` and
536
+ ``confirm='DELETE_ARCHIVE_BUFFER_RESIDUE'``. ``batch_runs`` and
537
+ ``max_runtime_ms`` are bounded by the Logs Worker so callers can run
538
+ confirmed salvage in safe chunks.
529
539
  """
530
540
  body = {
531
- "dry_run": True,
541
+ "dry_run": dry_run,
542
+ "confirm": confirm,
543
+ "all_scopes": all_scopes,
544
+ "max_scopes": max_scopes,
545
+ "salvage_missing_archive": salvage_missing_archive,
532
546
  "sample_limit": sample_limit,
533
547
  "max_archive_buckets": max_archive_buckets,
548
+ "batch_runs": batch_runs,
549
+ "stop_when_payload_fields_at_or_below": stop_when_payload_fields_at_or_below,
550
+ "max_runtime_ms": max_runtime_ms,
534
551
  "target_org_id": target_org_id,
535
552
  "target_env": target_env,
536
553
  }
@@ -543,6 +560,35 @@ class LogsNamespace:
543
560
  use_gateway=True,
544
561
  )
545
562
 
563
+ async def dry_run_archive_buffer_maintenance(
564
+ self,
565
+ *,
566
+ all_scopes: Optional[bool] = None,
567
+ max_scopes: Optional[int] = None,
568
+ sample_limit: Optional[int] = None,
569
+ max_archive_buckets: Optional[int] = None,
570
+ batch_runs: Optional[int] = None,
571
+ stop_when_payload_fields_at_or_below: Optional[int] = None,
572
+ max_runtime_ms: Optional[int] = None,
573
+ target_org_id: Optional[str] = None,
574
+ target_env: Optional[str] = None,
575
+ use_shared: Optional[bool] = None,
576
+ ) -> Dict[str, Any]:
577
+ """Dry-run Redis archive buffer/payload residue cleanup proof."""
578
+ return await self.run_archive_buffer_maintenance(
579
+ dry_run=True,
580
+ all_scopes=all_scopes,
581
+ max_scopes=max_scopes,
582
+ sample_limit=sample_limit,
583
+ max_archive_buckets=max_archive_buckets,
584
+ batch_runs=batch_runs,
585
+ stop_when_payload_fields_at_or_below=stop_when_payload_fields_at_or_below,
586
+ max_runtime_ms=max_runtime_ms,
587
+ target_org_id=target_org_id,
588
+ target_env=target_env,
589
+ use_shared=use_shared,
590
+ )
591
+
546
592
  async def query(
547
593
  self,
548
594
  minutes: int = 10,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dominus-sdk-python
3
- Version: 4.0.4
3
+ Version: 4.0.6
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.4`
45
+ - Current package version: `4.0.6`
46
46
 
47
47
  ## Install
48
48
 
@@ -80,6 +80,10 @@ timeline_archive = await dominus.authority.get_timeline_archive_status(
80
80
  app_slug="carebridge-summit",
81
81
  env="production",
82
82
  )
83
+ archive_batch = await dominus.authority.archive_timelines(
84
+ max_hours=24,
85
+ max_runtime_ms=600000,
86
+ )
83
87
  logs_archive = await dominus.logs.get_archive_status(
84
88
  all_scopes=True,
85
89
  include_buffer=True,
@@ -98,6 +102,17 @@ prune = await dominus.logs.prune_archive_retention(
98
102
  buffer_proof = await dominus.logs.dry_run_archive_buffer_maintenance(
99
103
  sample_limit=100,
100
104
  )
105
+ buffer_cleanup = await dominus.logs.run_archive_buffer_maintenance(
106
+ dry_run=False,
107
+ confirm="DELETE_ARCHIVE_BUFFER_RESIDUE",
108
+ salvage_missing_archive=True,
109
+ sample_limit=500,
110
+ max_archive_buckets=24,
111
+ batch_runs=5,
112
+ max_runtime_ms=8000,
113
+ )
114
+
115
+ schedules = await dominus.authority.list_schedules(all_scopes=True)
101
116
  ```
102
117
 
103
118
  ## Session-Scoped Clients
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "dominus-sdk-python"
7
- version = "4.0.4"
7
+ version = "4.0.6"
8
8
  description = "Python SDK for the Dominus gateway-first platform"
9
9
  readme = "README.md"
10
10
  license = {text = "Proprietary"}
@@ -79,9 +79,11 @@ def test_authority_scope_signatures_use_new_public_vocabulary():
79
79
  timeline_sig = inspect.signature(AuthorityNamespace.get_run_timeline)
80
80
  query_timelines_sig = inspect.signature(AuthorityNamespace.query_timelines)
81
81
  archive_status_sig = inspect.signature(AuthorityNamespace.get_timeline_archive_status)
82
+ archive_run_sig = inspect.signature(AuthorityNamespace.archive_timelines)
82
83
  archive_repair_sig = inspect.signature(AuthorityNamespace.repair_timeline_archive_manifests)
83
84
  archive_verify_sig = inspect.signature(AuthorityNamespace.verify_timeline_archive_manifests)
84
85
  archive_prune_sig = inspect.signature(AuthorityNamespace.prune_timeline_archive_retention)
86
+ schedule_create_sig = inspect.signature(AuthorityNamespace.create_schedule)
85
87
  mint_sig = inspect.signature(core_helpers.mint_selected_scope_jwt)
86
88
 
87
89
  for name in ("app_slug", "env", "target_org_id", "target_env", "run_kind", "target_app_slug", "bootstrap_profile"):
@@ -107,12 +109,16 @@ def test_authority_scope_signatures_use_new_public_vocabulary():
107
109
  assert name in timeline_sig.parameters
108
110
  assert name in query_timelines_sig.parameters
109
111
  assert "all_scopes" in archive_status_sig.parameters
112
+ for name in ("max_hours", "max_runtime_ms"):
113
+ assert name in archive_run_sig.parameters
110
114
  for name in ("since", "until"):
111
115
  assert name in archive_repair_sig.parameters
112
116
  assert name in archive_verify_sig.parameters
113
117
  assert name in archive_prune_sig.parameters
114
118
  for name in ("retention_days", "dry_run", "confirm"):
115
119
  assert name in archive_prune_sig.parameters
120
+ for name in ("gateway_route", "target_url", "frequency_kind", "max_runtime_ms", "app_slug", "env"):
121
+ assert name in schedule_create_sig.parameters
116
122
 
117
123
  for name in ("target_org_id", "target_app_slug", "target_env"):
118
124
  assert name in mint_sig.parameters
@@ -206,6 +212,10 @@ async def test_authority_scope_methods_use_canonical_context_wire_names():
206
212
  env="production",
207
213
  limit=5,
208
214
  )
215
+ await namespace.archive_timelines(
216
+ max_hours=24,
217
+ max_runtime_ms=600000,
218
+ )
209
219
  await namespace.repair_timeline_archive_manifests(
210
220
  since="2026-04-01T00:00:00Z",
211
221
  until="2026-04-02T00:00:00Z",
@@ -221,6 +231,22 @@ async def test_authority_scope_methods_use_canonical_context_wire_names():
221
231
  dry_run=False,
222
232
  confirm="DELETE_EXPIRED_ARCHIVE_OBJECTS",
223
233
  )
234
+ await namespace.create_schedule(
235
+ schedule_key="demo.hourly",
236
+ app_slug="carebridge",
237
+ env="production",
238
+ target_url="https://demo.example/api/hourly",
239
+ frequency_kind="hourly",
240
+ max_runtime_ms=600000,
241
+ )
242
+ await namespace.update_schedule(
243
+ "demo.hourly",
244
+ enabled=False,
245
+ next_due_at="2026-04-19T00:00:00Z",
246
+ )
247
+ await namespace.claim_due_schedules(limit=5, lock_seconds=900)
248
+ await namespace.complete_schedule_run("demo.hourly", "run-1", result={"ok": True})
249
+ await namespace.fail_schedule_run("demo.hourly", "run-2", error="boom", result={"ok": False})
224
250
 
225
251
  ensure_body = client.calls[0]["body"]
226
252
  assert ensure_body["app_slug"] == "carebridge"
@@ -279,25 +305,50 @@ async def test_authority_scope_methods_use_canonical_context_wire_names():
279
305
  "/api/authority/timelines/archive-status?"
280
306
  "all_scopes=true&app_slug=carebridge&env=production&limit=5"
281
307
  )
282
- assert client.calls[10]["endpoint"] == "/api/authority/timelines/archive-repair"
308
+ assert client.calls[10]["endpoint"] == "/api/authority/timelines/archive"
283
309
  assert client.calls[10]["method"] == "POST"
284
310
  assert client.calls[10]["body"] == {
285
- "since": "2026-04-01T00:00:00Z",
286
- "until": "2026-04-02T00:00:00Z",
311
+ "max_hours": 24,
312
+ "max_runtime_ms": 600000,
287
313
  }
288
- assert client.calls[11]["endpoint"] == "/api/authority/timelines/archive-verify"
314
+ assert client.calls[11]["endpoint"] == "/api/authority/timelines/archive-repair"
315
+ assert client.calls[11]["method"] == "POST"
289
316
  assert client.calls[11]["body"] == {
290
317
  "since": "2026-04-01T00:00:00Z",
291
318
  "until": "2026-04-02T00:00:00Z",
292
319
  }
293
- assert client.calls[12]["endpoint"] == "/api/authority/timelines/archive-prune"
320
+ assert client.calls[12]["endpoint"] == "/api/authority/timelines/archive-verify"
294
321
  assert client.calls[12]["body"] == {
322
+ "since": "2026-04-01T00:00:00Z",
323
+ "until": "2026-04-02T00:00:00Z",
324
+ }
325
+ assert client.calls[13]["endpoint"] == "/api/authority/timelines/archive-prune"
326
+ assert client.calls[13]["body"] == {
295
327
  "since": "2026-01-01T00:00:00Z",
296
328
  "until": "2026-01-02T00:00:00Z",
297
329
  "retention_days": 365,
298
330
  "dry_run": False,
299
331
  "confirm": "DELETE_EXPIRED_ARCHIVE_OBJECTS",
300
332
  }
333
+ assert client.calls[14]["endpoint"] == "/api/authority/schedules"
334
+ assert client.calls[14]["body"] == {
335
+ "schedule_key": "demo.hourly",
336
+ "owner_app_slug": "carebridge",
337
+ "owner_env": "production",
338
+ "target_url": "https://demo.example/api/hourly",
339
+ "frequency_kind": "hourly",
340
+ "max_runtime_ms": 600000,
341
+ }
342
+ assert client.calls[15]["endpoint"] == "/api/authority/schedules/demo.hourly"
343
+ assert client.calls[15]["method"] == "PATCH"
344
+ assert client.calls[15]["body"] == {
345
+ "enabled": False,
346
+ "next_due_at": "2026-04-19T00:00:00Z",
347
+ }
348
+ assert client.calls[16]["endpoint"] == "/api/authority/schedules/claim-due"
349
+ assert client.calls[16]["body"] == {"limit": 5, "lock_seconds": 900}
350
+ assert client.calls[17]["endpoint"] == "/api/authority/schedules/demo.hourly/runs/run-1/complete"
351
+ assert client.calls[18]["endpoint"] == "/api/authority/schedules/demo.hourly/runs/run-2/fail"
301
352
 
302
353
 
303
354
  @pytest.mark.asyncio
@@ -130,6 +130,21 @@ async def test_logs_archive_maintenance_helpers_forward_canonical_body():
130
130
  target_env="production",
131
131
  use_shared=True,
132
132
  )
133
+ await namespace.run_archive_buffer_maintenance(
134
+ dry_run=False,
135
+ confirm="DELETE_ARCHIVE_BUFFER_RESIDUE",
136
+ all_scopes=True,
137
+ max_scopes=1,
138
+ salvage_missing_archive=True,
139
+ sample_limit=500,
140
+ max_archive_buckets=24,
141
+ batch_runs=5,
142
+ stop_when_payload_fields_at_or_below=145000,
143
+ max_runtime_ms=8000,
144
+ target_org_id="org-123",
145
+ target_env="production",
146
+ use_shared=True,
147
+ )
133
148
 
134
149
  assert client.calls[0]["endpoint"] == "/api/logs/archive-repair"
135
150
  assert client.calls[0]["method"] == "POST"
@@ -164,3 +179,20 @@ async def test_logs_archive_maintenance_helpers_forward_canonical_body():
164
179
  "target_env": "production",
165
180
  "use_shared": True,
166
181
  }
182
+ assert client.calls[4]["endpoint"] == "/api/logs/archive-buffer-maintenance"
183
+ assert client.calls[4]["method"] == "POST"
184
+ assert client.calls[4]["body"] == {
185
+ "dry_run": False,
186
+ "confirm": "DELETE_ARCHIVE_BUFFER_RESIDUE",
187
+ "all_scopes": True,
188
+ "max_scopes": 1,
189
+ "salvage_missing_archive": True,
190
+ "sample_limit": 500,
191
+ "max_archive_buckets": 24,
192
+ "batch_runs": 5,
193
+ "stop_when_payload_fields_at_or_below": 145000,
194
+ "max_runtime_ms": 8000,
195
+ "target_org_id": "org-123",
196
+ "target_env": "production",
197
+ "use_shared": True,
198
+ }