fix(funnel): repair activation-path bugs found in end-to-end audit (#114)

Systematic trace of the activation funnel (store listing -> docs setup
guide -> in-app onboarding -> connect flow) after #113 showed a docs
404 bug had slipped through. Fixes found in this pass:

- README.md and CONTRIBUTING.md still told developers to run
  `npm install -g opencode` (missing -ai), the exact 404 #113 fixed
  everywhere else. Same package-name fix applied here.
- src/lib/diagnostics.ts: the health probe reported ok:true for any
  resolved fetch, including 401/403/404/500 responses. A wrong
  password or broken server therefore classified as "Health endpoint
  responded - connection actually works now.", contradicting the
  error shown right next to it. Health now requires a real 2xx;
  root-reachability probe explicitly opts out (requireOk: false) to
  keep its existing reachability-only semantics (already encoded in
  diagnostics-classify.test.ts).
- docs-site/vs-termux: architecture explainer described
  /session/{id}/events and /session/{id}/chat, neither of which
  exists; the app actually uses /global/event and
  /session/{id}/prompt_async (src/lib/sdk.ts).
- docs-site/features: opencode repo link pointed at
  github.com/opencode-ai/opencode instead of github.com/sst/opencode,
  the org used everywhere else in the docs.
- Stale "v0.4.3" version strings (README x3, docs-site/download x2)
  bumped to v0.4.7, the actual latest shipped release confirmed live
  on GitHub Releases and the F-Droid repo index. Fixed the associated
  Android min-OS contradiction (7.0 vs 8.0+) to 7.0, matching the live
  F-Droid manifest's minSdkVersion 24.
- Dropped stale "GPT-4" model naming on 3 docs-site pages, consistent
  with the model-agnostic policy already applied to the Play listing
  in #83.

See PR body for the found-not-fixed list (Play Console listing drift,
GitHub Sponsors not enabled, a Cloud-vs-waitlist messaging conflict)
that need a product/human decision rather than a code fix.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Den
2026-07-17 21:47:13 -07:00
committed by GitHub
parent b802df9797
commit 3c88e16ae5
7 changed files with 25 additions and 18 deletions

View File

@@ -84,7 +84,7 @@ You need a running [opencode](https://github.com/sst/opencode) server to test th
```bash
# Install opencode
npm install -g opencode
npm install -g opencode-ai
# Start it in server mode on all interfaces
OPENCODE_SERVER_PASSWORD=devpassword opencode serve --hostname 0.0.0.0 --port 4096

View File

@@ -28,7 +28,7 @@ There are **three working ways** to install OpenCode Mobile today, all for Andro
```
https://dzianisv.github.io/opencode-mobile/fdroid/repo
```
In the F-Droid app: **Settings → Repositories → + (add)** and paste the URL above. Current version: **v0.4.3**.
In the F-Droid app: **Settings → Repositories → + (add)** and paste the URL above. Current version: **v0.4.7**.
3. **Direct signed APK** — download the latest release and install it manually:
**https://github.com/dzianisv/opencode-mobile/releases/latest**
@@ -64,7 +64,7 @@ OpenCode Mobile is a React Native / Expo app that brings the power of the [openc
## Get OpenCode Mobile
Package: `cc.agentlabs.opencode` · Android only · current version v0.4.3
Package: `cc.agentlabs.opencode` · Android only · current version v0.4.7
| Channel | Status | How |
|---|---|---|
@@ -86,7 +86,7 @@ Package: `cc.agentlabs.opencode` · Android only · current version v0.4.3
```bash
# Install opencode (if you haven't already)
npm install -g opencode
npm install -g opencode-ai
# Run opencode in server mode
OPENCODE_SERVER_PASSWORD=yourpassword opencode serve --hostname 0.0.0.0 --port 4096
@@ -137,7 +137,7 @@ OpenCode Mobile is a thin client. It speaks the opencode HTTP + SSE API: listing
## Project Status
**Current version: v0.4.3**
**Current version: v0.4.7**
| Feature | Status |
|---|---|

View File

@@ -198,7 +198,7 @@ td:first-child{font-weight:500}
</tr>
<tr>
<td>Bring your own LLM</td>
<td class="yes">Yes — any provider opencode supports (Claude, GPT-4, local models)</td>
<td class="yes">Yes — any provider opencode supports (Claude, GPT, Gemini, local models)</td>
<td class="no">OpenAI models only</td>
<td class="no">GitHub Copilot models only</td>
</tr>

View File

@@ -42,11 +42,11 @@
"@context": "https://schema.org",
"@type": "SoftwareApplication",
"name": "OpenCode Mobile",
"operatingSystem": "Android 8.0+",
"operatingSystem": "Android 7.0+",
"applicationCategory": "DeveloperApplication",
"description": "Open-source Android client for the OpenCode AI coding agent. Connect to an opencode server you run yourself and code from your phone.",
"downloadUrl": "https://github.com/dzianisv/opencode-mobile/releases/latest/download/app-release.apk",
"softwareVersion": "0.4.3",
"softwareVersion": "0.4.7",
"license": "https://github.com/dzianisv/opencode-mobile/blob/main/LICENSE",
"author": { "@type": "Organization", "name": "VIBE TECHNOLOGIES, LLC" },
"offers": { "@type": "Offer", "price": "0", "priceCurrency": "USD" }
@@ -171,7 +171,7 @@ footer a{margin:0 8px}
OpenCode Mobile is the free, open-source Android client for the
<a href="https://github.com/sst/opencode">OpenCode</a> AI coding agent. Install it from
the F-Droid repository for automatic updates, or grab the signed APK directly.
No account, no tracking, no ads. Current version <strong>0.4.3</strong>.
No account, no tracking, no ads. Current version <strong>0.4.7</strong>.
</p>
<div class="note">
<strong>Before you install:</strong> OpenCode Mobile is a <em>client</em> — it connects to an
@@ -252,7 +252,7 @@ footer a{margin:0 8px}
</p>
<h2>What do I need to run it?</h2>
<p>
An Android 8.0+ device, a machine running <code>opencode serve</code>, a way to reach that machine
An Android 7.0+ device, a machine running <code>opencode serve</code>, a way to reach that machine
from your phone (Tailscale, a Cloudflare/ngrok tunnel, or the same local network), and your own AI
provider key. The <a href="../guide/">setup guide</a> walks through every step in about 15 minutes.
</p>

View File

@@ -50,7 +50,7 @@
"offers": { "@type": "Offer", "price": "0", "priceCurrency": "USD" },
"featureList": [
"Real-time AI coding sessions via Server-Sent Events",
"Multi-model support: Claude, GPT-4, Gemini, Ollama, and any opencode-compatible model",
"Multi-model support: Claude, GPT, Gemini, Ollama, and any opencode-compatible model",
"Tailscale and VPN connectivity for private server access",
"Streaming diff and tool-use output as the agent works",
"Full session history with scroll and search",
@@ -234,7 +234,7 @@ footer a:hover{color:var(--accent)}
<h2>System requirements</h2>
<ul class="check-list">
<li>Android 7.0 (API 24) or later</li>
<li><a href="https://github.com/opencode-ai/opencode">opencode</a> running on a reachable server (<code>opencode serve</code>)</li>
<li><a href="https://github.com/sst/opencode">opencode</a> running on a reachable server (<code>opencode serve</code>)</li>
<li>Network path from phone to server (LAN, Tailscale VPN, or any tunnel)</li>
<li>~15 MB storage for the app; session history grows with use</li>
</ul>

View File

@@ -436,9 +436,9 @@ OPENCODE_SERVER_PASSWORD=yourpassword opencode serve --hostname 0.0.0.0 --port 4
happens:
</p>
<ol>
<li>The app opens an SSE stream to your opencode server's <code>/session/{id}/events</code> endpoint.</li>
<li>You type a prompt — the app posts it to <code>/session/{id}/chat</code>.</li>
<li>The server forwards the prompt to your configured LLM (Claude, GPT-4, a local model, whatever you set up).</li>
<li>The app opens an SSE stream to your opencode server's <code>/global/event</code> endpoint.</li>
<li>You type a prompt — the app posts it to <code>/session/{id}/prompt_async</code>.</li>
<li>The server forwards the prompt to your configured LLM (Claude, GPT, a local model, whatever you set up).</li>
<li>Tokens stream back from the LLM to the server, and from the server over SSE to the app.</li>
<li>When the agent proposes a file change, the app renders the diff and waits for your approve/reject tap.</li>
<li>Your approval goes back to the server, which applies the change to the real filesystem.</li>

View File

@@ -36,13 +36,20 @@ export interface DiagnosticReport {
timestamp: string
}
async function timedFetch(name: string, target: string, init?: RequestInit): Promise<ProbeAttempt> {
// requireOk: whether a non-2xx HTTP response counts as a probe failure.
// The health probe must actually succeed (2xx) to mean "the server works" —
// otherwise a 401/403/404/500 response was being reported as ok:true, which
// made classify() short-circuit to "connection actually works now" even when
// auth failed or the server errored. The root probe only checks reachability
// (any HTTP response, even an error status, proves something is listening).
async function timedFetch(name: string, target: string, init?: RequestInit, opts?: { requireOk?: boolean }): Promise<ProbeAttempt> {
const requireOk = opts?.requireOk ?? true
const start = Date.now()
const controller = new AbortController()
const timer = setTimeout(() => controller.abort(), PROBE_TIMEOUT_MS)
try {
const res = await fetch(target, { ...init, signal: controller.signal })
return { name, target, ok: true, status: res.status, durationMs: Date.now() - start }
return { name, target, ok: requireOk ? res.ok : true, status: res.status, durationMs: Date.now() - start }
} catch (error: unknown) {
const err = error as { name?: string; message?: string; cause?: unknown }
const aborted = err?.name === "AbortError"
@@ -80,7 +87,7 @@ export async function probeConnection(url: string, auth?: { username: string; pa
const base = `${parsed.scheme}://${parsed.host}:${parsed.port}`
;[health, root, internet] = await Promise.all([
timedFetch("health", `${base}/global/health`, { headers }),
timedFetch("server-root", `${base}/`, { headers }),
timedFetch("server-root", `${base}/`, { headers }, { requireOk: false }),
timedFetch("internet", INTERNET_CHECK_URL),
])
} else {