load-aware dispatch: run-on-mac1 routes Mac2↔Mac1 by load + caps script writes 44 ecosystem.config.cjs files
Files touched
M scripts/apply-pm2-caps.sh
Diff
commit e6e31828b2480dffc5916d27db9430cb1cbc5b5e
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Mon May 11 09:42:52 2026 -0700
load-aware dispatch: run-on-mac1 routes Mac2↔Mac1 by load + caps script writes 44 ecosystem.config.cjs files
---
scripts/apply-pm2-caps.sh | 49 ++++++++++++++++++++++++++++++++++++++---------
1 file changed, 40 insertions(+), 9 deletions(-)
diff --git a/scripts/apply-pm2-caps.sh b/scripts/apply-pm2-caps.sh
index c98b8a2..8d11546 100644
--- a/scripts/apply-pm2-caps.sh
+++ b/scripts/apply-pm2-caps.sh
@@ -50,21 +50,28 @@ log "mode=$MODE sister-cap=$SISTER_CAP llm-cap=$LLM_CAP"
exit 3
}
-/usr/bin/python3 <<PYEOF
+CAPS_MODE="$MODE" \
+CAPS_SISTER="$SISTER_CAP" \
+CAPS_LLM="$LLM_CAP" \
+CAPS_LOG="$LOG" \
+CAPS_PM2="$PM2_BIN" \
+CAPS_JLIST="/tmp/pm2-jlist-${TS}.json" \
+/usr/bin/python3 <<'PYEOF'
import json, os, sys, subprocess, time
-MODE = "$MODE"
-SISTER_CAP = "$SISTER_CAP"
-LLM_CAP = "$LLM_CAP"
-LOG = "$LOG"
-PM2 = "$PM2_BIN"
+MODE = os.environ["CAPS_MODE"]
+SISTER_CAP = os.environ["CAPS_SISTER"]
+LLM_CAP = os.environ["CAPS_LLM"]
+LOG = os.environ["CAPS_LOG"]
+PM2 = os.environ["CAPS_PM2"]
+JLIST = os.environ["CAPS_JLIST"]
def logmsg(s):
line = f"[{time.strftime('%H:%M:%S', time.gmtime())}] {s}"
print(line)
with open(LOG, 'a') as f: f.write(line + "\n")
-procs = json.load(open("/tmp/pm2-jlist-${TS}.json"))
+procs = json.load(open(JLIST))
# Identify candidates: no max_memory_restart, has a valid pm_cwd + pm_exec_path
todo = []
@@ -106,9 +113,33 @@ for t in todo:
logmsg(f"already-has-ecosystem {t['name']}")
already += 1
continue
- # Build the script body
+ # Build the script body — preserve ALL user env vars from current pm2 state.
+ # pm2_env mixes internal keys (pm_*, PM2_*, status, etc.) with user env;
+ # we whitelist by skipping pm2-internal prefixes.
+ proc = [p for p in procs if p.get('name') == t['name']][0]
+ full_env = proc.get('pm2_env', {})
+ SKIP_PREFIXES = ('pm_', 'pm2_', 'PM2_', 'PWD', 'OLDPWD', '_', 'SHLVL')
+ SKIP_KEYS = {'instance_var','restart_time','unstable_restarts','created_at','axm_actions','axm_monitor','axm_options','axm_dynamic','axm_metrics','exec_interpreter','watch','env','env_diff','status','windowsHide','vizion_running','instances','exec_mode','autorestart','automation','treekill','username','versioning','version','node_args','source_map_support','exit_code','pmx','filter_env','disable_trace','merge_logs','vizion','autostart','windows_hide','watch_options','APP_PATH','HOME','PATH','SHELL','USER','LOGNAME','TERM','LANG','LC_ALL','TMPDIR','XPC_FLAGS','XPC_SERVICE_NAME','__CFBundleIdentifier','SSH_AUTH_SOCK','SSH_CLIENT','SSH_CONNECTION','SSH_TTY','TERM_PROGRAM','TERM_PROGRAM_VERSION','TERM_SESSION_ID','ITERM_PROFILE','ITERM_SESSION_ID','MAIL','OLDPWD','PYENV_SHELL','LSCOLORS','LS_COLORS','COLORTERM','HOMEBREW_PREFIX','HOMEBREW_CELLAR','HOMEBREW_REPOSITORY','MANPATH','INFOPATH','EDITOR'}
+ user_env = {}
+ for k, v in full_env.items():
+ if not isinstance(v, (str, int, bool)): continue
+ if any(k.startswith(p) for p in SKIP_PREFIXES): continue
+ if k in SKIP_KEYS: continue
+ # Skip giant string values that aren't real config
+ sv = str(v)
+ if len(sv) > 500: continue
+ user_env[k] = v
+ env_lines = []
+ for k, v in user_env.items():
+ if isinstance(v, bool):
+ env_lines.append(f" {k}: {str(v).lower()},")
+ elif isinstance(v, int):
+ env_lines.append(f" {k}: {v},")
+ else:
+ esc = str(v).replace('\\', '\\\\').replace('"', '\\"')
+ env_lines.append(f' {k}: "{esc}",')
+ env_block = "\n".join(env_lines) + "\n" if env_lines else ""
script_basename = os.path.basename(t['script'])
- env_block = f" PORT: {t['port']},\n" if t['port'] else ""
content = f"""module.exports = {{
apps: [{{
name: "{t['name']}",