feat(runtime): restore mobile control page#1968
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces a built-in mobile control page for the codewhale serve command, accessible via a new --mobile flag. This feature enables thread management and tool approval resolution from mobile devices on the same LAN. Key changes include a new single-page HTML/JS interface, CLI updates to handle network binding, and expanded SSE event support. Reviewers recommended URL-encoding the runtime token in the displayed URLs and refining the LAN IP detection logic to avoid dependencies on hardcoded external DNS servers like 8.8.8.8.
| token | ||
| .filter(|token| !token.trim().is_empty()) | ||
| .map(|token| format!("?token={token}")) | ||
| .unwrap_or_default() |
There was a problem hiding this comment.
The token is appended to the URL without percent-encoding. If a manually configured token contains characters like &, #, or +, it will corrupt the query string or lead to authentication failures, as the server-side token_from_query (and the mobile page's own JS) expects correctly formatted parameters. Consider URL-encoding the token here for robustness.
|
|
||
| fn detect_lan_ip() -> Option<String> { | ||
| let socket = UdpSocket::bind("0.0.0.0:0").ok()?; | ||
| socket.connect("8.8.8.8:80").ok()?; |
There was a problem hiding this comment.
Hardcoding 8.8.8.8 for LAN IP detection might not work for users in regions where Google services are blocked (e.g., China), which is a key demographic for this project. Consider using a more neutral target or a non-routable IP like 10.255.255.255:1 to determine the default interface's local address without sending actual packets.
|
Independent review: Security model — main concern
Code quality
v0.8.48 conflict: only README.md / README.zh-CN.md conflict against Suggest: warn-log the host rebind, add a test for |
dcc28df to
c8c5e52
Compare
|
Addressed the review in
Local validation:
GitHub checks are green on the updated PR head. |
|
Handled the Greptile P2 follow-ups in
Validation:
GitHub checks are green again on the latest head. |
|
wow! this is amazing! thank you for building this. will work on getting this in asap |
|
This mobile-control-page work is still interesting, thank you for putting the branch together. I am leaving it open because it is currently conflicting with Please rebase and rerun the full matrix, then smoke |
|
I rebased this onto current main and resolved the one conflict in |
|
Thank you again @axobase001. I refreshed this onto current main after the skills API and Baidu search merges moved nearby runtime API imports. The conflict stayed tiny and the mobile page behavior is still intact. Maintainer verification on the updated head (
CI is running again now; if the matrix stays green I think this one is ready to land. |
|
Merged at 9716619. Thank you @axobase001 — this was a substantial runtime/UI contribution, and the extra retries/null-response hardening made it much safer to land. The final rerun passed Windows too, so the mobile control page is now in |
Rebased replacement for #852 after the v0.8.41 CodeWhale rebrand.
What changed:
codewhale serve --mobileas a thin mobile/LAN entry point over the existing runtime HTTP/SSE API/mobilecontrol page from a separateruntime_mobile.htmlfile viainclude_str!/v1/approvals/{approval_id}bridge instead of restoring the old per-turn approval routesValidation:
cargo fmt --allgit diff --checkcargo check -p codewhale-tui --lockedcargo test -p codewhale-tui --bin codewhale-tui --locked runtime_api::tests::codewhale-tui serve --mobile --port 7879 --insecure,GET /mobilereturned HTTP 200 with titleCodeWhale MobileNote:
cargo test -p codewhale-tui --locked runtime_api::tests::also passed the runtime API module tests, but the full cargo invocation then tried to launch the filteredskill_installintegration binary on Windows and hit OS error 740 (requires elevation). The bin-scoped command above avoids that unrelated Windows integration-test launcher issue.Greptile Summary
This PR adds
codewhale serve --mobileas a thin LAN entry point: it starts the existing HTTP/SSE runtime API and serves a compiled-in mobile control page at/mobile. The approach reuses the current/v1/approvals/{id}bridge and the existing token-guard middleware rather than restoring any old auth code.--mobileflag (mutually exclusive with--http) defaults the bind host to0.0.0.0with an explicit CLI warning, and passesmobile: boolthrough toRuntimeApiOptions/RuntimeApiStateso the/mobileroute returns 404 when the flag is absent.runtime_mobile.html(embedded viainclude_str!) is a single-file mobile page with thread listing, SSE live-stream, prompt/steer/interrupt controls, and token-gated approval buttons; all server-supplied text is HTML-escaped before insertion.percent_decode_query_componenthelper so tokens with special characters survive the?token=query parameter path.Confidence Score: 5/5
Safe to merge. The mobile feature is well-contained, reuses the existing auth middleware correctly, and the new token percent-decode path is covered by tests.
The auth logic for /mobile (inline check using the same request_has_runtime_token helper as the middleware) is consistent and correct. XSS is addressed throughout the HTML page. The percent-decode/encode round-trip for special-character tokens is tested end-to-end. The only issues found are display-only: the LAN IP detection probe address and a JSON-vs-HTML auth error for direct browser navigation.
No files require special attention. The two suggestions are in runtime_api.rs but neither affects correctness or security.
Important Files Changed
Sequence Diagram
sequenceDiagram participant User as Mobile Browser participant Server as codewhale serve --mobile Note over Server: binds 0.0.0.0 (default)<br/>prints Local + LAN URLs with ?token= User->>Server: "GET /mobile?token=XYZ" alt "mobile_enabled && token valid" Server-->>User: 200 HTML (MOBILE_HTML) else token missing/invalid Server-->>User: 401 JSON error end Note over User: JS stores token to localStorage<br/>removes from URL (history.replaceState) User->>Server: GET /v1/threads/summary (Bearer token) Server-->>User: JSON thread list User->>Server: POST /v1/threads (Bearer token) Server-->>User: 201 thread object User->>Server: "GET /v1/threads/{id}/events?since_seq=0&token=XYZ (EventSource)" loop SSE stream Server-->>User: approval.required event User->>Server: "POST /v1/approvals/{id} {decision, remember}" Server-->>User: "200 {decision}" Server-->>User: approval.decided event end User->>Server: "POST /v1/threads/{id}/turns/{tid}/interrupt" Server-->>User: 200Reviews (4): Last reviewed commit: "fix(runtime): restore mobile retry guard..." | Re-trigger Greptile