feat: expose terminal.temp_dir to redirect session temp root off tmpfs
Some Linux distros (notably RAM-based tmpfs /tmp on several Arch-based setups) cap the temp directory at a small size, so Hermes runs out of space for session temp files (background logs/pid/exit files, code- execution sandboxes). Add a terminal.temp_dir config key that points these at real storage. - Add terminal.temp_dir default (empty) in config_defaults.py - Bridge it to TERMINAL_TEMP_DIR via TERMINAL_CONFIG_ENV_MAP - Honor TERMINAL_TEMP_DIR first in LocalEnvironment.get_temp_dir(), falling through to TMPDIR//tmp//gettempdir when unset/invalid - Add tests covering override, process-env, missing-dir, and empty
This commit is contained in:
@@ -3640,6 +3640,7 @@ TERMINAL_CONFIG_ENV_MAP = {
|
||||
"modal_mode": "TERMINAL_MODAL_MODE",
|
||||
"degraded_mode": "TERMINAL_DEGRADED_MODE",
|
||||
"cwd": "TERMINAL_CWD",
|
||||
"temp_dir": "TERMINAL_TEMP_DIR",
|
||||
"timeout": "TERMINAL_TIMEOUT",
|
||||
"lifetime_seconds": "TERMINAL_LIFETIME_SECONDS",
|
||||
"docker_image": "TERMINAL_DOCKER_IMAGE",
|
||||
|
||||
@@ -382,6 +382,13 @@ DEFAULT_CONFIG = {
|
||||
# preserves the historical error + traceback behavior.
|
||||
"degraded_mode": "warn",
|
||||
"cwd": ".", # Use current directory
|
||||
# Root directory for Hermes' terminal session temp files (background
|
||||
# logs/pid/exit files, code-execution sandboxes, etc.). Defaults to the
|
||||
# OS temp root. Set this to a path on real storage when /tmp is a small
|
||||
# tmpfs and Hermes runs out of space — e.g. on RAM-based tmpfs setups
|
||||
# common to some Arch-based distros. Must be an absolute POSIX path;
|
||||
# relative or empty values fall back to the default temp resolution.
|
||||
"temp_dir": "",
|
||||
# Terminal font family for the desktop app's embedded xterm.js terminal.
|
||||
# When set (e.g. "'CaskaydiaCoveNerdFont', 'JetBrains Mono', monospace"),
|
||||
# the desktop terminal uses this as the CSS font-family value, with the
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
"""Tests for ``LocalEnvironment.get_temp_dir`` temp-dir redirect.
|
||||
|
||||
Hermes exposes ``terminal.temp_dir`` (mirrored to ``TERMINAL_TEMP_DIR``) so
|
||||
users on RAM-based tmpfs ``/tmp`` can point session temp files (background
|
||||
logs/pid/exit files, code-execution sandboxes) at real storage.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
from tools.environments.local import LocalEnvironment
|
||||
|
||||
|
||||
def _make_local_env(env: dict) -> LocalEnvironment:
|
||||
"""Construct a LocalEnvironment without running init_session (no bash)."""
|
||||
obj = LocalEnvironment.__new__(LocalEnvironment)
|
||||
obj.env = dict(env)
|
||||
return obj
|
||||
|
||||
|
||||
def test_temp_dir_override_honored(tmp_path):
|
||||
target = str(tmp_path)
|
||||
env = _make_local_env({"TERMINAL_TEMP_DIR": target})
|
||||
assert env.get_temp_dir() == target
|
||||
|
||||
|
||||
def test_temp_dir_from_process_env(tmp_path):
|
||||
target = str(tmp_path)
|
||||
env = _make_local_env({})
|
||||
prev = os.environ.get("TERMINAL_TEMP_DIR")
|
||||
os.environ["TERMINAL_TEMP_DIR"] = target
|
||||
try:
|
||||
assert env.get_temp_dir() == target
|
||||
finally:
|
||||
if prev is None:
|
||||
os.environ.pop("TERMINAL_TEMP_DIR", None)
|
||||
else:
|
||||
os.environ["TERMINAL_TEMP_DIR"] = prev
|
||||
|
||||
|
||||
def test_temp_dir_non_existent_falls_through(tmp_path):
|
||||
"""A configured path that does not exist must not be used."""
|
||||
missing = str(tmp_path / "does-not-exist")
|
||||
env = _make_local_env({"TERMINAL_TEMP_DIR": missing})
|
||||
# Should fall through to the standard TMPDIR//tmp//gettempdir chain, not
|
||||
# return the missing path.
|
||||
assert env.get_temp_dir() != missing
|
||||
|
||||
|
||||
def test_temp_dir_empty_falls_through(tmp_path, monkeypatch):
|
||||
"""An empty/relative terminal.temp_dir must not redirect."""
|
||||
monkeypatch.setenv("TMPDIR", str(tmp_path))
|
||||
env = _make_local_env({"TERMINAL_TEMP_DIR": ""})
|
||||
assert env.get_temp_dir() == str(tmp_path)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(pytest.main([__file__, "-q"]))
|
||||
@@ -1789,6 +1789,13 @@ class LocalEnvironment(BaseEnvironment):
|
||||
# Force forward slashes so the same string serves both contexts.
|
||||
return str(cache_dir).replace("\\", "/")
|
||||
|
||||
# Explicit temp-dir override from terminal.temp_dir (TERMINAL_TEMP_DIR).
|
||||
# Honored ahead of the generic TMPDIR so users can redirect Hermes' temp
|
||||
# root to real storage when /tmp is a small tmpfs.
|
||||
configured = self.env.get("TERMINAL_TEMP_DIR") or os.environ.get("TERMINAL_TEMP_DIR")
|
||||
if configured and configured.startswith("/") and os.path.isdir(configured):
|
||||
return configured.rstrip("/") or "/"
|
||||
|
||||
for env_var in ("TMPDIR", "TMP", "TEMP"):
|
||||
candidate = self.env.get(env_var) or os.environ.get(env_var)
|
||||
if candidate and candidate.startswith("/"):
|
||||
|
||||
Reference in New Issue
Block a user