feat(ios,android): P2 wave, git panel, token UX, per-host WS token, docs
App layer, four sequential slices (a shared .xcodeproj means adding files regenerates it, so these could not run in parallel): - token UX end to end: pairing prompts for a token when a host 401s, POST /auth validates it, and 204-without-Set-Cookie is correctly read as "this server has auth disabled" rather than "authenticated". A host paired before the token was turned on recovers by re-pairing in place. Remove-host now exists and finally gives PushRegistrar.handleHostRemoved a caller. - project git panel + worktree lifecycle (T-iOS-32) + claude --resume history — the parity gap with Android and the web front end. - terminal search (T-iOS-33) and voice PTT (T-iOS-31) with an epoch guard so a session switch between dictation and confirm cannot inject into the wrong session. - theme + Dynamic Type (T-iOS-34) and web ?join= interop (T-iOS-35). RootView no longer hard-locks .preferredColorScheme(.dark). Also unpins SwiftTerm to 1.15.0 by dropping the local hasActiveSelection that collided with the upstream one, verified green from a fresh derivedDataPath. Includes the two HIGH fixes the security review found: - iOS resolved the WS token host-independently, so a token-gated host sitting next to an open one could never open a terminal and no on-screen remedy could fix it. Now one transport per host; cross-host leakage is structurally impossible since both read paths return only that host's own value. - Android reported the host's own git-credential 401 (git-ops.ts:108, "Push authentication required on the host.") as "your access token is wrong", because a blanket 401 mapping ran ahead of the per-route one. Git-write routes are now ROUTE_DEFINED and keep the server's message. And the doc sync: README/ios README no longer claim the client is unmerged on feat/ios-client, the Clients section finally lists Android, and the plan checkboxes reflect what is actually built. iOS 534 app tests + 452 package tests; Android 687 tests.
This commit is contained in:
@@ -191,10 +191,18 @@ final class PushRegistrar {
|
||||
logger.error("remote notification registration failed: \(error)")
|
||||
}
|
||||
|
||||
/// 主机移除时注销该主机上的 device token(**additive hook**:当前 App
|
||||
/// 层尚无移除主机的 UI 路径——`HostStore.remove(id:)` 无消费者;未来的
|
||||
/// 移除路径应调用本方法。失败仅记日志:服务器侧对失效 token 也会经
|
||||
/// APNs 410 自行清理)。
|
||||
/// 主机移除时注销该主机上的 device token。
|
||||
///
|
||||
/// C1 · 这个钩子曾经**没有调用方**(App 层没有移除主机的 UI,
|
||||
/// `HostStore.remove(id:)` 无消费者),于是 APNs token 永远留在被移除的主机
|
||||
/// 上。现在接线是:配对页「已配对主机」→ `PairingViewModel.removeHost(id:)`
|
||||
/// → `Probe.unregisterPush` → `PushHostDeregistration.run(for:)`(在
|
||||
/// `AppEnvironment` 里解析到 `PushAppDelegate` 持有的**活**实例——device
|
||||
/// token 只在内存里,只有它知道)→ 本方法 → 之后才写存储删除主机。
|
||||
///
|
||||
/// 顺序是有意的:请求先发出,此时主机记录(以及它的访问令牌,令牌门后的
|
||||
/// 主机需要它才能通过 401)还在。失败仅记日志:用户要的是移除,且服务器侧
|
||||
/// 对失效 token 也会经 APNs 410 自行清理。
|
||||
func handleHostRemoved(_ host: HostRegistry.Host) async {
|
||||
registeredHostIds.remove(host.id)
|
||||
guard let token = currentTokenHex else { return }
|
||||
|
||||
Reference in New Issue
Block a user