compat(plugins): temporary import-path shims for external plugins — ONE commit, revert on schedule
The Sep 2026 decomposition (PR #102117) makes internal import paths a non-API: names now live in the focused modules that define them. This commit is the ONLY thing keeping the old paths alive, so external plugins have time to update. It is deliberately a single, unsquashed commit: git revert <this sha> removes every shim, stub and manifest at once on the announced date. Nothing in-tree may depend on these pointers: scripts/check_compat_pointers.py (wired into lint.yml) fails CI if it does. What it adds (see COMPAT_MANIFEST.md, compat_manifest.json): - 332 facade modules get one delimited `PLUGIN-COMPAT` block appended at the end of the file - 1,172 moved names resolved lazily via a module `__getattr__` (PEP 562) — never a top-level import, so no import cycles; facades that already had `__getattr__` get a chained one - 592 third-party/stdlib names the old modules used to expose, with their original import statements - 266 public definitions that had been deleted as unused, restored byte-for-byte from the pre-decomposition tree (+40 private helpers and 16 imports pulled in only because a restored definition needs them) - 3 deleted modules recreated as re-export stubs (gateway/startup_watchdog, hermes_cli/observability/ relay_runtime, tools/environments/modal_utils) - private names (`_x`) get no pointer: they were never API (3,792 skipped) Verified: all 335 touched modules import under a fresh HERMES_HOME and every manifest name resolves; the lint reports zero in-tree uses; ruff clean; targeted suites unchanged.
This commit is contained in:
@@ -92,3 +92,126 @@ def prompt_for_secret(cli, var_name: str, prompt: str, metadata=None) -> dict:
|
||||
_invalidate(cli)
|
||||
cprint(f"\n{_DIM} ⏱ Timeout — secret capture cancelled{_RST}")
|
||||
return _skipped(var_name, "timeout", "Secret setup timed out and was skipped.")
|
||||
|
||||
|
||||
# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ----
|
||||
# Names external plugins imported from this module before the Sep 2026 decomposition.
|
||||
# Internal code MUST NOT use these (scripts/check_compat_pointers.py fails CI if it does).
|
||||
# The whole block is removed by reverting the commit that added it.
|
||||
|
||||
def approval_callback(cli, command: str, description: str) -> str:
|
||||
"""Prompt for dangerous command approval through the TUI.
|
||||
|
||||
Shows a selection UI with choices: once / session / always / deny.
|
||||
When the command is longer than 70 characters, a "view" option is
|
||||
included so the user can reveal the full text before deciding.
|
||||
|
||||
Uses cli._approval_lock to serialize concurrent requests (e.g. from
|
||||
parallel delegation subtasks) so each prompt gets its own turn.
|
||||
"""
|
||||
lock = getattr(cli, "_approval_lock", None)
|
||||
if lock is None:
|
||||
import threading
|
||||
cli._approval_lock = threading.Lock()
|
||||
lock = cli._approval_lock
|
||||
|
||||
with lock:
|
||||
from cli import CLI_CONFIG
|
||||
timeout = CLI_CONFIG.get("approvals", {}).get("timeout", 300)
|
||||
response_queue = queue.Queue()
|
||||
choices = ["once", "session", "always", "deny"]
|
||||
if len(command) > 70:
|
||||
choices.append("view")
|
||||
|
||||
cli._approval_state = {
|
||||
"command": command,
|
||||
"description": description,
|
||||
"choices": choices,
|
||||
"selected": 0,
|
||||
"response_queue": response_queue,
|
||||
}
|
||||
cli._approval_deadline = _time.monotonic() + timeout
|
||||
|
||||
if hasattr(cli, "_app") and cli._app:
|
||||
cli._app.invalidate()
|
||||
|
||||
while True:
|
||||
try:
|
||||
result = response_queue.get(timeout=1)
|
||||
cli._approval_state = None
|
||||
cli._approval_deadline = 0
|
||||
if hasattr(cli, "_app") and cli._app:
|
||||
cli._app.invalidate()
|
||||
return result
|
||||
except queue.Empty:
|
||||
remaining = cli._approval_deadline - _time.monotonic()
|
||||
if remaining <= 0:
|
||||
break
|
||||
if hasattr(cli, "_app") and cli._app:
|
||||
cli._app.invalidate()
|
||||
|
||||
cli._approval_state = None
|
||||
cli._approval_deadline = 0
|
||||
if hasattr(cli, "_app") and cli._app:
|
||||
cli._app.invalidate()
|
||||
cprint(f"\n{_DIM} ⏱ Timeout — denying command{_RST}")
|
||||
return "timeout"
|
||||
|
||||
def clarify_callback(cli, question, choices, multi_select=False):
|
||||
"""Prompt for clarifying question through the TUI.
|
||||
|
||||
Sets up the interactive selection UI, then blocks until the user
|
||||
responds. Returns the user's choice or a timeout message.
|
||||
|
||||
When ``multi_select`` is True, shows checkboxes and the user can
|
||||
select multiple options with Space, confirming with Enter.
|
||||
"""
|
||||
from cli import CLI_CONFIG
|
||||
from tools.clarify_gateway import resolve_clarify_timeout
|
||||
|
||||
# Canonical clarify timeout, shared with the gateway/TUI path. `<= 0`
|
||||
# means unlimited (never auto-skip mid-think) → a null deadline.
|
||||
timeout = resolve_clarify_timeout(CLI_CONFIG)
|
||||
response_queue = queue.Queue()
|
||||
is_open_ended = not choices
|
||||
effective_multi = multi_select and not is_open_ended
|
||||
|
||||
cli._clarify_state = {
|
||||
"question": question,
|
||||
"choices": choices if not is_open_ended else [],
|
||||
"selected": 0,
|
||||
"multi_select": effective_multi,
|
||||
"selected_indices": set() if effective_multi else None,
|
||||
"response_queue": response_queue,
|
||||
}
|
||||
cli._clarify_deadline = None if timeout <= 0 else _time.monotonic() + timeout
|
||||
cli._clarify_freetext = is_open_ended
|
||||
|
||||
if hasattr(cli, "_app") and cli._app:
|
||||
cli._app.invalidate()
|
||||
|
||||
while True:
|
||||
try:
|
||||
result = response_queue.get(timeout=1)
|
||||
cli._clarify_deadline = None
|
||||
return result
|
||||
except queue.Empty:
|
||||
# None deadline = unlimited: never auto-skip, just keep polling.
|
||||
if cli._clarify_deadline is not None:
|
||||
remaining = cli._clarify_deadline - _time.monotonic()
|
||||
if remaining <= 0:
|
||||
break
|
||||
if hasattr(cli, "_app") and cli._app:
|
||||
cli._app.invalidate()
|
||||
|
||||
cli._clarify_state = None
|
||||
cli._clarify_freetext = False
|
||||
cli._clarify_deadline = None
|
||||
if hasattr(cli, "_app") and cli._app:
|
||||
cli._app.invalidate()
|
||||
cprint(f"\n{_DIM}(clarify timed out after {timeout}s — agent will decide){_RST}")
|
||||
return (
|
||||
"The user did not provide a response within the time limit. "
|
||||
"Use your best judgement to make the choice and proceed."
|
||||
)
|
||||
# ---- END PLUGIN-COMPAT ----
|
||||
|
||||
Reference in New Issue
Block a user