fix(ci): CUA smoke true-E2E with local opencode server (#15) (#18)

* chore: repoint OpenCode links to agentlabs.cc/opencode

agentlabs.cc/opencode and /opencode/privacy are now live (200). Repoint
README, distribution listings (Play/App Store/F-Droid/IzzyOnDroid/iOS),
docs, and in-app privacy links (settings + telemetry consent) from
www.vibebrowser.app/opencode to the canonical agentlabs.cc hub.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(ci): run local opencode server for CUA smoke true-E2E (#15)

GitHub-hosted runners can't reach the Tailscale dev server
(100.108.64.76:4096), so the CUA smoke always failed at session creation.

- Install opencode-ai and run `opencode serve` on the runner host; the
  Android emulator reaches it via 10.0.2.2. OPENCODE_URL now points there.
- Healthcheck /global/health before launching the app; dump server log on
  failure for diagnosis.
- Add --only-connect-scenario to the smoke script and run just the
  connect-and-verify-sessions path in CI: deterministic, needs no model
  backend. The scenario now creates a session if the list is empty, so a
  fresh server still yields a non-empty list.

This makes the smoke a true E2E and also exercises the #10 sessions-list
rendering path against a real server.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(ci): emulator smoke script is dash, not bash — drop brace-group healthcheck

android-emulator-runner runs the script: block under /usr/bin/sh (dash). The
multi-line `|| { ...; }` healthcheck was a dash syntax error (end of file
unexpected), failing the step before the smoke ran. Replace with a non-fatal
one-line re-check; the server was already health-gated in the prior step.

* docs(tasks): record smoke CI round 1 failure + dash fix

---------

Co-authored-by: engineer <engineer@opencode.ai>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Den
2026-06-01 15:32:06 -07:00
committed by GitHub
parent d1888dc392
commit c9a57901c4
13 changed files with 95 additions and 24 deletions

View File

@@ -19,7 +19,9 @@ jobs:
AZURE_OPENAI_API_KEY: ${{ secrets.AZURE_OPENAI_API_KEY }} AZURE_OPENAI_API_KEY: ${{ secrets.AZURE_OPENAI_API_KEY }}
AZURE_OPENAI_ENDPOINT: ${{ secrets.AZURE_OPENAI_ENDPOINT }} AZURE_OPENAI_ENDPOINT: ${{ secrets.AZURE_OPENAI_ENDPOINT }}
AZURE_OPENAI_API_VERSION: "2024-08-01-preview" AZURE_OPENAI_API_VERSION: "2024-08-01-preview"
OPENCODE_URL: "http://100.108.64.76:4096" # Android emulator reaches the runner host loopback via 10.0.2.2.
# A real `opencode serve` runs on the host (see steps below), making this a true E2E.
OPENCODE_URL: "http://10.0.2.2:4096"
ARCHIVEBOX_URL: ${{ secrets.ARCHIVEBOX_URL }} ARCHIVEBOX_URL: ${{ secrets.ARCHIVEBOX_URL }}
ARCHIVEBOX_API_KEY: ${{ secrets.ARCHIVEBOX_API_KEY }} ARCHIVEBOX_API_KEY: ${{ secrets.ARCHIVEBOX_API_KEY }}
steps: steps:
@@ -67,6 +69,22 @@ jobs:
- name: Install Python deps - name: Install Python deps
run: pip install openai run: pip install openai
- name: Install & start opencode server on runner host
run: |
npm install -g opencode-ai
# Bind all interfaces so the emulator can reach it via 10.0.2.2.
nohup opencode serve --hostname 0.0.0.0 --port 4096 > /tmp/opencode-server.log 2>&1 &
echo "Waiting for opencode server /global/health ..."
for i in $(seq 1 60); do
if curl -sf http://127.0.0.1:4096/global/health > /dev/null; then
echo "opencode server healthy after ${i}s"; break
fi
sleep 1
done
curl -sf http://127.0.0.1:4096/global/health || {
echo "::error::opencode server failed to become healthy"; cat /tmp/opencode-server.log; exit 1;
}
- name: Run CUA smoke test with emulator - name: Run CUA smoke test with emulator
uses: reactivecircus/android-emulator-runner@v2 uses: reactivecircus/android-emulator-runner@v2
with: with:
@@ -74,11 +92,20 @@ jobs:
arch: x86_64 arch: x86_64
target: google_apis target: google_apis
emulator-options: -no-window -no-audio -no-boot-anim -gpu swiftshader_indirect -no-snapshot emulator-options: -no-window -no-audio -no-boot-anim -gpu swiftshader_indirect -no-snapshot
# NOTE: android-emulator-runner runs this script with /usr/bin/sh (dash).
# Avoid bash-only constructs like multi-line `|| { ... }` brace groups.
script: | script: |
# Non-fatal re-check; server health was already gated in the prior step.
curl -sf http://127.0.0.1:4096/global/health || echo "WARN: opencode health re-check failed (started in prior step)"
adb install android/app/build/outputs/apk/release/app-release.apk adb install android/app/build/outputs/apk/release/app-release.apk
adb shell am start -n cc.agentlabs.opencode/.MainActivity adb shell am start -n cc.agentlabs.opencode/.MainActivity
sleep 5 sleep 5
python3 scripts/android-cua-smoke.py --model gpt-5.4 --include-xml # True E2E: app connects to the local opencode server (10.0.2.2:4096) and verifies the session list.
python3 scripts/android-cua-smoke.py --model gpt-5.4 --include-xml --only-connect-scenario
- name: opencode server log
if: always()
run: cat /tmp/opencode-server.log || true
- name: Upload test artifacts - name: Upload test artifacts
if: always() if: always()

30
.tasks/15/STATE.md Normal file
View File

@@ -0,0 +1,30 @@
# Task 15 — STATE
- phase: 3-design
- issue: #15 (CUA smoke can't reach opencode server in CI)
- branch: fix/cua-smoke-local-opencode-server
- supervisor: opus 4.8 (solo-founder loop)
## Success metric (R1)
GitHub Actions `CUA Smoke Test` workflow run on the branch ends with
`All N scenarios passed.` (exit 0), where the connect-and-verify-sessions
scenario connected the app to a real opencode server running on the runner
and saw a non-empty session list. Observable in the Actions run log.
## Approach
- Run real `opencode serve` on runner; app (emulator) reaches it via 10.0.2.2.
- Add `--only-connect-scenario` to smoke script; enhance connect goal to
create a session so a fresh empty server still yields a non-empty list.
- This also gives E2E verification for #10 (sessions list after connect).
## Local de-risk (2026-06-01)
- opencode-ai@1.15.13 installs; `opencode serve` healthy in 1s.
- GET /global/health -> {"healthy":true}; /project/current 200; /path 200.
- POST /session then GET /session returns the created session. Flow validated.
- phase: 5-implemented, ready for CI verify.
## CI verify round 1 (FAILED -> fixed)
- run 26750362939: build OK, opencode server step healthy, but emulator
script died with dash syntax error on `|| { ...; }` brace group before smoke ran.
- Fix 42e60db: emulator script: block runs under /usr/bin/sh; replaced brace
group with non-fatal one-line re-check.
- Re-dispatched: run 26751422570.

View File

@@ -124,7 +124,7 @@ OpenCode Mobile is a thin client. It speaks the opencode HTTP + SSE API: listing
## Supporters and Sponsors ## Supporters and Sponsors
OpenCode Mobile is built and maintained by [VIBE TECHNOLOGIES, LLC](https://www.vibebrowser.app/opencode). 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. OpenCode Mobile is built and maintained by [VIBE TECHNOLOGIES, LLC](https://agentlabs.cc/opencode). 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: If OpenCode Mobile saves you time, consider sponsoring:
@@ -134,7 +134,7 @@ If OpenCode Mobile saves you time, consider sponsoring:
|---|---|---| |---|---|---|
| Supporter | $5/mo | Your name in `SUPPORTERS.md` | | Supporter | $5/mo | Your name in `SUPPORTERS.md` |
| Backer | $15/mo | Name + early access to opencode Cloud beta | | Backer | $15/mo | Name + early access to opencode Cloud beta |
| Business | $50/mo | Logo on [agentlabs.cc/opencode](https://www.vibebrowser.app/opencode) + quarterly support call | | Business | $50/mo | Logo on [agentlabs.cc/opencode](https://agentlabs.cc/opencode) + quarterly support call |
Questions or private support: [support@vibebrowser.app](mailto:support@vibebrowser.app) Questions or private support: [support@vibebrowser.app](mailto:support@vibebrowser.app)

View File

@@ -202,7 +202,7 @@ export default function SettingsScreen() {
label="Privacy Policy" label="Privacy Policy"
description="What data we collect and how" description="What data we collect and how"
isDark={isDark} isDark={isDark}
onPress={() => Linking.openURL("https://www.vibebrowser.app/opencode-mobile/privacy")} onPress={() => Linking.openURL("https://agentlabs.cc/opencode/privacy")}
right={<Ionicons name="open-outline" size={20} color={isDark ? "#666666" : "#999999"} />} right={<Ionicons name="open-outline" size={20} color={isDark ? "#666666" : "#999999"} />}
/> />
</SettingSection> </SettingSection>

View File

@@ -101,7 +101,7 @@ Alternative / supplemental terms to rotate in A/B: `code review`, `AI assistant`
| Field | Value | | Field | Value |
|---|---| |---|---|
| Support URL | https://www.vibebrowser.app/opencode | | Support URL | https://agentlabs.cc/opencode |
| Marketing URL | https://github.com/dzianisv/opencode-mobile | | Marketing URL | https://github.com/dzianisv/opencode-mobile |
| Privacy Policy URL | https://opencode.vibebrowser.app/privacy | | Privacy Policy URL | https://opencode.vibebrowser.app/privacy |

View File

@@ -21,7 +21,7 @@ License: MIT
AuthorName: VIBE TECHNOLOGIES, LLC AuthorName: VIBE TECHNOLOGIES, LLC
AuthorEmail: support@vibebrowser.app AuthorEmail: support@vibebrowser.app
WebSite: https://www.vibebrowser.app/opencode WebSite: https://agentlabs.cc/opencode
SourceCode: https://github.com/dzianisv/opencode-mobile SourceCode: https://github.com/dzianisv/opencode-mobile
IssueTracker: https://github.com/dzianisv/opencode-mobile/issues IssueTracker: https://github.com/dzianisv/opencode-mobile/issues
Changelog: https://github.com/dzianisv/opencode-mobile/releases Changelog: https://github.com/dzianisv/opencode-mobile/releases

View File

@@ -18,7 +18,7 @@ Reference: https://developer.apple.com/programs/enroll/
| Primary Contact / Authorized Agent | Dzianis Vashchuk | Governor of LLC | | Primary Contact / Authorized Agent | Dzianis Vashchuk | Governor of LLC |
| Primary Contact Phone | (use a number reachable for Apple's verification call) | | | Primary Contact Phone | (use a number reachable for Apple's verification call) | |
| Primary Contact Email | support@vibebrowser.app | | | Primary Contact Email | support@vibebrowser.app | |
| Website | https://www.vibebrowser.app/opencode | | | Website | https://agentlabs.cc/opencode | |
--- ---
@@ -62,7 +62,7 @@ Fill as follows:
| ZIP | `98108` | | ZIP | `98108` |
| Country | `United States` | | Country | `United States` |
| Phone | (Dzianis's direct mobile — Apple calls this) | | Phone | (Dzianis's direct mobile — Apple calls this) |
| Website | `https://www.vibebrowser.app/opencode` | | Website | `https://agentlabs.cc/opencode` |
### Step 4 — Verify Your Authority ### Step 4 — Verify Your Authority
@@ -113,7 +113,7 @@ While waiting for Apple's verification call and approval:
- [ ] Create app icon 1024×1024 PNG - [ ] Create app icon 1024×1024 PNG
- [ ] Capture iPhone screenshots (use iOS Simulator in Xcode on any Mac) - [ ] Capture iPhone screenshots (use iOS Simulator in Xcode on any Mac)
- [ ] Capture iPad screenshots - [ ] Capture iPad screenshots
- [ ] Write/publish privacy policy at https://www.vibebrowser.app/opencode-mobile/privacy - [ ] Write/publish privacy policy at https://agentlabs.cc/opencode/privacy
- [ ] Set up EAS account at https://expo.dev/ (free tier, log in with Expo account) - [ ] Set up EAS account at https://expo.dev/ (free tier, log in with Expo account)
- [ ] Add iOS config patches to `app.json` (done in this PR) - [ ] Add iOS config patches to `app.json` (done in this PR)
- [ ] Run `npx expo prebuild --platform ios` on a Mac to validate the Xcode project - [ ] Run `npx expo prebuild --platform ios` on a Mac to validate the Xcode project

View File

@@ -106,5 +106,5 @@ We will notify the IzzyOnDroid team via this issue when that happens.
Developer: VIBE TECHNOLOGIES, LLC Developer: VIBE TECHNOLOGIES, LLC
Email: support@vibebrowser.app Email: support@vibebrowser.app
Website: https://www.vibebrowser.app/opencode Website: https://agentlabs.cc/opencode
Privacy policy: https://opencode.vibebrowser.app/privacy Privacy policy: https://opencode.vibebrowser.app/privacy

View File

@@ -114,7 +114,7 @@ Issues: github.com/dzianisv/opencode-mobile/issues
| Tags | developer, ai, coding, open-source, productivity | Submit all five in Play Console. | | Tags | developer, ai, coding, open-source, productivity | Submit all five in Play Console. |
| Email | support@vibebrowser.app | Already verified during signup. | | Email | support@vibebrowser.app | Already verified during signup. |
| Phone | +1 360-504-8967 | Optional public; matches developer profile. | | Phone | +1 360-504-8967 | Optional public; matches developer profile. |
| Website | https://www.vibebrowser.app/opencode | Already verified. | | Website | https://agentlabs.cc/opencode | Already verified. |
--- ---

View File

@@ -106,7 +106,7 @@ All diagnostic data is transmitted over HTTPS (TLS 1.2+) to Sentry. We do not tr
## 10. Changes to This Policy ## 10. Changes to This Policy
If we make material changes to this policy, we will update the effective date and, where feasible, notify users via an in-app notice. The latest version is always available at: If we make material changes to this policy, we will update the effective date and, where feasible, notify users via an in-app notice. The latest version is always available at:
https://www.vibebrowser.app/opencode-mobile/privacy https://agentlabs.cc/opencode/privacy
--- ---

View File

@@ -152,7 +152,7 @@ When the opencode AI agent requests a file-access permission, the notification b
**Files:** `app.json:29`, `app.json:38-39` **Files:** `app.json:29`, `app.json:38-39`
**Description:** Both platforms allow HTTP connections, which is required for local/LAN servers. This is intentional and correct for the use case. However, neither the Play Store listing, App Store listing, nor a privacy policy document currently explains that HTTP connections may be made to user-provided servers. Google Play's Data Safety section and Apple's App Privacy report will flag arbitrary network access if not documented. **Description:** Both platforms allow HTTP connections, which is required for local/LAN servers. This is intentional and correct for the use case. However, neither the Play Store listing, App Store listing, nor a privacy policy document currently explains that HTTP connections may be made to user-provided servers. Google Play's Data Safety section and Apple's App Privacy report will flag arbitrary network access if not documented.
**Remediation:** **Remediation:**
1. Update the privacy policy at `vibebrowser.app/opencode-mobile/privacy` to explain that the app connects to user-configured server addresses that may use HTTP. 1. Update the privacy policy at `agentlabs.cc/opencode/privacy` to explain that the app connects to user-configured server addresses that may use HTTP.
2. In Play Store Data Safety: disclose "Other app performance data" collected (crash reports via Sentry — opt-in). 2. In Play Store Data Safety: disclose "Other app performance data" collected (crash reports via Sentry — opt-in).
**Status:** Open **Status:** Open

View File

@@ -603,8 +603,11 @@ def _connect_and_verify_sessions_goal(url: str) -> str:
"Wait 3 seconds. " "Wait 3 seconds. "
"Now navigate to the Sessions tab (bottom navigation bar). " "Now navigate to the Sessions tab (bottom navigation bar). "
"Wait 5 seconds for sessions to load. " "Wait 5 seconds for sessions to load. "
"If the sessions list is empty or shows 'No sessions yet', tap the '+' button "
"(top-right) to create a new session, wait 3 seconds, then navigate back to the "
"Sessions tab and wait 3 seconds for the list to refresh. "
"Report SUCCESS if you see at least one session listed (a session title is visible). " "Report SUCCESS if you see at least one session listed (a session title is visible). "
"Report FAILURE if the sessions list is empty, shows 'No sessions yet', or shows an error." "Report FAILURE if the sessions list is still empty, shows 'No sessions yet', or shows an error."
) )
@@ -625,6 +628,12 @@ def main():
action="store_true", action="store_true",
help="Skip the default connect-and-verify regression scenario.", help="Skip the default connect-and-verify regression scenario.",
) )
parser.add_argument(
"--only-connect-scenario",
action="store_true",
help="Run ONLY the connect-and-verify-sessions scenario. Use in CI with a "
"local opencode server for a deterministic true-E2E (no model backend needed).",
)
args = parser.parse_args() args = parser.parse_args()
# Verify ADB # Verify ADB
@@ -635,15 +644,20 @@ def main():
except FileNotFoundError: except FileNotFoundError:
sys.exit("adb not found in PATH") sys.exit("adb not found in PATH")
scenarios = [{"name": "custom", "goal": args.goal}] if args.goal else list(SMOKE_SCENARIOS)
# Keep connect-and-verify in the default smoke path so regressions are exercised.
if not args.goal and not args.skip_connect_scenario:
connect_url = args.opencode_url or os.environ.get("OPENCODE_URL") or "http://100.108.64.76:4096" connect_url = args.opencode_url or os.environ.get("OPENCODE_URL") or "http://100.108.64.76:4096"
scenarios.append({ connect_scenario = {
"name": "connect_and_verify_sessions", "name": "connect_and_verify_sessions",
"goal": _connect_and_verify_sessions_goal(connect_url), "goal": _connect_and_verify_sessions_goal(connect_url),
}) }
if args.only_connect_scenario:
# CI true-E2E: just connect to the local opencode server and verify the list.
scenarios = [connect_scenario]
else:
scenarios = [{"name": "custom", "goal": args.goal}] if args.goal else list(SMOKE_SCENARIOS)
# Keep connect-and-verify in the default smoke path so regressions are exercised.
if not args.goal and not args.skip_connect_scenario:
scenarios.append(connect_scenario)
results = [] results = []
for scenario in scenarios: for scenario in scenarios:

View File

@@ -46,7 +46,7 @@ export function TelemetryConsentModal({ visible, onAllow, onDecline }: Props) {
{/* Privacy policy link */} {/* Privacy policy link */}
<TouchableOpacity <TouchableOpacity
onPress={() => Linking.openURL("https://www.vibebrowser.app/opencode-mobile/privacy")} onPress={() => Linking.openURL("https://agentlabs.cc/opencode/privacy")}
> >
<Text style={styles.privacyLink}>Read our full privacy policy</Text> <Text style={styles.privacyLink}>Read our full privacy policy</Text>
</TouchableOpacity> </TouchableOpacity>