Agent 只能跑在 macOS 上,Linux 服务器只能当 relay;iOS 需先装 Xcode 与模拟器运行时,可跑 tapflow setup ios 补齐;Docker 镜像只含 relay,且必须挂载数据卷保存登录密钥;从设备复制到剪贴板需 HTTPS 或 localhost,纯 HTTP 下只能落到设备自身剪贴板。
tapflow
jo-duchanMIT活跃维护自托管的 iOS/Android 模拟器串流服务,让团队在浏览器里直接操作真机模拟器做测试和调试,可替代 Appetize、BrowserStack 等付费方案。
Self-hosted iOS & Android simulator streaming for the whole team
npm install -g tapflow
tapflow setup
tapflow startRelay 需 Node.js ≥ 22、约 512MB 内存,任意 Linux/macOS 均可;Agent 仅支持 macOS,iOS 需 macOS 26/27 与 Xcode 26/27 及模拟器运行时,Android 需 Java 与 Android SDK 及 AVD;README 未提 GPU 或显存要求。
自托管 iOS/Android 模拟器串流,让全团队用浏览器做移动端 QA
自托管的 iOS/Android 模拟器串流服务,让团队在浏览器里直接操作真机模拟器做测试和调试,可替代 Appetize、BrowserStack 等付费方案。
核心能力
- 用 H.264 加零缓冲解码器串流,约 30fps
- 浏览器内实时转发触摸、滑动与捏合手势
- 注入 iOS 触摸事件,无需 WebDriverAgent
- 提供 REST API 与 PAT,支持 CI/CD 和 AI 代理
- 录制 QA 会话,在 relay 保留约 72 小时后自动清除
容易踩的坑
- v0.32.0最新正式版2026-10-31 · 2026-10-31 08:04 UTC
What's Changed
- fix: keep the app under test when a viewer reconnects by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1092
- feat: come back to a QA session after a dropped connection by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1093
- fix: give the interrupted-session banner's two buttons one width by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1096
- feat: let an app have a different bundle ID on each platform, and merge apps by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1097
- feat: merge apps by dragging a bundle ID onto another app in Settings by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1098
- feat: split one platform of an app into a new app (arrange mode) by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1099
- feat: refresh the device list without an agent restart, and let teams organize it (#1085) by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1101
- feat: edit a build's label, version and build number (#1084) by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1102
- feat(dashboard): give dialogs three widths, sm, md and lg by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1103
- chore: release v0.32.0 — picking up where you left off by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1104
Full Changelog: https://github.com/jo-duchan/tapflow/compare/v0.31.0...v0.32.0
- v0.31.02026-10-29 · 2026-10-29 14:40 UTC
查看更新说明
What's Changed
- feat(protocol,ios-agent): drive the iPhone Duo simulator by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1071
- feat(dashboard): show, turn and fold the iPhone Duo by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1072
- docs: document folding and rotating the iPhone Duo by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1073
- feat(ios-agent): turn every iOS simulator the way it is held by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1074
- docs: rotation is a quarter clockwise on iOS, portrait and landscape on Android by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1075
- chore(deps): Bump the npm-minor-patch group across 1 directory with 7 updates by @dependabot[bot] in https://github.com/jo-duchan/tapflow/pull/1076
- feat(android-agent): turn an Android emulator's body the way it is held by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1077
- feat(dashboard): draw an Android emulator by its body, and spin it on Rotate by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1078
- docs: an Android emulator turns its body a quarter clockwise by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1079
- docs(contributing): record where an iOS simulator's orientation can be read and written by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1081
- fix(audio): play device audio without ticking by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1082
- perf(dashboard): mirror an iOS frame only when a new one arrives by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1083
- perf(ios-agent): halve the capture helper's CPU while the screen moves by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1088
- perf(ios-agent): copy each captured frame on the GPU by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1089
- fix(dashboard): record an iOS device at the stream's resolution by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1090
- chore: release v0.31.0 — devices turn the way they are held by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1091
Full Changelog: https://github.com/jo-duchan/tapflow/compare/v0.30.0...v0.31.0
- v0.30.02026-10-27 · 2026-10-27 11:19 UTC
查看更新说明
What's Changed
- feat: pass launch arguments and env to the app on launch (#1047) by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1063
- feat: per-app launch defaults in tapflow.config.json (#1047) by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1064
- docs: sync context docs with the code after the launch and lease work by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1066
- fix(deps): take the patched postcss-selector-parser by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1067
- fix(agents): take the periodic child-process reads off the event loop by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1068
- fix(playground): load launch defaults in the dev relay by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1069
- chore: release v0.30.0 — Apps launch with the arguments and environment a test needs by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1070
Full Changelog: https://github.com/jo-duchan/tapflow/compare/v0.29.0...v0.30.0
- v0.29.02026-10-25 · 2026-10-25 16:30 UTC
查看更新说明
What's Changed
- docs: reframe VISION around tapflow Core and three ways to use it by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1034
- fix(ios-agent): read a placeholder field's identifier and a selected element's label by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1035
- feat(playground): sample apps and a script to measure the iOS UI tree by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1036
- fix(ios-agent): give a client's tap a minimum contact time by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1037
- feat: carry a text field's placeholder in the UI tree and match it last by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1038
- feat(playground): an Android Compose sample for measuring the UI tree by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1039
- fix(android-agent): keep a node with an id, and round its far edge down by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1040
- fix(android-agent): send emulator touch calls one at a time by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1041
- feat: run only the flows that apply to the build and the tags by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1042
- fix(relay): let a person join a device the relay has up on a Mac short of memory by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1043
- chore(deps): bump the npm-minor-patch group with 5 updates by @dependabot[bot] in https://github.com/jo-duchan/tapflow/pull/1032
- docs(protocol): say what a clientKind claim buys now that the resource gate reads it by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1044
- docs(contributing): record what a simulator boot does to other sessions on the same Mac by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1045
- feat: report on device:ready whether the boot brought the device up by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1046
- feat: boot one device at a time per Mac, with a person's boot first by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1048
- feat(dashboard): show the per-Mac boot gate to the person waiting on it by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1049
- feat: let a run choose the services an iOS simulator boots with by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1050
- feat: keep a run's service tier on record, and finish the tier follow-ups by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1051
- feat: refuse new devices on a Mac under critical memory pressure by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1052
- fix(deps): update dompurify to 3.4.17 by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1053
- fix(ios-agent): keep com.apple.financed on in Lean mode by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1054
- feat: cap the devices automation keeps running on each Mac by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1055
- feat: reset the device before a flow run by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1056
- feat(relay): lease a device by platform, chosen and booted in one step by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1057
- feat(relay): record every device of a run, and which flow each one is on by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1058
- feat(cli): run flows across leased devices with flow run --parallel by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1059
- feat(mcp-server): tell the agent how to use tapflow and how to test with it by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1060
- chore: release v0.29.0 — Coding agents know how to test, and their taps land by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1061
- fix(deps): take the patched body-parser, fast-uri and undici by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1062
Full Changelog: https://github.com/jo-duchan/tapflow/compare/v0.28.1...v0.29.0
- v0.28.12026-10-21 · 2026-10-21 14:50 UTC
查看更新说明
What's Changed
- docs: docs-production is live since v0.28.0 — drop the transition caveats by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1005
- feat: a 1200x630 link-preview banner for tapflow.dev and the dashboard by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1006
- fix(dashboard): close the mobile sidebar sheet when the page changes by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1008
- docs(dashboard): SidebarMenuButton component spec by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1007
- docs(dashboard): DropdownMenuItem component spec by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1009
- docs(dashboard): Kbd component spec by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1010
- docs(dashboard): SearchInput component spec by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1011
- fix(relay): the build version search matches % and _ literally by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1012
- docs(dashboard): TableRow component spec by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1013
- fix(dashboard): skeleton rows take the table's row height, so Flow Runs does not jump by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1014
- fix(dashboard): the comment box recovers from a failed post and keeps focus by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1015
- docs(dashboard): Textarea component spec by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1016
- fix(dashboard): leaving a QA session sends device:shutdown before the socket closes by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1017
- docs(dashboard): Breadcrumb component spec by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1018
- docs(dashboard): Switch component spec by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1019
- fix(dashboard): mount toasts through shadcn's current Toaster by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1020
- fix(dashboard): toasts are set in Inter; comments no longer claim a toast role by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1022
- docs(dashboard): Toast (sonner) component spec by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1021
- docs(dashboard): Field component spec by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1023
- docs(dashboard): UserAvatar component spec by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1024
- fix(relay): a nickname or team name of only spaces is no longer saved by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1025
- fix(dashboard): the collapsed sidebar's account button is named after the person by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1026
- docs(dashboard): Badge spec — platform labels are iOS/Android/Both since #992 by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1027
- chore: release v0.28.1 — dashboard fixes, and a Node.js security update to take by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1028
- fix(release): the changelog sweep's awk survives /release with an argument by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1029
- chore(deps): follow-redirects 1.16.1 by @jo-duchan in https://github.com/jo-duchan/tapflow/pull/1031
Full Changelog: https://github.com/jo-duchan/tapflow/compare/v0.28.0...v0.28.1
用户点评
登录用户的真实使用记录,可按版本和硬件筛选还没有人点评。第一个说出真实体验的人最有价值。
https://github.com/user-attachments/assets/dbba8bde-74b6-4fb9-bdb6-3919bc4295c4
v0.x, actively developed — backward-compatible by default; breaking changes are rare and always noted in the changelog. Roadmap →
Why tapflow?
Mobile QA usually depends on access to simulators, emulators, or physical devices — and that access is uneven across a team.
For mobile developers it means opening Xcode or Android Studio on a Mac. For everyone else, it often means asking a mobile developer every single time:
Backend developer — "How do I install the sandbox build to check what was deployed?"
Product manager — "I keep installing and removing versions just to compare behavior."
Designer — "I need to check the layout across screen sizes, but I don't have the right devices."
Physical devices add their own overhead — OS-version coverage, availability, charging, storage, handoff. Cloud simulator services solve access, but they require uploading internal builds to a third-party service and paying for remote devices while your own Macs can already run the same simulators.
We hit this exact problem, so we built tapflow.
| Solution | The catch |
|---|---|
| Appetize / BrowserStack | Recurring cost — and app builds are uploaded to a third-party cloud |
| Physical devices | Cost, availability, OS coverage, management overhead |
| Xcode / Android Studio | Each teammate needs a Mac and a full mobile toolchain |
| tapflow | Reuse your own Macs — data stays on infrastructure you control, and the whole team does QA from a browser |
How it works
tapflow connects three parts:
- A self-hosted relay (Linux or Mac) — also serves the dashboard on the same port, so there's no separate web server.
- A macOS agent that drives the iOS Simulator and Android emulator — it connects outbound to the relay (no inbound firewall rules) and injects iOS touch directly, without WebDriverAgent.
- A browser dashboard for the rest of the team — pick an available device and interact with it in real time while the simulators keep running on your Macs.
Browser (your team) ←─ WebSocket ─→ Relay Server ←─ WebSocket (outbound) ─→ Mac Agent
(Linux / Mac) (iOS · Android)
What tapflow is not
tapflow doesn't replace native mobile development tools. Mobile developers still use Xcode, Android Studio, and their build tooling. tapflow makes the running simulators and emulators accessible to the rest of the team through a browser, and it is not a device farm. It does include automation — a deterministic flow runner, and an MCP server for LLM agents — but wiring in external automation frameworks like WebDriverAgent or Appium is out of scope; tapflow ships its own minimal runner instead.
Where this is heading is laid out in VISION.md: three ways to use one shared layer, and work that moves from something done once to something every build runs.
Quick Start
1. Install
npm install -g tapflow
# or: yarn global add tapflow | pnpm add -g tapflow
2. Set up the environment
On the Mac that will run an agent, install the simulator/emulator prerequisites in one step:
tapflow setup
Skip this on a relay-only server (Linux). See Environment Setup for details.
3. Start relay + agent
tapflow start
# ✓ Relay started on http://localhost:4000
# ✓ iOS Agent connected (3 simulators available)
This starts both the relay and the agent on the same Mac (local mode).
4. Create the first admin account
Open http://localhost:4000 in your browser. tapflow redirects you to /setup to create the admin account.
Headless server? Use
tapflow admin initto create the admin account via CLI instead.
5. Open the dashboard
Navigate to http://localhost:4000 and sign in with the account you just created.
Having issues? Run
tapflow doctorto re-check prerequisites at any time.
Requirements
| Component | Requirements |
|---|---|
| Relay server | Node.js ≥ 22, any OS (Linux/macOS), ~512 MB RAM |
| iOS Agent | macOS 26 or 27, Xcode 26 or 27 + iOS Simulator runtime (or run tapflow setup ios), Node.js ≥ 22 |
| Android Agent | macOS, Java + Android SDK with an AVD (or run tapflow setup android), Node.js ≥ 22 |
| Browser (QA) | Any modern browser — Chrome, Firefox, Safari, Edge |
Agents run on macOS only (they drive the iOS Simulator and Android emulator on a Mac). The relay runs anywhere.
Features
- No mobile toolchain for QA users — teammates test from a browser without installing Xcode, Android Studio, or local simulator tooling.
- Self-hosted by default — no third-party cloud and no external accounts; everything runs on hardware you already own.
- Use your existing Mac setup — run agents on Macs that already have the iOS Simulator or Android emulator available.
- API-first — REST endpoints and Personal Access Tokens support CI/CD and AI-agent workflows.
What's included:
- Browser streaming — iOS & Android at ~30 fps, no extra app on the device. Both stream H.264 through a 2-tier decoder (WebCodecs on secure contexts, WASM/tinyh264 on plain HTTP), which removes the media-element buffer from the decode path. Resolution adapts to the connection — native on a secure context, downscaled on plain-HTTP LAN.1
- Codec fallback — the stream negotiates the codec per client and falls back to JPEG when a hardware or WASM decoder isn't available, so older browsers still work.
- Touch, swipe & pinch — real-time input forwarded to the simulator or emulator.
- Deeplink toolbar — open supported deeplinks directly from the QA toolbar.
- Keyboard shortcuts — trigger simulator toolbar actions from the keyboard.
- Clipboard sync — copy and paste between your Mac and the device, in both directions. Copying from the device needs the dashboard on HTTPS or localhost; over plain HTTP the text lands on the device's own clipboard and the dashboard says so.
- Device audio — the app's sound comes back to the browser tab.
- Network control — take a device off the network and put it back, to test how the app behaves offline. iOS installs a network filter once; Android needs nothing.
- App Center — upload
.app.zip/.apkand track builds by status (Backlog / In Progress / Done / Rejected). - Session recordings — record and share QA sessions, kept on the relay for ~72 hours, then purged automatically.
- Screenshot REST endpoint —
GET /api/v1/sessions/:sessionId/screenshotfor CI and AI agents. - Mac resource monitoring — CPU & RAM per agent, to spot overloaded hosts before assigning sessions.
- Team management — invite links, roles (Admin / Developer / QA / Viewer), and Personal Access Tokens.
- Flow runner —
tapflow flow runreplays a YAML flow with condition-based waits and a JUnit report, and calls no LLM at replay time. Experimental. - MCP Server —
@tapflowio/mcp-serverlets Claude Code and other LLM agents control simulators as native tools. Experimental.
1 On a real LAN, decode-to-present measures in the low tens of milliseconds (p50 ~11–17 ms with the WASM software decoder; faster with WebCodecs on HTTPS); end-to-end "glass-to-glass" latency adds your network's round trip on top. See the streaming latency log for the full measurements, conditions, and known limitations.
Security & Privacy
tapflow is self-hosted by design — build files, device streams, and session recordings stay on infrastructure you control, never sent to a third-party service.
| Data | Where it stays |
|---|---|
App binaries (.app.zip / .apk) | Relay storage |
| Device streams (video · touch) | The relay ↔ browser path you host |
| Session recordings | Relay storage; expire after 72h, then purged |
| Account & team data | The relay's SQLite DB |
| Third-party simulator cloud | Not required |
- LAN-first — the agent ↔ relay leg is internal traffic; the device stream never transits a third party.
- The relay is the trust boundary — it holds accounts, builds and live device control, so whoever can reach it is who you are exposing. The agent only dials out.
- Authenticated by default off-host — the relay accepts unauthenticated connections only from its own machine (
localhost). Browsers reaching it from elsewhere sign in; agents on another machine present anagent-scope token. Traffic through a tunnel counts as off-host, even though the tunnel client runs on the relay's machine. - PAT + roles — Personal Access Tokens carry scopes (
builds:writefor CI uploads,agentfor remote agents), and team roles (Admin / Developer / QA / Viewer) govern dashboard access.
Found a vulnerability? See SECURITY.md. For the full model, read Security & Privacy.
Self-Hosting
A single Mac needs nothing beyond tapflow start (see Quick Start). For a team, run the relay on a separate Linux server or dedicated Mac, and point each Mac agent at it.
Relay server:
# Recommended: PM2 for automatic restarts
npm install -g pm2 tapflow
JWT_SECRET=$(openssl rand -hex 32) pm2 start tapflow --name relay -- relay start
pm2 save && pm2 startup
Or run the relay with Docker:
docker run -d -p 4000:4000 -v "$PWD/data:/app/.tapflow/data" tapflow/tapflow:latest
The image is the relay only — agents stay on your Macs. The volume is required (it holds the sign-in secret), and invite links need
TAPFLOW_RELAY_URL. See Docker Compose for the full setup.
Each Mac agent:
tapflow agent start --relay wss://your-relay-url --token <agent-token>
A relay on a different machine accepts an agent only with an
agent-scope token — create one in Settings → Tokens (Admin only). Agents on the relay's own machine (tapflow start) need no token. See Remote relay authentication.For nginx / Caddy reverse proxy setup and external access, see External access.
CLI Reference
| Command | Description |
|---|---|
tapflow start | Start relay + agent together (local mode) |
tapflow relay start | Start relay only |
tapflow agent start --relay <url> [--token <pat>] | Start agent and connect to a relay (remote relays need an agent-scope token) |
tapflow init | Configure this machine's tapflow (~/.tapflow by default) |
tapflow admin init | Create the first admin account (CLI fallback) |
tapflow doctor [platform] | Diagnose prerequisites (Node, iOS, Android) |
tapflow setup [platform] | Install & configure the local environment |
tapflow devices | List available simulators and emulators |
tapflow boot <name|udid> | Boot a simulator or emulator |
tapflow status | Show connected agents, devices, active sessions |
tapflow reset | Shut down all simulators and emulators |
tapflow logs | Show recent relay log entries (run on the relay host) |
Full reference → CLI docs
Documentation
Full docs: www.tapflow.dev
- Guides — Quick Start · Environment Setup · Self-Hosting · Uploading Builds (CI/CD) · Troubleshooting
- Reference — CLI · Configuration · REST API · Security & Privacy
- AI Agent — MCP Server
Contributing
tapflow is actively developed and PRs are welcome — see CONTRIBUTING.md for branch strategy, commit conventions, and an architecture overview. For deep dives, the contributor notes cover the SimulatorKit reverse-engineering and the streaming render pipeline. Everyone taking part is expected to follow the Code of Conduct.
Requirements: Node.js ≥ 22, pnpm ≥ 9
git clone https://github.com/jo-duchan/tapflow.git
cd tapflow
pnpm install
pnpm dev
License
MIT — Copyright © 2026-present tapflow contributors
tapflow bundles scrcpy-server (Apache-2.0) for Android screen streaming. See NOTICE for full attribution.