Files
hermes-agent/activate
ethernet 92686159d1 fix(pm): integrate audited runtime and lifecycle repairs
Prepare dependency generations before selecting them. Keep shipped tool
bytes separate from writable additions, and store facts beside their entries.
Validate proposed plugin sets before config publication. Restore the previous
config if the facts write fails.

Consolidate duplicate updater, backup, setup, and voice helpers. Repair
launcher selection, dependency consumers, download ownership, update feeds,
and native Windows process and file handling.

Verification: 206 changed/prior-failing Python files reported 4630 passed,
one failed, and 330 skipped. Fix the remaining Hindsight fixture boundary.
The final targeted rerun reported 234 passed and two skipped. The store
review regression batch reported 83 passed and one skipped. Desktop
TypeScript checks, 56 selected Electron tests, 24 release tests, and the
removed-import/compatibility guards passed.

This is an integration checkpoint, not full audit acceptance. The complete
Python suite has not run on this fixed tree. Crash-atomic plugin publication,
generation cleanup, receipt correlation, and packaged lifecycle acceptance
remain open in docs/pm-audit-status.md.
2026-09-05 22:36:48 -04:00

96 lines
4.1 KiB
Bash

#!/bin/bash
# ============================================================================
# venv-style activation for the Hermes dev environment (pm-managed tools).
#
# source ./activate (bash / zsh, repo root)
# .\activate.ps1 (PowerShell)
#
# Emits the composed pm env (PATH + tool vars) into the CURRENT shell, with
# save/restore: `deactivate` undoes exactly what activation changed.
#
# Requires a completed `./setup-hermes.sh` — it never invokes uv. It runs
# the pm store's pinned python (fallback: the repo venv) to emit the env
# JSON, then exports it here.
# ============================================================================
# Guard against double-sourcing: venv activate precedent (re-activating is a
# no-op re-save; we keep it idempotent by deactivating first).
if [ -n "${__HERMES_ACTIVATED:-}" ]; then
deactivate
fi
_HERMES_REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Bootstrap Python only emits the environment; it does not install anything.
_hermes_repo="$_HERMES_REPO"
_hermes_py=""
for _hermes_candidate in "$_hermes_repo/.venv/bin/python" "$_hermes_repo/.venv/Scripts/python.exe" \
"$_hermes_repo/venv/bin/python" "$_hermes_repo/venv/Scripts/python.exe"; do
[ -x "$_hermes_candidate" ] && { _hermes_py="$_hermes_candidate"; break; }
done
if [ -z "$_hermes_py" ]; then
for _hermes_store in "${HERMES_RUNTIME_DIR:-}" "$_hermes_repo/../tools" "${HERMES_HOME:-$HOME/.hermes}/tools"; do
[ -n "$_hermes_store" ] || continue
for _hermes_candidate in "$_hermes_store"/python-*/bin/python3 "$_hermes_store"/python-*/python.exe "$_hermes_store"/python-*/bin/python "$_hermes_store"/python-*/bin/python.exe; do
[ -x "$_hermes_candidate" ] && { _hermes_py="$_hermes_candidate"; break; }
done
[ -n "$_hermes_py" ] && break
done
fi
if [ -z "$_hermes_py" ]; then
echo "activate: no bootstrap Python found; run setup-hermes.sh" >&2
return 1 2>/dev/null || exit 1
fi
# --- emit export lines from `pm env` ---
_hermes_json="$(PYTHONHOME= PYTHONPATH="$_hermes_repo" "$_hermes_py" -m hermes_cli.runtime_paths)"
if [ -z "$_hermes_json" ] || [ "${_hermes_json#*{}" = "$_hermes_json" ]; then
echo "activate: could not read pm env (run ./setup-hermes.sh first)" >&2
unset _hermes_repo _hermes_os _hermes_arch _hermes_target _hermes_store _hermes_py _hermes_json
return 1 2>/dev/null || exit 1
fi
_hermes_keys="$(printf '%s' "$_hermes_json" | "$_hermes_py" -c 'import json,sys; print(" ".join(json.load(sys.stdin)))' \
| tr ' ' '\n' | grep -E '^[A-Za-z_][A-Za-z0-9_]*$' | tr '\n' ' ')"
[ -n "$_hermes_keys" ] || _hermes_keys="PATH"
# --- snapshot what we are about to change (deactivate restores this) ---
__HERMES_ACTIVATED=1
__HERMES_SAVED_PATH="$PATH"
for _hermes_k in $_hermes_keys; do
eval "__HERMES_SAVED_$_hermes_k=\"\${$_hermes_k+set}\""
eval "__HERMES_PRIOR_$_hermes_k=\${$_hermes_k-__HERMES_UNSET__}"
done
eval "$(printf '%s' "$_hermes_json" | "$_hermes_py" -c '
import json, sys, shlex, re
for k, v in json.load(sys.stdin).items():
# Windows carries names like ProgramFiles(ARM) that are not valid
# bash identifiers — skip them (they pass through untouched).
if not re.fullmatch(r"[A-Za-z_][A-Za-z0-9_]*", k):
continue
print("export %s=%s" % (k, shlex.quote(str(v))))')"
deactivate() {
export PATH="$__HERMES_SAVED_PATH"
for _hermes_k in $__HERMES_KEY_LIST; do
eval "_hermes_was=\"\$__HERMES_SAVED_$_hermes_k\""
eval "_hermes_old=\"\$__HERMES_PRIOR_$_hermes_k\""
if [ "$_hermes_was" != "set" ]; then
unset "$_hermes_k"
else
export "$_hermes_k=$_hermes_old"
fi
unset "__HERMES_SAVED_$_hermes_k" "__HERMES_PRIOR_$_hermes_k"
done
unset __HERMES_SAVED_PATH __HERMES_KEY_LIST __HERMES_ACTIVATED
unset -f deactivate
unset _hermes_k _hermes_was _hermes_old
}
# remember the key list for deactivate (kept out of the loop vars above)
__HERMES_KEY_LIST="$_hermes_keys"
unset _hermes_repo _hermes_os _hermes_arch _hermes_target _hermes_store \
_hermes_py _hermes_json _hermes_keys _hermes_k _hermes_entry _hermes_venvpy \
_hermes_winarch