This commit is contained in:
nose
2025-12-21 05:10:09 -08:00
parent 8ca5783970
commit 11a13edb84
15 changed files with 1712 additions and 213 deletions

View File

@@ -20,11 +20,54 @@ from __future__ import annotations
import sys
import shlex
from contextlib import contextmanager
from typing import Any, Dict, List, Optional, Sequence
from models import PipelineStageContext
from SYS.logger import log
# Live progress UI instance (optional). Set by the pipeline runner.
_LIVE_PROGRESS: Any = None
def set_live_progress(progress_ui: Any) -> None:
"""Register the current Live progress UI so cmdlets can suspend it during prompts."""
global _LIVE_PROGRESS
_LIVE_PROGRESS = progress_ui
def get_live_progress() -> Any:
return _LIVE_PROGRESS
@contextmanager
def suspend_live_progress():
"""Temporarily pause Live progress rendering.
This avoids Rich Live cursor control interfering with interactive tables/prompts
emitted by cmdlets during preflight (e.g. URL-duplicate confirmation).
"""
ui = _LIVE_PROGRESS
paused = False
try:
if ui is not None and hasattr(ui, "pause"):
try:
ui.pause()
paused = True
except Exception:
paused = False
yield
finally:
# If a stage requested the pipeline stop (e.g. user declined a preflight prompt),
# do not resume Live rendering.
if get_pipeline_stop() is not None:
return
if paused and ui is not None and hasattr(ui, "resume"):
try:
ui.resume()
except Exception:
pass
def _is_selectable_table(table: Any) -> bool:
"""Return True when a table can be used for @ selection."""
@@ -96,6 +139,28 @@ _PENDING_PIPELINE_SOURCE: Optional[str] = None
_UI_LIBRARY_REFRESH_CALLBACK: Optional[Any] = None
# ============================================================================
# PIPELINE STOP SIGNAL
# ============================================================================
_PIPELINE_STOP: Optional[Dict[str, Any]] = None
def request_pipeline_stop(*, reason: str = "", exit_code: int = 0) -> None:
"""Request that the pipeline runner stop gracefully after the current stage."""
global _PIPELINE_STOP
_PIPELINE_STOP = {"reason": str(reason or "").strip(), "exit_code": int(exit_code)}
def get_pipeline_stop() -> Optional[Dict[str, Any]]:
return _PIPELINE_STOP
def clear_pipeline_stop() -> None:
global _PIPELINE_STOP
_PIPELINE_STOP = None
# ============================================================================
# PUBLIC API
# ============================================================================