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:
@@ -84,7 +84,7 @@ You need a running [opencode](https://github.com/sst/opencode) server to test th
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Install opencode
|
# Install opencode
|
||||||
npm install -g opencode
|
npm install -g opencode-ai
|
||||||
|
|
||||||
# Start it in server mode on all interfaces
|
# Start it in server mode on all interfaces
|
||||||
OPENCODE_SERVER_PASSWORD=devpassword opencode serve --hostname 0.0.0.0 --port 4096
|
OPENCODE_SERVER_PASSWORD=devpassword opencode serve --hostname 0.0.0.0 --port 4096
|
||||||
|
|||||||
@@ -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
|
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:
|
3. **Direct signed APK** — download the latest release and install it manually:
|
||||||
**https://github.com/dzianisv/opencode-mobile/releases/latest**
|
**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
|
## 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 |
|
| Channel | Status | How |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
@@ -86,7 +86,7 @@ Package: `cc.agentlabs.opencode` · Android only · current version v0.4.3
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Install opencode (if you haven't already)
|
# Install opencode (if you haven't already)
|
||||||
npm install -g opencode
|
npm install -g opencode-ai
|
||||||
|
|
||||||
# Run opencode in server mode
|
# Run opencode in server mode
|
||||||
OPENCODE_SERVER_PASSWORD=yourpassword opencode serve --hostname 0.0.0.0 --port 4096
|
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
|
## Project Status
|
||||||
|
|
||||||
**Current version: v0.4.3**
|
**Current version: v0.4.7**
|
||||||
|
|
||||||
| Feature | Status |
|
| Feature | Status |
|
||||||
|---|---|
|
|---|---|
|
||||||
|
|||||||
@@ -198,7 +198,7 @@ td:first-child{font-weight:500}
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Bring your own LLM</td>
|
<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">OpenAI models only</td>
|
||||||
<td class="no">GitHub Copilot models only</td>
|
<td class="no">GitHub Copilot models only</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@@ -42,11 +42,11 @@
|
|||||||
"@context": "https://schema.org",
|
"@context": "https://schema.org",
|
||||||
"@type": "SoftwareApplication",
|
"@type": "SoftwareApplication",
|
||||||
"name": "OpenCode Mobile",
|
"name": "OpenCode Mobile",
|
||||||
"operatingSystem": "Android 8.0+",
|
"operatingSystem": "Android 7.0+",
|
||||||
"applicationCategory": "DeveloperApplication",
|
"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.",
|
"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",
|
"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",
|
"license": "https://github.com/dzianisv/opencode-mobile/blob/main/LICENSE",
|
||||||
"author": { "@type": "Organization", "name": "VIBE TECHNOLOGIES, LLC" },
|
"author": { "@type": "Organization", "name": "VIBE TECHNOLOGIES, LLC" },
|
||||||
"offers": { "@type": "Offer", "price": "0", "priceCurrency": "USD" }
|
"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
|
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
|
<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.
|
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>
|
</p>
|
||||||
<div class="note">
|
<div class="note">
|
||||||
<strong>Before you install:</strong> OpenCode Mobile is a <em>client</em> — it connects to an
|
<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>
|
</p>
|
||||||
<h2>What do I need to run it?</h2>
|
<h2>What do I need to run it?</h2>
|
||||||
<p>
|
<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
|
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.
|
provider key. The <a href="../guide/">setup guide</a> walks through every step in about 15 minutes.
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -50,7 +50,7 @@
|
|||||||
"offers": { "@type": "Offer", "price": "0", "priceCurrency": "USD" },
|
"offers": { "@type": "Offer", "price": "0", "priceCurrency": "USD" },
|
||||||
"featureList": [
|
"featureList": [
|
||||||
"Real-time AI coding sessions via Server-Sent Events",
|
"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",
|
"Tailscale and VPN connectivity for private server access",
|
||||||
"Streaming diff and tool-use output as the agent works",
|
"Streaming diff and tool-use output as the agent works",
|
||||||
"Full session history with scroll and search",
|
"Full session history with scroll and search",
|
||||||
@@ -234,7 +234,7 @@ footer a:hover{color:var(--accent)}
|
|||||||
<h2>System requirements</h2>
|
<h2>System requirements</h2>
|
||||||
<ul class="check-list">
|
<ul class="check-list">
|
||||||
<li>Android 7.0 (API 24) or later</li>
|
<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>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>
|
<li>~15 MB storage for the app; session history grows with use</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@@ -436,9 +436,9 @@ OPENCODE_SERVER_PASSWORD=yourpassword opencode serve --hostname 0.0.0.0 --port 4
|
|||||||
happens:
|
happens:
|
||||||
</p>
|
</p>
|
||||||
<ol>
|
<ol>
|
||||||
<li>The app opens an SSE stream to your opencode server's <code>/session/{id}/events</code> endpoint.</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}/chat</code>.</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-4, a local model, whatever you set up).</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>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>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>
|
<li>Your approval goes back to the server, which applies the change to the real filesystem.</li>
|
||||||
|
|||||||
@@ -36,13 +36,20 @@ export interface DiagnosticReport {
|
|||||||
timestamp: string
|
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 start = Date.now()
|
||||||
const controller = new AbortController()
|
const controller = new AbortController()
|
||||||
const timer = setTimeout(() => controller.abort(), PROBE_TIMEOUT_MS)
|
const timer = setTimeout(() => controller.abort(), PROBE_TIMEOUT_MS)
|
||||||
try {
|
try {
|
||||||
const res = await fetch(target, { ...init, signal: controller.signal })
|
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) {
|
} catch (error: unknown) {
|
||||||
const err = error as { name?: string; message?: string; cause?: unknown }
|
const err = error as { name?: string; message?: string; cause?: unknown }
|
||||||
const aborted = err?.name === "AbortError"
|
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}`
|
const base = `${parsed.scheme}://${parsed.host}:${parsed.port}`
|
||||||
;[health, root, internet] = await Promise.all([
|
;[health, root, internet] = await Promise.all([
|
||||||
timedFetch("health", `${base}/global/health`, { headers }),
|
timedFetch("health", `${base}/global/health`, { headers }),
|
||||||
timedFetch("server-root", `${base}/`, { headers }),
|
timedFetch("server-root", `${base}/`, { headers }, { requireOk: false }),
|
||||||
timedFetch("internet", INTERNET_CHECK_URL),
|
timedFetch("internet", INTERNET_CHECK_URL),
|
||||||
])
|
])
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user