mirror of
https://github.com/saphid/frame-control.git
synced 2026-10-06 08:00:32 +02:00
Valve publishes no Steam Frame OS image, so tests/frame-container builds the Frame's SSH surface on Valve and Collabora's Holo Core aarch64 base: a steamos user with a password and sudo, OpenSSH with keys and passwords, Python, and a systemctl that only records requests. Against it from the Simulator: password pairing, the host-key pin, the power password check. Found and fixed: a changed host key or a refused login said "Can't reach the Frame" and retried forever; they now say "Pair with the Frame again" and offer that. The server's key rejection now says the header may be wrong, not only missing. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
40 lines
1.8 KiB
Swift
40 lines
1.8 KiB
Swift
import SwiftUI
|
|
|
|
@main
|
|
struct FrameControlApp: App {
|
|
@StateObject private var model = AppModel()
|
|
@Environment(\.scenePhase) private var scenePhase
|
|
|
|
var body: some Scene {
|
|
WindowGroup {
|
|
RootView(model: model)
|
|
.task {
|
|
#if DEBUG
|
|
// Simulator testing without the pairing screen: print this device's key,
|
|
// and connect to FRAME_TEST_HOST with it (`simctl launch` passes
|
|
// SIMCTL_CHILD_FRAME_TEST_HOST through as FRAME_TEST_HOST).
|
|
print("FRAME_CONTROL_KEY: \(model.authorizedKeysLine)")
|
|
// FRAME_TEST_PAIR="host|user|password" runs the real password pairing.
|
|
if model.settings == nil, let pair = ProcessInfo.processInfo.environment["FRAME_TEST_PAIR"] {
|
|
let f = pair.components(separatedBy: "|")
|
|
if f.count == 3 { await model.pair(host: f[0], user: f[1], password: f[2]); return }
|
|
}
|
|
if model.settings == nil, let host = ProcessInfo.processInfo.environment["FRAME_TEST_HOST"] {
|
|
await model.useKey(host: host, user: "steamos")
|
|
return
|
|
}
|
|
#endif
|
|
if model.settings != nil { await model.connect() }
|
|
}
|
|
.onOpenURL { url in
|
|
// frame-control://install?… from a website (docs/web-install.md).
|
|
guard let link = InstallLink(url.absoluteString), model.pendingInstallLinks.count < 5 else { return }
|
|
model.pendingInstallLinks.append(link)
|
|
}
|
|
.onChange(of: scenePhase) { _, phase in
|
|
if phase == .active { model.resume() }
|
|
}
|
|
}
|
|
}
|
|
}
|