(self)
| 101 | # -- lifecycle --------------------------------------------------------- |
| 102 | |
| 103 | async def _ensure_started(self) -> asyncio.subprocess.Process: |
| 104 | if self._proc is not None: |
| 105 | return self._proc |
| 106 | async with self._start_lock: |
| 107 | if self._proc is not None: |
| 108 | return self._proc |
| 109 | env = { |
| 110 | **os.environ, |
| 111 | "CMDOP_TOKEN": self._cfg.token, |
| 112 | "CMDOP_BASE_URL": self._cfg.base_url, |
| 113 | # Django platform plane (skills) — separate credential + base URL. |
| 114 | "CMDOP_API_BASE_URL": self._cfg.api_base_url, |
| 115 | } |
| 116 | if self._cfg.api_key: |
| 117 | env["CMDOP_API_KEY"] = self._cfg.api_key |
| 118 | proc = await asyncio.create_subprocess_exec( |
| 119 | self._bin, |
| 120 | "--stdio", |
| 121 | stdin=asyncio.subprocess.PIPE, |
| 122 | stdout=asyncio.subprocess.PIPE, |
| 123 | stderr=asyncio.subprocess.PIPE, |
| 124 | env=env, |
| 125 | ) |
| 126 | self._proc = proc |
| 127 | self._reader_task = asyncio.create_task(self._read_loop()) |
| 128 | self._stderr_task = asyncio.create_task(self._drain_stderr()) |
| 129 | return proc |
| 130 | |
| 131 | async def _drain_stderr(self) -> None: |
| 132 | """Drain the core's stderr so its diagnostics never block / corrupt the |
no test coverage detected