Devices: the assistant's keep-awake and panel tools reach the chosen headset; a Mac view tunnel opened during a switch is dropped

Integration review findings: the scripts they run ssh'd to whatever 'frame' means
in ~/.ssh/config. They now take FRAME_ALIAS and FRAME_SSH_OPTS from the server's route.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
saphidandClaude Opus 5.5 committed 2026-09-29 12:16:25 +10:00
1 parent 08fbe730c6
commit ffef4d897a
5 files changed
+39 -6

No files matched your search

+4 -2
View File
@@ -15,11 +15,13 @@
set -euo pipefail set -euo pipefail
FRAME_ALIAS=${FRAME_ALIAS:-frame} FRAME_ALIAS=${FRAME_ALIAS:-frame}
# Frame Control passes the headset it has chosen: its address and pinned identity.
ssh_opts=(${(Q)${(z)FRAME_SSH_OPTS:-}})
HERE=${0:A:h} HERE=${0:A:h}
cmd=${1:-status} cmd=${1:-status}
case $cmd in on|off|status) ;; *) echo "usage: keep-awake.sh on|off|status" >&2; exit 2 ;; esac case $cmd in on|off|status) ;; *) echo "usage: keep-awake.sh on|off|status" >&2; exit 2 ;; esac
ssh -o ConnectTimeout=8 "$FRAME_ALIAS" \ ssh "${ssh_opts[@]}" -o ConnectTimeout=8 "$FRAME_ALIAS" \
'mkdir -p ~/.cache/frame-control && cat > ~/.cache/frame-control/frame_steam.py' < "$HERE/../ui/frame_steam.py" 'mkdir -p ~/.cache/frame-control && cat > ~/.cache/frame-control/frame_steam.py' < "$HERE/../ui/frame_steam.py"
# Runs on the Frame. Verified 2026-09-28 (BUILD_ID 20260925.6191901): the # Runs on the Frame. Verified 2026-09-28 (BUILD_ID 20260925.6191901): the
@@ -27,7 +29,7 @@ ssh -o ConnectTimeout=8 "$FRAME_ALIAS" \
# written the way Steam's settings page does (steamui module exporting the # written the way Steam's settings page does (steamui module exporting the
# SetSetting wrapper). logind refuses an inhibitor from an SSH session # SetSetting wrapper). logind refuses an inhibitor from an SSH session
# ("Interactive authentication required") but allows one from a user unit. # ("Interactive authentication required") but allows one from a user unit.
ssh "$FRAME_ALIAS" python3 - "$cmd" <<'EOF' ssh "${ssh_opts[@]}" "$FRAME_ALIAS" python3 - "$cmd" <<'EOF'
import json, os, subprocess, sys import json, os, subprocess, sys
sys.path.insert(0, os.path.expanduser("~/.cache/frame-control")) sys.path.insert(0, os.path.expanduser("~/.cache/frame-control"))
from frame_steam import Page from frame_steam import Page
+3 -1
View File
@@ -21,6 +21,8 @@
set -euo pipefail set -euo pipefail
FRAME_ALIAS=${FRAME_ALIAS:-frame} FRAME_ALIAS=${FRAME_ALIAS:-frame}
# Frame Control passes the headset it has chosen: its address and pinned identity.
ssh_opts=(${(Q)${(z)FRAME_SSH_OPTS:-}})
REMMINA_PROFILE="~/.var/app/org.remmina.Remmina/data/remmina/mac-screen-sharing.remmina" REMMINA_PROFILE="~/.var/app/org.remmina.Remmina/data/remmina/mac-screen-sharing.remmina"
id="" name="" id="" name=""
@@ -109,4 +111,4 @@ EOF
) )
b64=$(print -rn -- "$remote" | base64) b64=$(print -rn -- "$remote" | base64)
ssh "$FRAME_ALIAS" "bash -c \"\$(echo $b64 | base64 -d)\" panel-on-frame $id ${(j: :)${(@q)cmd}}" ssh "${ssh_opts[@]}" "$FRAME_ALIAS" "bash -c \"\$(echo $b64 | base64 -d)\" panel-on-frame $id ${(j: :)${(@q)cmd}}"
+20
View File
@@ -252,3 +252,23 @@ class ComputerState(unittest.TestCase):
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()
class ScriptsFollowTheHeadset(unittest.TestCase):
"""keep_awake and panel tools run scripts that ssh on their own: they must reach the
headset the server is routed to, not whatever `frame` means in ~/.ssh/config."""
@unittest.skipUnless(shutil.which('zsh'), 'needs zsh')
def test_scripts_get_the_routed_alias_and_options(self):
import shlex
fake = mock.Mock(FRAME='frame-2', LOCAL=False, HERE=Path(__file__).resolve().parent.parent / 'ui',
SSH=['ssh', '-o', 'BatchMode=yes', '-o', 'HostName=192.0.2.2', '-o', 'HostKeyAlias=frame-control-ab'])
with mock.patch.object(agent.subprocess, 'run', return_value=mock.Mock(returncode=0, stdout='ok', stderr='')) as run:
agent.run_script(fake, 'keep-awake.sh', ['status'])
env = run.call_args.kwargs['env']
self.assertEqual(env['FRAME_ALIAS'], 'frame-2')
self.assertEqual(shlex.split(env['FRAME_SSH_OPTS']), fake.SSH[1:])
# and the script turns that back into the same argv
out = subprocess.run(['zsh', '-c', 'ssh_opts=(${(Q)${(z)FRAME_SSH_OPTS:-}}); print -l -- $ssh_opts'],
env={**os.environ, 'FRAME_SSH_OPTS': env['FRAME_SSH_OPTS']}, capture_output=True, text=True)
self.assertEqual(out.stdout.splitlines(), fake.SSH[1:])
+6 -1
View File
@@ -1,7 +1,9 @@
"""Agent actions and one-use human approvals. No model SDK or network calls here.""" """Agent actions and one-use human approvals. No model SDK or network calls here."""
import hashlib import hashlib
import os
from pathlib import Path from pathlib import Path
import secrets import secrets
import shlex
import shutil import shutil
import subprocess import subprocess
import threading import threading
@@ -129,7 +131,10 @@ def run_script(server, name, args):
script = server.HERE.parent / 'scripts' / name script = server.HERE.parent / 'scripts' / name
if not script.exists() or not shutil.which('zsh') or server.LOCAL: if not script.exists() or not shutil.which('zsh') or server.LOCAL:
raise ValueError(name + ' requires a computer with zsh and the matching script installed') raise ValueError(name + ' requires a computer with zsh and the matching script installed')
result = subprocess.run(['zsh', str(script), *args], capture_output=True, text=True, timeout=60) # The headset the server is routed to, not whatever `frame` means in ~/.ssh/config.
env = {**os.environ, 'FRAME_ALIAS': server.FRAME,
'FRAME_SSH_OPTS': shlex.join(server.SSH[1:])}
result = subprocess.run(['zsh', str(script), *args], capture_output=True, text=True, timeout=60, env=env)
if result.returncode: if result.returncode:
raise ValueError(result.stderr.strip() or 'Script failed') raise ValueError(result.stderr.strip() or 'Script failed')
return {'message': result.stdout.strip()} return {'message': result.stdout.strip()}
+6 -2
View File
@@ -262,12 +262,13 @@ class MacView:
"""Tries the ports on one route; True once the tunnel answers. With self.lock held.""" """Tries the ports on one route; True once the tunnel answers. With self.lock held."""
last = "" last = ""
for port in ports: for port in ports:
target = (self.frame, self.host_opts) # retarget() may change these meanwhile
# `via` first: ssh keeps the first value of an option, so USB-C's HostName wins # `via` first: ssh keeps the first value of an option, so USB-C's HostName wins
# while the headset's pinned identity (in host_opts) still checks it. # while the headset's pinned identity (in host_opts) still checks it.
proc = subprocess.Popen([*self.tunnel_ssh, "-o", "ControlPath=none", *via, *self.host_opts, proc = subprocess.Popen([*self.tunnel_ssh, "-o", "ControlPath=none", *via, *target[1],
"-o", "ExitOnForwardFailure=yes", "-o", "ExitOnForwardFailure=yes",
"-o", "ServerAliveInterval=5", "-o", "ServerAliveCountMax=3", "-N", "-o", "ServerAliveInterval=5", "-o", "ServerAliveCountMax=3", "-N",
"-R", f"127.0.0.1:{port}:127.0.0.1:{self.port}", self.frame], "-R", f"127.0.0.1:{port}:127.0.0.1:{self.port}", target[0]],
stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL,
stderr=subprocess.PIPE, text=True) stderr=subprocess.PIPE, text=True)
# A taken port makes ssh exit once it's connected; a working # A taken port makes ssh exit once it's connected; a working
@@ -280,6 +281,9 @@ class MacView:
if self._probe(port): if self._probe(port):
ok = True ok = True
break break
if ok and target[0] != self.frame:
ok = False # the app switched headset while this one connected: not its tunnel
self._last_tunnel_error = "switched headset"
if ok: if ok:
self.tunnel, self.remote_port = proc, port self.tunnel, self.remote_port = proc, port
self.track(proc) self.track(proc)