feat(clients): add a native Android client for Hermes Studio - #2307
feat(clients): add a native Android client for Hermes Studio#2307twuijri wants to merge 1 commit into
Conversation
The web UI is built for a desktop screen, and EKKOLearnAI#2184 asks for a phone client that is not a WebView wrapper. This adds one under clients/android: Kotlin and Compose, talking to this server over the endpoints and Socket.IO namespaces the web UI already uses. Nothing outside clients/android is touched and the server is unchanged: the app signs in with POST /api/auth/login, streams over /chat-run with the token in the Socket.IO handshake, posts into rooms over /group-chat, and reads and writes the same profile-scoped config the settings pages use — including the X-Hermes-Profile header. When the socket cannot connect it falls back to POST /api/chat-run/runs, so a reverse proxy that drops WebSocket upgrades costs the streaming, not the answer. Scope today: conversations with history, streaming replies with a stop button and the reasoning folded under each one, attachments, camera and voice through the profile's own STT, group rooms that can be created and posted into, scheduled jobs, channel credentials, and the settings screens that mirror Studio's tabs. Ten locales, right-to-left included. The build is self-contained (gradle assembleDebug, JDK 17, compileSdk 34) and adds no dependency to the Node workspace. tools/mock-studio.py answers the endpoints the app calls, so every screen can be opened without a running Hermes Studio.
|
@EKKOLearnAI — following up on this one. I'd rather have a decision than leave it sitting: do you want the Android client in this repo, or kept outside as a community client you link from the README? Either answer works for me and I'll keep maintaining it. In-tree means it ships and versions with Studio, and the protocol stays in sync automatically — the cost is a Kotlin app inside a Node monorepo that your CI doesn't build. Out-of-tree costs you nothing and keeps this repo single-language; the risk is drift whenever the API changes. If in-tree is the concern rather than the idea, I'm happy to reshape it however you prefer — separate directory, separate release workflow, or a thinner client. Since opening this I've also contributed #2310 and #2328, so the protocol side is something I'm already keeping current. No rush on my side — just tell me which direction and I'll follow it. |
Addresses the Android half of #2184. Opening it as code rather than a proposal so there is something concrete to accept, reshape, or turn down.
Everything here is additive. One new directory,
clients/android/. No existing file is modified, the server is unchanged, and the Node workspace gains no dependency — the app builds on its own withgradle assembleDebug(JDK 17, compileSdk 34)./chat-run, with the stop button, the reasoning folded underneath, and the tool the agent is running named while it works.platformEnvMapmaps, and saving lets the server restart the gateway itself.How it talks to this server
Nothing invented, nothing added server-side:
POST /api/auth/login·GET /api/auth/meGET /api/hermes/sessions·…/conversations/{id}/messages/chat-run—run,abort, with the bearer token in the handshakePOST /api/chat-run/runswhen the socket cannot connectGET/POST/DELETE /api/hermes/group-chat/rooms· Socket.IO/group-chatPOST /upload·POST /api/hermes/stt/transcribeGET/POST/PATCH/DELETE /api/hermes/jobs(+ pause, resume, run)/api/hermes/profiles·/api/hermes/available-models·/api/hermes/config(+/credentials)Profile-scoped calls carry
X-Hermes-Profile, the same as the web UI.What it does today
Bearer token in
EncryptedSharedPreferencesbacked by the Keystore. Traffic goes only to the address the user enters. No analytics, no hosted service in between.Reading order, if you review it
clients/android/app/src/main/java/us/i3u/hermesstudio/HermesApi.kt— every HTTP call the app makes, in one fileChatSocket.ktandGroupSocket.kt— the two namespaces and the fallbackclients/android/README.md— what works, and how to run it againsttools/mock-studio.pywithout a serverIf you would rather not take it in-tree
Entirely reasonable — a Kotlin app inside a Node monorepo is a real maintenance surface, and CI here does not build it. Two alternatives that cost you nothing:
I will keep maintaining it either way; the point of this PR is to let you choose with the code in front of you rather than in the abstract. Licensing: I have left my own LICENSE file out, so anything merged carries this repository's terms.
An iOS client would map almost one-to-one from
HermesApi.kt, and I am happy to help whoever wants to build it with the protocol side.