refactor(env): agent.secret_scope.load_env_file is the only .env tokenizer; six hand parsers collapse onto it

Six independent line-parsers with three different quoting/comment semantics
read the same .env files: tools/skills_tool.load_env (strip("\"'"), no inline
comments), hermes_cli/managed_scope._parse_env (same, no export, no BOM),
web_server_cron._profile_env_value (plain utf-8, no BOM), profile_cmd
._env_file_has_key, env_loader._env_keys_defined_in_dotenv (utf-8, so a BOM'd
first key stayed "\ufeffKEY" and the dashboard profile scrub missed line 1),
mem0/_setup._prompt_api_key (startswith scan, no quote strip). The boundary
parsers (scrub key set, skill secret capture) therefore disagreed with the
parser that installs the profile scope.

Now every one is a 1-3 line forwarder onto load_env_file, and
hermes_cli.config.load_env is memo over it (public signature unchanged).
_parse_env_value moves next to its only caller in secret_scope.
load_env_file gains the same latin-1 fallback env_loader uses to install
into os.environ, so a mis-encoded file yields the same key set on both sides.
Managed .env keeps its fail-LOUD contract (decode error logs and ignores the
file) instead of load_env_file's fail-soft {}.

Behavior change: managed .env, skills_tool and mem0 setup now honour
`export`, quoted-value escapes and inline comments the way the profile scope
does; web_server_cron and the dashboard scrub tolerate a BOM.

Invariant test: a BOM'd/export/quoted/commented .env yields the same key set
via load_hermes_dotenv (installer), load_env_file (scope) and
_env_keys_defined_in_dotenv (scrub); fails with the old scrub parser.
This commit is contained in:
teknium1
2026-09-12 20:37:52 -07:00
committed by Teknium
parent 226df89f74
commit c849bc383a
9 changed files with 105 additions and 108 deletions
+4 -15
View File
@@ -31,21 +31,10 @@ def _is_active(p, active: str) -> bool:
def _env_file_has_key(env_path: Path, key: str) -> bool:
"""True when *key* is assigned in *env_path*. Read as utf-8-sig: a Notepad-edited .env can
carry a BOM that would hide the first key behind U+FEFF. A mis-encoded file (UnicodeDecodeError
is a ValueError, not OSError) must not abort the install preview — skip the pre-check."""
if not env_path.is_file():
return False
try:
# .env is written as UTF-8 everywhere in the codebase, but a Notepad-edited file can carry a BOM —
# read as utf-8-sig so the first key isn't hidden behind U+FEFF (#62617).
for raw in env_path.read_text(encoding="utf-8-sig").splitlines():
line = raw.strip()
if line and not line.startswith("#") and line.split("=", 1)[0].strip() == key:
return True
except (OSError, UnicodeDecodeError):
pass
return False
"""True when *key* is assigned in *env_path* (unreadable/mis-encoded file → False, never aborts)."""
from agent.secret_scope import load_env_file
return key in load_env_file(env_path)
def _render_distribution_plan(plan) -> None: