Devices: fixes from review round 12

- A reconnect while an install runs keeps the login it started with; a new
  one from ~/.ssh/config applies after.
- Frame > Open SSH goes through the server, so it uses the same headset and
  address as the app and refuses when there's none.
- A bare frame alias in use when a headset is set up stays selectable.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
saphidandClaude Opus 5.5 committed 2026-09-28 23:21:19 +10:00
1 parent 2e9bc8624b
commit 98ef6944c9
3 files changed
+34 -2

No files matched your search

+24 -1
View File
@@ -258,6 +258,29 @@ ipcMain.on("devices:changed", (e, list) => {
buildMenu();
});
const activeAlias = () => (devices.find(d => d.active) || {}).alias || FRAME;
// SSH through the server, so it goes to the headset and address the app is using
// (with its own pinned identity), and refuses when there's none.
function openSsh() {
if (!url) return dialog.showErrorBox("Couldn't open SSH", "Frame Control's server isn't running.");
const body = JSON.stringify({ what: "terminal" });
const req = http.request(new URL("/api/open", url), {
method: "POST", timeout: 15000,
headers: { "Content-Type": "application/json", "X-Frame-UI": "1", "Content-Length": Buffer.byteLength(body) },
}, (res) => {
let data = "";
res.on("data", (c) => { data += c; });
res.on("end", () => {
if (res.statusCode === 200) return;
let why = `HTTP ${res.statusCode}`;
try { why = JSON.parse(data).error || why; } catch {}
dialog.showErrorBox("Couldn't open SSH", why);
});
});
req.on("error", (e) => dialog.showErrorBox("Couldn't open SSH", e.message));
req.on("timeout", () => req.destroy(new Error("the server didn't answer")));
req.end(body);
}
function showDevices() {
if (win && url) win.webContents.executeJavaScript('location.hash = "devices"').catch(() => {});
}
@@ -362,7 +385,7 @@ function buildMenu() {
label: "Frame",
submenu: [
{ label: "Set Up Connection…", click: () => setUpConnection() },
{ label: IS_MAC ? "Open SSH in Terminal" : "Open SSH in a Terminal", click: () => runInTerminal(["ssh", activeAlias()]) },
{ label: IS_MAC ? "Open SSH in Terminal" : "Open SSH in a Terminal", click: openSsh },
{ type: "separator" },
...(devices.length > 1 ? [{
label: "Headset",
+2
View File
@@ -278,6 +278,8 @@ class Connecting(unittest.TestCase):
self.link.config_mtime = None
self.link.watch_config()
self.assertEqual(len(self.routes), routes) # an install is running: not yet
self.link.connect(["dropped"]) # a reconnect meanwhile keeps the login it started with
self.assertIn("User=steamos", self.routes[-1][1])
running[0] = 0
self.link.watch_config()
self.assertIn("User=deck", self.routes[-1][1])
+8 -1
View File
@@ -413,6 +413,7 @@ class Link:
if before.get("transient") and not before.get("none") and self.routed is not None:
# A bare alias is in use: a headset set up now doesn't take over by itself.
self.override = self.override or before["alias"]
self.session_alias = self.session_alias or before["alias"] # and stays on the list
if self.reg.sync_from_config():
self.devices_changed()
after = self.active_device()
@@ -435,9 +436,15 @@ class Link:
why = self.describe(reasons)
self.last_attempt = now()
self.close_master()
with self.route_lock:
with self.work_lock, self.route_lock:
gen = self.attempt_gen = self.gen
device = self.active_device()
if self.routed and self.routed[0] == device["id"] and self.route_key(device) != self.routed \
and self.work():
# Its login changed in ~/.ssh/config while an install runs: reconnect with the
# one it started with; the change applies once it's done (see watch_config).
device = dict(device, user=self.routed[1], port=self.routed[2])
self.deferred = True
if self.route_key(device) != self.routed:
# Another headset, or a new user or port: nothing may go on using the old
# route, even if this attempt fails.