* test: E2E coverage for directory picker, all-sessions, variant picker Extend the Maestro suite for the features merged into main today: DirectoryBrowserSheet's server-folder picker, the directory-less all-sessions-across-projects list (+ the #46/#48 open-across-project regression), and VariantPicker's reasoning-effort chip. - tests/fixtures/mock-opencode-server.ts: GET /file (directory-scoped via the x-opencode-directory header) with a small fake tree, GET /project for the "Server Projects" section, POST /session honoring the directory header, GET /session/:id (needed to open a session from the all-sessions list), GET /provider variants for VariantPicker, and an optional --seed-sessions mode that pre-populates two sessions across two directories. --fail-auth mode is untouched. - .maestro/flows/directory-picker.yaml, all-sessions.yaml, variant-picker.yaml: three new flows, run in the same emulator session as the existing activation flows. - Additive testIDs on DirectoryBrowserSheet, the "Browse Folders" row, session list rows, the variant chip, and VariantPicker rows. - .github/workflows/activation-e2e.yml: two more mock server instances (4098 seeded, 4099 fresh) and three more maestro test steps. Verified: tsc --noEmit clean, all 108 existing unit tests pass, every new mock endpoint curled against its real shape read from the app code, YAML validated. No Android emulator available locally to run the Maestro flows themselves. * test(mock): enforce per-directory session scoping so #46/#48 coverage can fail Review finding (HIGH): GET /session/:id and /session/:id/message ignored x-opencode-directory, so all-sessions.yaml could not fail if the directory threading fix regressed. The mock now mirrors the real server's per-directory workspace scoping: - GET /session/:id and GET /session/:id/message 404 unless the request's x-opencode-directory (or DEFAULT_DIRECTORY when absent) matches the stored session's directory. - GET /session without ?roots=true is scoped to the request's directory; loadSessions()'s directory-less roots=true call still returns everything. - Document the port-4099 shared-state coupling between directory-picker and variant-picker flows, and why all-sessions.yaml now has teeth (flow comment). Curl-verified: correct header 200, wrong/no header 404, scoped vs roots listing, create-then-open paths for all three flows, --fail-auth untouched. tsc clean, 108/108 unit tests pass. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NJKAQ6HAikWGQK7PGZ5Y4E * fix(e2e): widen connect-handshake wait past client's own 30s timeout Run 29546383612 (7cbd3a6, first real emulator execution of these flows) failed on activation-positive.yaml: "Assert that id: connection-status-dot is visible" timed out after the flow's 20s extendedWaitUntil, right after tapOn connect-submit-button. The mock server itself is fast (verified locally: health + project/current + path respond in ~30ms total), so this isn't a mock fidelity gap. But Quick Connect's testConnection()/addConnection() path chains up to 3 fetches (health, then project.current + path.get in parallel), and each individual fetch is capped by src/lib/sdk.ts REQUEST_TIMEOUT_MS = 30_000 — strictly longer than the 20s the flow was willing to wait. A first-attempt emulator-to-host (10.0.2.2) connection that's merely slow to establish, rather than outright failing, would blow past the test's wait before the app's own client-side timeout even fires. Bump the connect -> connection-status-dot / "Connection Failed" waits from 20000 to 40000 across all 5 flows that share this pattern (activation-positive, activation-negative-401, all-sessions, directory-picker, variant-picker) so the wait is never shorter than the code path it's gating on. Assertions are unchanged — still requires the real dot / real error text, just with a timeout that isn't racing the client. Verified locally: typecheck clean, all 108 unit tests pass, YAML parses, mock server confirmed fast under direct curl. Emulator behavior itself (whether 40s consistently clears it) is unverified until the next CI run. * fix(e2e): use adb reverse + 127.0.0.1 instead of 10.0.2.2; capture logcat/maestro debug Root cause of the activation-e2e failure (connect step timed out, ~0 requests reaching the mock): the 10.0.2.2 host alias is unreliable under the headless emulator-runner — the app's http://10.0.2.2:4096/global/health never completed, so connection-status-dot never rendered. - run-e2e-flows.sh: single script (fixes cd-per-line fragility) that adb-reverses each mock port (4096-4099) into the emulator's localhost, runs every flow with --debug-output, and dumps logcat on exit. - All flows now connect to 127.0.0.1:<port> (the adb reverse target). - Upload maestro-debug (UI hierarchy on failure) + logcat as artifacts so future failures are diagnosable instead of blind. * fix(e2e): connect via 127.0.0.1:PORT in IP field, stop typing into port input Root cause of every activation-e2e connect failure (proven by the app's own logcat diagnostic: '[diag] probe start http://127.0.0.1:40966 ... server unreachable'): the port field defaults to useState("4096"), and the flow's eraseText + inputText "4096" raced the controlled number-pad input, leaving "40966" — nothing listens there, so connect always failed. This was never a 10.0.2.2 / adb reverse issue. Fix: buildUrl already extracts host:port from the IP field, so enter 127.0.0.1:<port> there and remove the flaky port-field steps entirely. pastedPort overrides the default port state, so each flow's port is deterministic (4096 positive / 4097 negative / 4098 all-sessions / 4099 directory+variant). --------- Co-authored-by: engineer <engineer@gray-knight-m1.local> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
OpenCode Mobile
The open-source Android client for the opencode AI coding agent. AI-assisted coding from your phone — Android, via F-Droid or a direct APK.
Not affiliated with opencode. OpenCode Mobile is an independent, community-built client and is not made by, endorsed by, or affiliated with the opencode / Anomaly team. It talks to an opencode server you run yourself, using opencode's open HTTP API.
Install (Android)
There are two working ways to install OpenCode Mobile today, both for Android:
-
F-Droid (recommended) — add our self-hosted repo to any F-Droid client, then install/update from there:
https://dzianisv.github.io/opencode-mobile/fdroid/repoIn the F-Droid app: Settings → Repositories → + (add) and paste the URL above. Current version: v0.4.3.
-
Direct signed APK — download the latest release and install it manually: https://github.com/dzianisv/opencode-mobile/releases/latest
iOS is not available (see Roadmap). Google Play is in internal testing only (no public listing yet). IzzyOnDroid submission is pending.
OpenCode Mobile is a React Native / Expo app that brings the power of the opencode AI coding agent to your phone. Connect to your own self-hosted opencode server over your local network, a Cloudflare Tunnel, ngrok, or Tailscale — and write, review, and ship code from anywhere. The mobile client is free and open-source under the MIT license. There is no feature gate, no telemetry you did not opt into, and no ad network.
Real on-device capture: add a connection, browse sessions, and watch the agent stream a response. Verified end-to-end on an Android emulator against a live opencode server (build cc.agentlabs.opencode).
Features
- Multi-connection — manage multiple opencode servers (local network, Cloudflare Tunnel, ngrok, or Tailscale)
- Biometric unlock — Face ID, Touch ID, or Android fingerprint protects the app and individual message sends
- Streaming chat — token-by-token streaming responses directly from your opencode server
- Diff viewer — inline side-by-side diffs of every file change the agent makes
- Tool call approval — review and approve (or reject) tool calls before the agent executes them
- Secure credential storage — server credentials stored in the Android Keystore via
expo-secure-store - Session management — browse, create, and resume coding sessions
Get OpenCode Mobile
Package: cc.agentlabs.opencode · Android only · current version v0.4.3
| Channel | Status | How |
|---|---|---|
| F-Droid (self-hosted repo) | Live | Add https://dzianisv.github.io/opencode-mobile/fdroid/repo in your F-Droid client |
| Direct APK | Live | github.com/dzianisv/opencode-mobile/releases/latest |
| Google Play | Internal testing / coming soon | No public listing yet |
| IzzyOnDroid | Submission pending | Not live yet |
| Apple App Store / iOS | Not available | See Roadmap |
The two live, supported install channels are the F-Droid self-hosted repo and the direct signed APK, both Android. Google Play is internal-testing only, IzzyOnDroid is pending, and there is no iOS build.
Quick Start
Step 1 — Start opencode on your machine
# Install opencode (if you haven't already)
npm install -g opencode
# Run opencode in server mode
OPENCODE_SERVER_PASSWORD=yourpassword opencode serve --hostname 0.0.0.0 --port 4096
Step 2 — Install OpenCode Mobile via the F-Droid self-hosted repo or direct APK (or build from source — see CONTRIBUTING.md).
Step 3 — Add a connection in the app
Open the app, tap Add Connection, and choose your connection type:
- Local network — your machine's LAN IP, e.g.
http://192.168.1.100:4096 - Tunnel — a Cloudflare Tunnel or ngrok URL, e.g.
https://my-opencode.trycloudflare.com - Tailscale — your machine's Tailscale IP, e.g.
http://100.x.x.x:4096 - opencode Cloud (planned — not yet shipped) — one-tap managed hosting, no server to run
Enter the password you set in Step 1, tap Connect, and you're in.
How It Works
OpenCode Mobile is a thin client. It speaks the opencode HTTP + SSE API: listing sessions, sending messages, streaming responses, and subscribing to file-change events. All AI model calls are handled by your opencode server — you bring your own API keys (OpenAI, Anthropic, etc.) and the app never touches them. The app never proxies your code or conversation through our servers.
┌─────────────────────────────────────┐
│ OpenCode Mobile │
│ (React Native / Expo, this repo) │
└──────────────┬──────────────────────┘
│ HTTP + SSE
│ (local network / tunnel)
▼
┌─────────────────────────────────────┐
│ opencode server │
│ (github.com/sst/opencode, MIT) │
│ Running on your laptop / VPS │
└──────────────┬──────────────────────┘
│ API calls
▼
┌─────────────────────────────────────┐
│ Your AI provider │
│ (OpenAI / Anthropic / Gemini / …) │
│ Your keys, your bill │
└─────────────────────────────────────┘
Project Status
Current version: v0.4.3
| Feature | Status |
|---|---|
| Multi-connection management | Stable |
| Session list + creation | Stable |
| Streaming chat | Stable |
| Diff viewer | Stable |
| Biometric unlock | Stable |
| Tool call approval UI | Stable |
| Sentry crash reporting (opt-in) | Stable |
| Cloudflare / ngrok tunnel wizard | Beta |
| opencode Cloud one-tap connect | Planned |
| iPad / tablet layout | Planned |
| Offline session history | Planned |
Supporters and Sponsors
OpenCode Mobile is built and maintained by VIBE TECHNOLOGIES, LLC. GitHub Sponsors help cover Sentry, EAS Build, and CI costs (~$60/month). The opencode Cloud hosted backend (planned, $10/mo) is the long-term revenue model.
If OpenCode Mobile saves you time, consider sponsoring:
github.com/sponsors/VibeTechnologies
| Tier | Price | Perk |
|---|---|---|
| Supporter | $5/mo | Your name in SUPPORTERS.md |
| Backer | $15/mo | Name + early access to opencode Cloud beta |
| Business | $50/mo | Logo on agentlabs.cc/opencode + quarterly support call |
Questions or private support: support@agentlabs.cc
Roadmap
Tracked on the GitHub Projects board and in the open milestones.
Near-term priorities:
- opencode Cloud one-tap connect + managed hosting
- F-Droid mainline acceptance (FCM audit + reproducible build verification)
- Tunnel setup wizard (Cloudflare / ngrok / Tailscale)
- iPad / tablet layout
- Offline session history cache
Contributing
We welcome bug reports, feature requests, and pull requests. See CONTRIBUTING.md for how to set up a dev environment and the contribution process.
Privacy
OpenCode Mobile does not collect personal data. Optional Sentry crash reporting (opt-in, off by default) sends anonymised crash traces to Sentry. No analytics SDKs are bundled. Credentials are stored exclusively on-device in the OS keystore.
Full privacy policy: dzianisv.github.io/opencode-mobile/privacy
License
MIT — see LICENSE.
Copyright (c) 2026 VIBE TECHNOLOGIES, LLC
Acknowledgments
- sst/opencode — the AI coding agent this app connects to (MIT)
- Expo — the React Native toolchain powering the app
- Every contributor who filed a bug, opened a PR, or starred the repo
