mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-11-06 13:26:40 +00:00
The Singularity/Apptainer backend (tools/environments/singularity.py) now ships as the official 'singularity' plugin in NousResearch/hermes-official-plugins, registered through the terminal-environment provider seam under the same backend name and config keys (terminal.backend: singularity, terminal.singularity_image / TERMINAL_SINGULARITY_IMAGE, terminal.container_*). - 'singularity' leaves BUILTIN_BACKEND_NAMES so the plugin can register it; every hardcoded classification set drops it (the provider declares is_remote/is_container/supports_streams instead). - LEFT_CORE gains a singularity row: homes whose terminal.backend (or .env TERMINAL_ENV) is singularity get the catalog plugin once, on hermes update or agent/gateway start. An unknown-backend error for 'singularity' names the install command (new row field terminal_backend). - A plugin backend receives its per-task '<backend>_image' override; batch runs register one for the active backend. - BaseEnvironment's sudo-probe knob is renamed _passwordless_sudo_probe_supported: plugin backends set it, and the old spelling trips the install scanner. - Core's scratch-dir disk warning and process-exit sweep were Singularity-only and move to the plugin; the singularity setup row, dashboard probe/row and config defaults/bridges go too. Docs point at the plugin.
35 lines
1.5 KiB
Python
35 lines
1.5 KiB
Python
"""Path-component helpers shared by execution environment backends. Kept separate from the
|
|
base class so lazy backend imports do not depend on newly added exports from a large module
|
|
cached earlier in a long-lived process."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import hashlib
|
|
import re
|
|
|
|
|
|
# A persistent sandbox's host directory is named after task_id, and that name
|
|
# then becomes the source half of a Docker bind spec or a plugin backend's
|
|
# writable overlay directory (singularity). Keep every backend on one collision-safe mapping.
|
|
_SANDBOX_DIR_UNSAFE_RE = re.compile(r"[^A-Za-z0-9._-]")
|
|
_SANDBOX_DIR_MAX_LEN = 128
|
|
_SANDBOX_DIR_HASH_LEN = 12
|
|
|
|
|
|
def sanitize_task_id_for_path(task_id: str) -> str:
|
|
"""Bind-mountable directory name for *task_id*'s sandbox. Already-safe names are returned
|
|
verbatim (preserving existing sandbox locations); rewritten names carry a digest because
|
|
substitution alone is not injective: ``a:b`` and ``a_b`` must not share state."""
|
|
value = task_id if isinstance(task_id, str) else ""
|
|
if not value:
|
|
return "default"
|
|
|
|
cleaned = _SANDBOX_DIR_UNSAFE_RE.sub("_", value)
|
|
if (cleaned == value and len(value) <= _SANDBOX_DIR_MAX_LEN
|
|
and value not in {".", ".."} and not value.endswith((".", " "))):
|
|
return value
|
|
|
|
digest = hashlib.sha256(value.encode("utf-8")).hexdigest()[:_SANDBOX_DIR_HASH_LEN]
|
|
stem = cleaned[: _SANDBOX_DIR_MAX_LEN - _SANDBOX_DIR_HASH_LEN - 1].strip("._")
|
|
return f"{stem or 'task'}-{digest}"
|