dominus-sdk-python-dev 2.3.0__py3-none-any.whl → 2.5.0__py3-none-any.whl

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.
@@ -0,0 +1,276 @@
1
+ """
2
+ VADGate - Voice Activity Detection Gate (INTERNAL)
3
+
4
+ Implements a 4-state machine for VAD gating:
5
+ IDLE -> ARMED -> SPEAKING -> TRAILING -> IDLE
6
+
7
+ Features:
8
+ - Pre-roll buffer (320ms) to capture word onsets
9
+ - Armed confirmation (160ms) to reject false triggers
10
+ - Post-roll/trailing (800ms) to avoid chopped endings
11
+ - Circular buffer for pre-roll storage
12
+
13
+ This module is INTERNAL and should NOT be exported publicly.
14
+ """
15
+
16
+ import asyncio
17
+ import struct
18
+ import math
19
+ from typing import List, Callable, Optional
20
+ from dataclasses import dataclass
21
+
22
+ from .types import VADState, ResolvedOracleSessionOptions
23
+
24
+
25
+ @dataclass
26
+ class VADGateConfig:
27
+ """Configuration for VADGate."""
28
+
29
+ # Number of frames in pre-roll buffer
30
+ preroll_frames: int
31
+
32
+ # Number of frames to confirm armed state
33
+ armed_confirm_frames: int
34
+
35
+ # Number of consecutive silence frames to confirm trailing (hysteresis)
36
+ silence_confirm_frames: int
37
+
38
+ # Post-roll timeout in ms
39
+ postroll_ms: int
40
+
41
+ # VAD threshold (0-1)
42
+ threshold: float
43
+
44
+ # Energy threshold for fallback VAD
45
+ energy_threshold: int
46
+
47
+
48
+ def detect_speech_by_energy(pcm_frame: bytes, threshold: int = 500) -> bool:
49
+ """
50
+ Energy-based speech detection fallback.
51
+ Calculates RMS energy and compares against threshold.
52
+
53
+ Args:
54
+ pcm_frame: Raw PCM16 audio data
55
+ threshold: RMS energy threshold
56
+
57
+ Returns:
58
+ True if speech detected, False otherwise
59
+ """
60
+ # Convert bytes to int16 samples
61
+ num_samples = len(pcm_frame) // 2
62
+ if num_samples == 0:
63
+ return False
64
+
65
+ samples = struct.unpack(f"<{num_samples}h", pcm_frame)
66
+
67
+ # Calculate RMS energy
68
+ sum_squares = sum(s * s for s in samples)
69
+ rms = math.sqrt(sum_squares / num_samples)
70
+
71
+ return rms > threshold
72
+
73
+
74
+ class EnergyVAD:
75
+ """
76
+ Energy-based VAD fallback.
77
+ Uses RMS energy detection when webrtcvad is unavailable.
78
+ """
79
+
80
+ def __init__(self, energy_threshold: int):
81
+ self.energy_threshold = energy_threshold
82
+
83
+ def is_speech(self, pcm_frame: bytes, _threshold: float) -> bool:
84
+ return detect_speech_by_energy(pcm_frame, self.energy_threshold)
85
+
86
+ def dispose(self) -> None:
87
+ pass
88
+
89
+
90
+ class WebRTCVAD:
91
+ """
92
+ WebRTC VAD wrapper.
93
+ Falls back to energy-based detection if webrtcvad is unavailable.
94
+ """
95
+
96
+ def __init__(self, threshold: float, energy_threshold: int, sample_rate: int = 16000):
97
+ self.threshold = threshold
98
+ self.sample_rate = sample_rate
99
+ self.fallback = EnergyVAD(energy_threshold)
100
+ self.vad = None
101
+ self.use_fallback = False
102
+
103
+ try:
104
+ import webrtcvad
105
+ self.vad = webrtcvad.Vad()
106
+ # Aggressiveness level 0-3 (3 = most aggressive filtering)
107
+ # Map threshold (0-1) to aggressiveness (0-3)
108
+ aggressiveness = max(0, min(3, int((1 - threshold) * 3)))
109
+ self.vad.set_mode(aggressiveness)
110
+ except ImportError:
111
+ print("[OracleSDK] webrtcvad not available, using energy-based fallback")
112
+ self.use_fallback = True
113
+
114
+ def is_speech(self, pcm_frame: bytes, threshold: float) -> bool:
115
+ if self.use_fallback or self.vad is None:
116
+ return self.fallback.is_speech(pcm_frame, threshold)
117
+
118
+ try:
119
+ # webrtcvad requires specific frame sizes: 10, 20, or 30 ms
120
+ # For 16kHz, 20ms = 320 samples = 640 bytes
121
+ return self.vad.is_speech(pcm_frame, self.sample_rate)
122
+ except Exception:
123
+ # Fall back to energy-based on any error
124
+ return self.fallback.is_speech(pcm_frame, threshold)
125
+
126
+ def dispose(self) -> None:
127
+ self.vad = None
128
+
129
+
130
+ class VADGate:
131
+ """
132
+ VADGate - 4-state Voice Activity Detection machine.
133
+
134
+ State transitions:
135
+ - IDLE -> ARMED: When VAD detects speech
136
+ - ARMED -> SPEAKING: After armed_confirm_frames of continuous speech
137
+ - ARMED -> IDLE: If speech stops before confirmation
138
+ - SPEAKING -> TRAILING: When VAD detects silence
139
+ - TRAILING -> SPEAKING: If speech resumes
140
+ - TRAILING -> IDLE: After postroll_ms timeout
141
+ """
142
+
143
+ def __init__(
144
+ self,
145
+ options: ResolvedOracleSessionOptions,
146
+ frame_duration_ms: int = 20,
147
+ loop: Optional[asyncio.AbstractEventLoop] = None
148
+ ):
149
+ self._state: VADState = "idle"
150
+ self._pre_roll_buffer: List[bytes] = []
151
+ self._armed_frame_count = 0
152
+ self._silence_frame_count = 0
153
+ self._trailing_task: Optional[asyncio.Task] = None
154
+ self._loop = loop
155
+
156
+ self._config = VADGateConfig(
157
+ preroll_frames=math.ceil(options.preroll_ms / frame_duration_ms),
158
+ armed_confirm_frames=math.ceil(options.armed_confirm_ms / frame_duration_ms),
159
+ silence_confirm_frames=8, # 160ms of silence before trailing
160
+ postroll_ms=options.postroll_ms,
161
+ threshold=options.vad_threshold,
162
+ energy_threshold=options.energy_threshold,
163
+ )
164
+
165
+ # Use energy-based VAD as the primary implementation
166
+ self._vad_model = EnergyVAD(self._config.energy_threshold)
167
+
168
+ # Callbacks
169
+ self.on_send_audio: Optional[Callable[[List[bytes]], None]] = None
170
+ self.on_state_change: Optional[Callable[[VADState], None]] = None
171
+
172
+ @property
173
+ def state(self) -> VADState:
174
+ """Get current VAD state."""
175
+ return self._state
176
+
177
+ async def initialize(self) -> None:
178
+ """Initialize the VAD model (call before processing frames)."""
179
+ # Energy VAD doesn't need initialization
180
+ pass
181
+
182
+ def process_frame(self, pcm_frame: bytes) -> None:
183
+ """
184
+ Process an audio frame through the VAD gate.
185
+
186
+ Args:
187
+ pcm_frame: 20ms PCM16 audio frame (640 bytes)
188
+ """
189
+ # Maintain circular pre-roll buffer
190
+ self._pre_roll_buffer.append(pcm_frame)
191
+ if len(self._pre_roll_buffer) > self._config.preroll_frames:
192
+ self._pre_roll_buffer.pop(0)
193
+
194
+ is_speech = self._vad_model.is_speech(pcm_frame, self._config.threshold)
195
+
196
+ if self._state == "idle":
197
+ if is_speech:
198
+ self._set_state("armed")
199
+ self._armed_frame_count = 1
200
+
201
+ elif self._state == "armed":
202
+ if is_speech:
203
+ self._armed_frame_count += 1
204
+ if self._armed_frame_count >= self._config.armed_confirm_frames:
205
+ # Confirmed speech - flush pre-roll buffer
206
+ if self.on_send_audio:
207
+ self.on_send_audio(list(self._pre_roll_buffer))
208
+ self._set_state("speaking")
209
+ else:
210
+ # False trigger - back to idle
211
+ self._set_state("idle")
212
+ self._armed_frame_count = 0
213
+
214
+ elif self._state == "speaking":
215
+ if self.on_send_audio:
216
+ self.on_send_audio([pcm_frame])
217
+
218
+ if not is_speech:
219
+ # Hysteresis: require multiple consecutive silence frames
220
+ self._silence_frame_count += 1
221
+ if self._silence_frame_count >= self._config.silence_confirm_frames:
222
+ self._set_state("trailing")
223
+ self._start_trailing_timer()
224
+ self._silence_frame_count = 0
225
+ else:
226
+ # Speech detected - reset silence counter
227
+ self._silence_frame_count = 0
228
+
229
+ elif self._state == "trailing":
230
+ if self.on_send_audio:
231
+ self.on_send_audio([pcm_frame])
232
+
233
+ if is_speech:
234
+ self._cancel_trailing_timer()
235
+ self._set_state("speaking")
236
+
237
+ def reset(self) -> None:
238
+ """Reset VAD state to idle."""
239
+ self._cancel_trailing_timer()
240
+ self._state = "idle"
241
+ self._pre_roll_buffer = []
242
+ self._armed_frame_count = 0
243
+ self._silence_frame_count = 0
244
+
245
+ def dispose(self) -> None:
246
+ """Clean up resources."""
247
+ self._cancel_trailing_timer()
248
+ self._vad_model.dispose()
249
+ self.on_send_audio = None
250
+ self.on_state_change = None
251
+
252
+ def _set_state(self, new_state: VADState) -> None:
253
+ """Update VAD state and fire callback."""
254
+ if self._state != new_state:
255
+ self._state = new_state
256
+ if self.on_state_change:
257
+ self.on_state_change(new_state)
258
+
259
+ def _start_trailing_timer(self) -> None:
260
+ """
261
+ Start the trailing timeout.
262
+ After postroll_ms, transition back to idle.
263
+ """
264
+ async def trailing_timeout():
265
+ await asyncio.sleep(self._config.postroll_ms / 1000.0)
266
+ self._set_state("idle")
267
+ self._trailing_task = None
268
+
269
+ loop = self._loop or asyncio.get_event_loop()
270
+ self._trailing_task = loop.create_task(trailing_timeout())
271
+
272
+ def _cancel_trailing_timer(self) -> None:
273
+ """Cancel the trailing timeout."""
274
+ if self._trailing_task is not None:
275
+ self._trailing_task.cancel()
276
+ self._trailing_task = None
dominus/start.py CHANGED
@@ -251,31 +251,6 @@ class SecureNamespace:
251
251
  )
252
252
 
253
253
 
254
- class OpenNamespace:
255
- """Open user namespace - Returns DSN string for dominus_open database."""
256
-
257
- def __init__(self, _execute_command):
258
- self._execute = _execute_command
259
-
260
- async def dsn(self) -> str:
261
- """
262
- Get the full DSN connection string for open_user role.
263
-
264
- Returns the complete PostgreSQL connection URI for the dominus_open
265
- database that can be used directly by clients to connect.
266
- """
267
- return await self._execute("sql.open.get_dsn")
268
-
269
-
270
- class HealthNamespace:
271
- """Health check operations namespace."""
272
-
273
- def __init__(self, _execute_command):
274
- self._execute = _execute_command
275
-
276
- async def check(self) -> Dict[str, Any]:
277
- """Check health of all services."""
278
- return await self._execute("health.check")
279
254
 
280
255
 
281
256
  class Dominus:
@@ -324,10 +299,8 @@ class Dominus:
324
299
 
325
300
  self._execute = _execute_command
326
301
 
327
- # Initialize minimal namespaces
302
+ # Initialize legacy flat API namespaces (backward compatibility)
328
303
  self.secure = SecureNamespace(_execute_command)
329
- self.open = OpenNamespace(_execute_command)
330
- self.health = HealthNamespace(_execute_command)
331
304
 
332
305
  # Initialize portal namespace (needs client reference for _request)
333
306
  from .namespaces.portal import PortalNamespace
@@ -337,24 +310,45 @@ class Dominus:
337
310
  from .namespaces.fastapi import FastAPINamespace
338
311
  self.fastapi = FastAPINamespace(self)
339
312
 
340
- # Initialize full namespaces (matching Node.js SDK pattern)
313
+ # Initialize all namespaces (matching Node.js SDK pattern)
341
314
  from .namespaces.db import DbNamespace
342
315
  from .namespaces.auth import AuthNamespace
343
316
  from .namespaces.ddl import DdlNamespace
344
317
  from .namespaces.files import FilesNamespace
345
318
  from .namespaces.admin import AdminNamespace
346
319
  from .namespaces.secure import SecureNamespace as SecureNS
347
-
320
+ from .namespaces.secrets import SecretsNamespace
321
+ from .namespaces.redis import RedisNamespace
322
+ from .namespaces.logs import LogsNamespace
323
+ from .namespaces.courier import CourierNamespace
324
+ from .namespaces.health import HealthNamespace
325
+ from .namespaces.open import OpenNamespace
326
+
327
+ # Core namespaces
348
328
  self.db = DbNamespace(self)
349
329
  self.auth = AuthNamespace(self)
350
330
  self.ddl = DdlNamespace(self)
351
331
  self.files = FilesNamespace(self)
352
332
  self.admin = AdminNamespace(self)
353
333
 
354
- # Note: self.secure is already defined above as the flat API namespace
355
- # The proper SecureNS namespace is available as dominus.db_secure for direct access
334
+ # Additional namespaces (Node.js SDK parity)
335
+ self.secrets = SecretsNamespace(self)
336
+ self.redis = RedisNamespace(self)
337
+ self.logs = LogsNamespace(self)
338
+ self.courier = CourierNamespace(self)
339
+ self.health = HealthNamespace(self)
340
+ self.open = OpenNamespace(self)
341
+
342
+ # Secure namespace with proper audit context support
356
343
  self.db_secure = SecureNS(self)
357
344
 
345
+ # Oracle namespace (Speech-to-Text streaming)
346
+ from .namespaces.oracle import OracleNamespace
347
+ self.oracle = OracleNamespace(self)
348
+
349
+ # Alias for Node.js SDK parity (stt = oracle)
350
+ self.stt = self.oracle
351
+
358
352
  # Cache for JWT public key
359
353
  self._public_key_cache = None
360
354
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dominus-sdk-python-dev
3
- Version: 2.3.0
3
+ Version: 2.5.0
4
4
  Summary: Python SDK for the Dominus Orchestrator Platform
5
5
  Author-email: CareBridge Systems <dev@carebridge.io>
6
6
  License: Proprietary
@@ -25,9 +25,20 @@ Requires-Dist: bcrypt>=4.0.0
25
25
  Requires-Dist: cryptography>=41.0.0
26
26
  Provides-Extra: jwt
27
27
  Requires-Dist: PyJWT>=2.8.0; extra == "jwt"
28
+ Provides-Extra: oracle
29
+ Requires-Dist: websockets>=12.0; extra == "oracle"
30
+ Requires-Dist: sounddevice>=0.4.6; extra == "oracle"
31
+ Requires-Dist: numpy>=1.24.0; extra == "oracle"
32
+ Requires-Dist: webrtcvad>=2.0.10; extra == "oracle"
28
33
  Provides-Extra: dev
29
34
  Requires-Dist: pytest>=7.0.0; extra == "dev"
30
35
  Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
36
+ Provides-Extra: all
37
+ Requires-Dist: PyJWT>=2.8.0; extra == "all"
38
+ Requires-Dist: websockets>=12.0; extra == "all"
39
+ Requires-Dist: sounddevice>=0.4.6; extra == "all"
40
+ Requires-Dist: numpy>=1.24.0; extra == "all"
41
+ Requires-Dist: webrtcvad>=2.0.10; extra == "all"
31
42
 
32
43
  # CB Dominus SDK for Python
33
44
 
@@ -417,8 +428,8 @@ from dominus import (
417
428
  ConflictError,
418
429
  ServiceError,
419
430
  SecureTableError,
420
- ConnectionError,
421
- TimeoutError,
431
+ DominusConnectionError, # Renamed to avoid shadowing built-in
432
+ DominusTimeoutError, # Renamed to avoid shadowing built-in
422
433
  )
423
434
 
424
435
  try:
@@ -445,6 +456,7 @@ except DominusError as e:
445
456
 
446
457
  | Error | Status | Description |
447
458
  |-------|--------|-------------|
459
+ | `DominusError` | - | Base class for all SDK errors |
448
460
  | `AuthenticationError` | 401 | Invalid or missing token |
449
461
  | `AuthorizationError` | 403 | Insufficient permissions |
450
462
  | `NotFoundError` | 404 | Resource not found |
@@ -452,8 +464,8 @@ except DominusError as e:
452
464
  | `ConflictError` | 409 | Duplicate or version conflict |
453
465
  | `ServiceError` | 5xx | Backend service error |
454
466
  | `SecureTableError` | 403 | Missing reason for secure table |
455
- | `ConnectionError` | - | Network connection failed |
456
- | `TimeoutError` | 504 | Request timed out |
467
+ | `DominusConnectionError` | - | Network connection failed |
468
+ | `DominusTimeoutError` | 504 | Request timed out |
457
469
 
458
470
  ## Configuration
459
471
 
@@ -551,10 +563,13 @@ async def get_all_users(page_size: int = 100):
551
563
 
552
564
  ## Version
553
565
 
554
- **v2.1.6** - Latest release with navigation routes and page scope methods
566
+ **v2.4.0** - Full namespace parity with Node.js SDK
555
567
 
556
568
  ### Changelog
557
569
 
570
+ - **v2.4.0** - Full parity with Node.js SDK: all namespaces properly initialized, error classes exported, cache utilities exposed
571
+ - **v2.3.0** - Add comprehensive auth namespace with 100+ methods
572
+ - **v2.2.0** - Add DDL schema builder and migration methods
558
573
  - **v2.1.6** - Fix navigation routes and add page scope methods
559
574
  - **v2.1.5** - Add PSK-only client login
560
575
  - **v2.1.4** - Make `tenant_id` optional in login methods
@@ -1,16 +1,14 @@
1
- dominus/__init__.py,sha256=-VkTYvkoJS7ltZr6A3qOfqqU1ueKkYv3dhznfGnJ3oY,2990
1
+ dominus/__init__.py,sha256=fBZgMu7J7o7DCUNH9PStQ873LW5n9IREXsuu6EsXO70,5132
2
2
  dominus/errors.py,sha256=Sfpg_4zlraE6fprdSu7RDNmSDv0Eg5OZRcPhNUQhNGs,5761
3
- dominus/start.py,sha256=mzBglBZ38jPPuHtLGJ7IIB5r_bnMR4_a5i4fZbyudQ8,32823
3
+ dominus/start.py,sha256=F_Z9qu8m5YLKk9uCeM9S2vnSfArDJVojoi6MDe8pNqE,32743
4
4
  dominus/config/__init__.py,sha256=nvLLNh4dM_MCqcfF3XFS2NnVn44q0RYHEfAPP-2G5To,434
5
5
  dominus/config/endpoints.py,sha256=fsV5c7vaicpJlF735o46FP4Xlu2Ap4rSvnNa4F0TOYg,1574
6
6
  dominus/helpers/__init__.py,sha256=cyoBvw1QJugtGt0gYitIz1yxOaMmBa-wMXqbh-fi4PU,61
7
7
  dominus/helpers/auth.py,sha256=IVZLelikaGIAZvyc-q7EoPd8fBNVlGffGXQkTP4x5Fk,633
8
- dominus/helpers/cache.py,sha256=U09xylQipotRUNdB3zsQRcjmeYgKzu639P-ucNgks5M,5721
8
+ dominus/helpers/cache.py,sha256=RgCTPLmHoOXGkxF-pLP9buKPkvZDYHtsLUxbTFjheJI,6028
9
9
  dominus/helpers/core.py,sha256=DeCo2sCmXJLnitJSDzRKcQGwYXyQOw1KRvzxDI6DQsA,24174
10
10
  dominus/helpers/crypto.py,sha256=XvP9ud5jJ9KPvIQmtAOeXZS4WUyCNb8X-RL_eOB7WNE,2890
11
11
  dominus/namespaces/__init__.py,sha256=Kh7tw-5LmY-CpZL2BdqbHc93QLjxTOCGLUVPwAqqDnE,659
12
- dominus/namespaces/_deprecated_crossover.py,sha256=NY73vatvc4CDgML9QMqmvoEjRr_t-Vq6FHtbby0M2-M,385
13
- dominus/namespaces/_deprecated_sql.py,sha256=U57p5hGFbsPEcNEl12m7tYTWux7j2rcCHbCjmNMJ8oY,38318
14
12
  dominus/namespaces/admin.py,sha256=qkGoteAJ6cqdNub4zL1mtyxAQtsM3-nUplRSgw2fMZY,1640
15
13
  dominus/namespaces/auth.py,sha256=Nqy4EOA1qMINvVuvf2-RnKPwN6TyLiD3vmz1G4J7TQg,55462
16
14
  dominus/namespaces/courier.py,sha256=WAhHpswhO8S_eEzn5zgBGhDqhgGxfDR_gs4i1rnotNw,6810
@@ -25,10 +23,14 @@ dominus/namespaces/portal.py,sha256=6T0ul2spnhcDORh1JN2v7zDbkdWqk0FblWKFIeUjSsA,
25
23
  dominus/namespaces/redis.py,sha256=WctuQdUddifDiato6QHntv1ulvAQVVC01iq_k2WUh9A,9384
26
24
  dominus/namespaces/secrets.py,sha256=6xhUJVeDBb5-AiwrwjozarwAc7SoiJ-2Rq6ByBAx684,3572
27
25
  dominus/namespaces/secure.py,sha256=9idGY5R6k7b-JM1SAF_1Gt-YV5OempyxFBflgLJieLo,7168
26
+ dominus/namespaces/oracle/__init__.py,sha256=XiJ8P-vFln73p4r1_ZXgmNQvawkNUt5htuGD51JCoBA,4584
27
+ dominus/namespaces/oracle/audio_capture.py,sha256=1-QcoAHYT1idEBGAJ7g4qpu5QeKdmwiyWciB6K0WkNM,11201
28
+ dominus/namespaces/oracle/oracle_websocket.py,sha256=VsKKPTZmtdchoXj9bFel3hrWUVELS-5UD7LNFWlDdfg,7540
29
+ dominus/namespaces/oracle/session.py,sha256=1As6q-bs0Ynoz3fsNk18E7CuouPROnVadCY_HeHRTkw,7998
30
+ dominus/namespaces/oracle/types.py,sha256=YGwb-PfKikRGNk4af5Av2DD9YPLJpogM21Co9X4T_cU,3517
31
+ dominus/namespaces/oracle/vad_gate.py,sha256=Gpb5q72-MSGYm4PVK64E4w6kawx5AMb1SsJ2uOJrtFw,9010
28
32
  dominus/services/__init__.py,sha256=LQl5sx6DHhX1xCN3MRoUgffxqwm3Mfm525hijyFods8,43
29
- dominus/services/_deprecated_architect.py,sha256=EAMhUK8C_4DLgYyKGVqcXrroEoAenWeuKWnPMowGMQk,14985
30
- dominus/services/_deprecated_sovereign.py,sha256=is9SQVCcmcGUEFKULqitHlk67pfpfkQE-ApU-gVjIEE,3511
31
- dominus_sdk_python_dev-2.3.0.dist-info/METADATA,sha256=zb7sLkwxGc2bPKH7WE3j_yYPJAxmQqRXcAQw9js_svU,16520
32
- dominus_sdk_python_dev-2.3.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
33
- dominus_sdk_python_dev-2.3.0.dist-info/top_level.txt,sha256=515zxMIbX0DpheRbjvNqKIt_AFqdFjX41jtyp_SqLf4,8
34
- dominus_sdk_python_dev-2.3.0.dist-info/RECORD,,
33
+ dominus_sdk_python_dev-2.5.0.dist-info/METADATA,sha256=j4BWCGfmmcMqgtDbt9VHq-oBzmARLshuRIdCr27G-aM,17401
34
+ dominus_sdk_python_dev-2.5.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
35
+ dominus_sdk_python_dev-2.5.0.dist-info/top_level.txt,sha256=515zxMIbX0DpheRbjvNqKIt_AFqdFjX41jtyp_SqLf4,8
36
+ dominus_sdk_python_dev-2.5.0.dist-info/RECORD,,
@@ -1,10 +0,0 @@
1
- """Crossover (admin) operations namespace - DEPRECATED
2
-
3
- Note: This namespace has been removed from the SDK.
4
- Provisioning and admin operations should be done via Architect's
5
- /api/crossover endpoint directly, not through the SDK.
6
-
7
- This file is kept as a placeholder to prevent import errors in
8
- any code that may still reference it.
9
- """
10
- # Empty file - all crossover functionality removed