Wiring view: every machine and what connects it (Companion, MIDI, Dante, watchdog, web, Art-Net), hide types or single links, scripts per computer

RESI LIVE NOT REC follows the Resi auto-record service (custom autorec_status, not during its REC hold; stale heartbeat = no verdict);
INGEST watches AUTO REC (running, heartbeat, Resi reachable), hold restarts it. Probes list each computer's scripts by file name only.
HyperDeck addresses read from bonjourHost.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-27 13:23:02 -04:00
co-authored by Claude Opus 5.5
parent 6c78ed3221
commit 26804e4ce2
11 changed files with 655 additions and 43 deletions
+54 -8
View File
@@ -27,7 +27,7 @@ MAC_PROBE = (
'echo SP_AGENTS; launchctl list 2>/dev/null; echo SP_PROCS; ps -axo comm= 2>/dev/null; '
'echo SP_APPS; ls /Applications 2>/dev/null; echo SP_HELPER; ls -l {helper} 2>&1; sudo -n -l {helper} 2>&1 | tail -1; '
'echo SP_SOLO; cat {solo}/solo-mode.json 2>/dev/null; echo; stat -f %m {solo}/solo-mode.json 2>/dev/null; '
'echo SP_NOW; date +%s; echo SP_END'
'echo SP_NOW; date +%s; echo SP_SCRIPTS; ps -axo pid=,args= 2>/dev/null | grep -E \'[.](py|js|mjs|sh|zsh|command|swift|rb|pl|scpt)( |$)\' | grep -v SP_SCRIPTS; echo SP_END'
)
WIN_PROBE = (
'echo SP_NAME=%COMPUTERNAME% & echo SP_TASKS & schtasks /Query /FO CSV /NH 2>nul & echo SP_PROCS & tasklist /FO CSV /NH 2>nul & '
@@ -35,8 +35,15 @@ WIN_PROBE = (
'"(Get-NetConnectionProfile | Select -First 1 -ExpandProperty NetworkCategory); '
'(Get-NetAdapterPowerManagement | Select -First 1 -ExpandProperty WakeOnMagicPacket); '
'((Get-NetFirewallRule -Name OpenSSH-Server-In-TCP -ErrorAction SilentlyContinue | Get-NetFirewallAddressFilter | '
'Select -ExpandProperty RemoteAddress) -join \',\')" 2>nul & echo SP_END'
'Select -ExpandProperty RemoteAddress) -join \',\')" 2>nul & echo SP_SCRIPTS & powershell -NoProfile -Command '
'"Get-CimInstance Win32_Process | Where-Object { $_.CommandLine -match \'[.](ps1|py|js|bat|cmd|vbs)\' } | '
'ForEach-Object { [string]$_.ProcessId + \' \' + $_.CommandLine }" 2>nul & echo SP_END'
)
# A script: a file run by an interpreter (its name only is kept, never the rest of the command line).
SCRIPT_RE = r"[^ /\\]+[.](py|js|mjs|sh|zsh|command|swift|rb|pl|scpt|ps1|bat|cmd|vbs)( |\"|$)"
# Not the user's scripts: programs' own insides (Companion's modules, app bundles, the system).
NOT_SCRIPTS = (".app/Contents/", "/companion/modules/", "/System/", "/usr/libexec/", "/Library/Apple/", "\\Windows\\",
"/node_modules/", "Program Files")
def shell_path(path):
@@ -113,6 +120,38 @@ def parse_sc(lines):
return services
def parse_scripts(lines, agents=None, tasks=None):
"""The scripts a computer runs: [{"name", "pid", "by", "running"}]. From `ps` (pid, command line) lines,
our own launchd jobs (org.waterschurch.*, linked to their script by pid) and the \\StatusPanel\\ tasks
({path: state}). Only a script's file name is kept, never the rest of its command line."""
agents, out, seen_pids = agents or {}, [], set()
by_pid = {pid: label for label, pid in agents.items() if pid}
for line in lines:
line = line.strip()
m = re.match(r"^(\d+)\s+(.*)$", line)
if not m or "SP_SCRIPTS" in line:
continue
found = re.search(SCRIPT_RE, m.group(2))
if not found:
continue
# The script's own path (from the last " /", ' "' or " C:\" before its name): the interpreter may sit
# inside an app (Apple's python3 is in Python.app) without the script being a program's insides.
head = m.group(2)[:found.end()]
cut = max([head.rfind(" /"), head.rfind(' "')] + [x.start() for x in re.finditer(r" [A-Za-z]:\\", head)])
if any(x in head[cut + 1:] for x in NOT_SCRIPTS):
continue
name = re.split(r"[\\/]", found.group(0).strip().strip('"'))[-1]
pid = int(m.group(1))
out.append({"name": name, "pid": pid, "by": by_pid.get(pid, ""), "running": True})
seen_pids.add(pid)
for label, pid in sorted(agents.items()):
if label.startswith("org.waterschurch.") and pid not in seen_pids:
out.append({"name": label[len("org.waterschurch."):], "pid": pid, "by": label, "running": pid is not None})
for path, st in sorted((tasks or {}).items()):
out.append({"name": path.split("\\")[-1], "pid": None, "by": path, "running": st.lower() == "running", "state": st})
return sorted(out, key=lambda x: (not x["running"], x["name"].lower()))
def light(ok, reason=""):
return {"light": "ok" if ok else "problem", "reason": reason if not ok else ""}
@@ -192,7 +231,7 @@ def mac_items(host, parsed, cfg):
item.update({"light": "unknown", "reason": ""})
items.append(item)
return items, {"agents": sorted(a for a in agents if not a.startswith(("com.apple.", "application."))),
"processes": sorted(proc_names)[:200]}
"processes": sorted(proc_names)[:200], "scripts": parse_scripts(parsed.get("SP_SCRIPTS", []), agents)}
def windows_items(host, parsed):
@@ -228,8 +267,13 @@ def windows_items(host, parsed):
item.update({"light": "unknown", "reason": ""})
items.append(item)
setup = {"profile": profile, "wake_on_lan": wol, "firewall_allow": allow}
return items, {"tasks": sorted(t for t in tasks if "StatusPanel" in t), "services": sorted(k for k, v in services.items()
if v == "RUNNING" and not k.lower().startswith(("w", "s", "d", "e", "n", "p", "t", "u", "c")))[:60]}, setup
# A task that starts a program is "Ready" once it did: it counts as running while that program runs.
up = {"\\StatusPanel\\" + run["name"] for run, item in zip(host.get("runs", []), items)
if run.get("kind") == "task" and run.get("exe") and item.get("light") == "ok"}
ours = {t: ("Running" if t in up else row[2] if len(row) > 2 else "") for t, row in tasks.items() if "StatusPanel" in t}
return items, {"tasks": sorted(ours), "services": sorted(k for k, v in services.items()
if v == "RUNNING" and not k.lower().startswith(("w", "s", "d", "e", "n", "p", "t", "u", "c")))[:60],
"scripts": parse_scripts(parsed.get("SP_SCRIPTS", []), tasks=ours)}, setup
def probe_host(cfg, alias, host):
@@ -245,7 +289,7 @@ def probe_host(cfg, alias, host):
rec["error"] = err.strip() or "no answer"
return rec
rec["reachable"] = True
parsed = sections(out, ("SP_TASKS", "SP_PROCS", "SP_SVC", "SP_PS", "SP_END"))
parsed = sections(out, ("SP_TASKS", "SP_PROCS", "SP_SVC", "SP_PS", "SP_SCRIPTS", "SP_END"))
rec["computer_name"] = values(out).get("NAME", "").strip()
rec["items"], rec["found"], rec["setup"] = windows_items(host, parsed)
rec["setup"]["key_ok"] = True
@@ -257,7 +301,7 @@ def probe_host(cfg, alias, host):
return rec
rec["reachable"] = True
v = values(out)
parsed = sections(out, ("SP_AGENTS", "SP_PROCS", "SP_APPS", "SP_HELPER", "SP_SOLO", "SP_NOW", "SP_END"))
parsed = sections(out, ("SP_AGENTS", "SP_PROCS", "SP_APPS", "SP_HELPER", "SP_SOLO", "SP_NOW", "SP_SCRIPTS", "SP_END"))
rec["computer_name"], rec["os_version"], rec["python"] = v.get("NAME", ""), v.get("OSV", ""), v.get("PY", "")
rec["items"], rec["found"] = mac_items(host, parsed, cfg)
rec["setup"] = {"key_ok": (v.get("KEY") or "0").strip() not in ("", "0"), "python_ok": "3." in v.get("PY", "")}
@@ -301,6 +345,7 @@ def probe_self(cfg, alias, host):
try:
agents_out = subprocess.run(["/bin/launchctl", "list"], capture_output=True, text=True, timeout=5).stdout
procs_out = subprocess.run(["/bin/ps", "-axo", "comm="], capture_output=True, text=True, timeout=5).stdout
args_out = subprocess.run(["/bin/ps", "-axo", "pid=,args="], capture_output=True, text=True, timeout=5).stdout
except (OSError, subprocess.TimeoutExpired) as e:
rec["error"] = str(e)
return rec
@@ -308,7 +353,8 @@ def probe_self(cfg, alias, host):
helper = subprocess.run(["/bin/zsh", "-c", "ls -l %s 2>&1; sudo -n -l %s 2>&1 | tail -1" % (paths["helper"], paths["helper"])],
capture_output=True, text=True, timeout=6).stdout
parsed = {"SP_AGENTS": agents_out.splitlines(), "SP_PROCS": procs_out.splitlines(), "SP_APPS": [],
"SP_HELPER": helper.splitlines(), "SP_SOLO": [], "SP_NOW": [str(int(time.time()))]}
"SP_HELPER": helper.splitlines(), "SP_SOLO": [], "SP_NOW": [str(int(time.time()))],
"SP_SCRIPTS": [l for l in args_out.splitlines() if re.search(SCRIPT_RE, l)]}
rec["items"], rec["found"] = mac_items(host, parsed, cfg)
for item, run in zip(rec["items"], host.get("runs", [])):
if run.get("kind") == "folder":