Merge ios-completion: device builds unblocked, access token on both clients, P2 wave

Closes the six remediation items from the 2026-07-29 iOS completion audit plus the
whole P2 wave and Android access-token parity.

The audit's headline was that the client was code-complete but stuck at the device
door: no DEVELOPMENT_TEAM, no entitlements, so it had never run on real hardware
once, and it had fallen two months behind the server (Android had the git panel,
iOS had none) while neither native client could connect at all once WEBTERM_TOKEN
was set.

Package tests 310 -> 452, app bundle 296 -> 550 (iPhone and iPad, zero known
issues), integration 10 -> 32, Android 687 -> 691. ClientTLS went 55.76% -> 89.49%
and is now actually in the coverage gate, which it never was. Device build now
succeeds on the free personal team.

src/ and public/ are untouched — the git-panel endpoints already existed
server-side; iOS simply never consumed them.

# Conflicts:
#	android/.gitignore
#	android/README.md
#	android/api-client/src/main/kotlin/wang/yaojia/webterm/api/routes/Endpoints.kt
#	android/app/src/main/java/wang/yaojia/webterm/screens/PairingScreen.kt
#	android/app/src/main/java/wang/yaojia/webterm/viewmodels/PairingViewModel.kt
#	android/app/src/main/java/wang/yaojia/webterm/wiring/AppEnvironment.kt
#	android/transport-okhttp/src/main/kotlin/wang/yaojia/webterm/transport/OkHttpClientFactory.kt
#	docs/PROGRESS_LOG.md
This commit is contained in:
Yaojia Wang
2026-07-30 18:12:03 +02:00
174 changed files with 22184 additions and 666 deletions

View File

@@ -181,6 +181,30 @@ The `:macrobenchmark` module exists and assembles (`com.android.test`, instrumen
expiry summary is expected to throw `NoClassDefFoundError` on-device TODAY.
- [ ] DataStore host list / `LastSessionStore` set-on-adopted / clear-on-exited.
## Access token — `WEBTERM_TOKEN` (B5 / ios-completion §1.1) — needs a token-enabled host
Run the host as `WEBTERM_TOKEN=<16512 chars> npm start`. The pure logic is JVM-tested (`AuthProbeTest`,
`AccessTokenCookieTest`, `PairingProbeTokenTest`, `OkHttpAccessTokenTest`, `SessionEngineUnauthorizedTest`,
`InMemoryAccessTokenStoreTest`); everything below needs real hardware + a real server.
- [ ] `TinkAccessTokenStore` instrumented suite on a device:
`./gradlew :host-registry:connectedDebugAndroidTest` (real AndroidKeyStore + Tink).
- [ ] Pair a token-enabled host with the token typed → paired; the terminal attaches (the **WS upgrade**
carries `Cookie: webterm_auth=…`), the session list, projects, diff and thumbnails all load.
- [ ] Pair the same host with **no** token typed → the probe 401s → the confirm step comes back asking for
the token ("该主机启用了访问令牌"); typing it and retrying pairs.
- [ ] Wrong token → "访问令牌不正确" **under the field** (still on the confirm step, nothing stored).
- [ ] 11 wrong tries within a minute → "尝试过多" (the server's 10/min/IP `/auth` limiter).
- [ ] Pair a host with `WEBTERM_TOKEN` **unset** while typing a token → pairs, and NOTHING is stored
(204-without-`Set-Cookie`); the app must not claim to be "authenticated".
- [ ] Restart the app → the terminal re-attaches with no re-prompt (the token decrypts from the
Keystore-backed store on a cold start).
- [ ] Rotate `WEBTERM_TOKEN` on the host and restart it → the WS upgrade 401s → the terminal banner shows
the 401 copy, offers **no** "新会话", and there is **no reconnect back-off storm** (verify with
`adb logcat` / the server log: exactly one upgrade attempt).
- [ ] `adb logcat` during all of the above: the token string appears **nowhere**; no request URL contains
it (check the server's access log too).
- [ ] `adb backup` / device-to-device transfer excludes the token blob (`allowBackup=false` +
`data_extraction_rules.xml`).
## Nav / deep links / adaptive (A32/A26/A29)
- [ ] cold-start AND warm deep link `webterminal://open?host=&join=` + the verified App Link route to the
right destination; invalid UUID ignored. (The App Link half needs the release-signed APK and