fix(mobile): wire plugin catalog and session-scoped subagent controls

This commit is contained in:
yilsem committed 2026-10-07 22:37:49 +03:00
1 parent 61d765e2df
commit 1acb5dd90f
27 files changed
+1573 -17

No files matched your search

+21 -3
View File
@@ -564,6 +564,22 @@ def _install_metadata_path() -> Path:
return get_hermes_home() / "plugins" / _INSTALL_METADATA_FILE
def _remove_plugin_tree(path: Path) -> None:
"""Remove one validated plugin tree, including read-only Git objects on Windows."""
import stat
target = path.resolve()
if _plugins_dir().resolve() not in target.parents:
raise ValueError("Plugin cleanup must stay strictly inside the plugin directory")
def retry_readonly(function, failed_path, exc_info):
if os.name != "nt" or not isinstance(exc_info[1], PermissionError):
raise exc_info[1]
os.chmod(failed_path, stat.S_IRUSR | stat.S_IWUSR)
function(failed_path)
shutil.rmtree(path, onerror=retry_readonly)
def _read_install_metadata() -> dict[str, dict[str, object]]:
"""Read profile-local, non-secret plugin source metadata from disk."""
path = _install_metadata_path()
@@ -867,7 +883,7 @@ def _install_plugin_core(
_write_install_metadata(new_metadata)
except Exception:
if target.exists():
shutil.rmtree(target)
_remove_plugin_tree(target)
if replaced_existing and backup.exists():
os.replace(backup, target)
if old_metadata:
@@ -1208,7 +1224,7 @@ def _remove_plugin_core(target: Path) -> None:
"""Remove one plugin and its metadata without splitting their state."""
metadata = _read_install_metadata()
if target.name not in metadata:
shutil.rmtree(target)
_remove_plugin_tree(target)
return
updated = dict(metadata)
@@ -1230,7 +1246,7 @@ def _remove_plugin_core(target: Path) -> None:
) from restore_exc
shutil.rmtree(staging, ignore_errors=True)
raise
shutil.rmtree(staging)
_remove_plugin_tree(staging)
def cmd_remove(name: str) -> None:
@@ -2641,6 +2657,7 @@ def dashboard_install_plugin(
*,
force: bool,
enable: bool,
ref: Optional[str] = None,
) -> dict[str, Any]:
"""Non-interactive install for the web dashboard. Returns a JSON-serializable dict."""
warnings: list[str] = []
@@ -2657,6 +2674,7 @@ def dashboard_install_plugin(
target, installed_manifest, installed_name = _install_plugin_core(
identifier,
force=force,
ref=ref,
)
except PluginScanBlocked as exc:
findings = []