tapflow 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +104 -68
- package/dist/commands/relay-start.d.ts.map +1 -1
- package/dist/commands/relay-start.js +11 -40
- package/dist/commands/relay-start.js.map +1 -1
- package/dist/commands/start.d.ts.map +1 -1
- package/dist/commands/start.js +16 -4
- package/dist/commands/start.js.map +1 -1
- package/dist/lib/doctor.d.ts.map +1 -1
- package/dist/lib/doctor.js +11 -3
- package/dist/lib/doctor.js.map +1 -1
- package/dist/lib/tunnel-runner.d.ts +13 -0
- package/dist/lib/tunnel-runner.d.ts.map +1 -0
- package/dist/lib/tunnel-runner.js +32 -0
- package/dist/lib/tunnel-runner.js.map +1 -0
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
<div align="center">
|
|
2
|
-
<img src="docs/public/logo-hero.svg" height="72" alt="tapflow" />
|
|
2
|
+
<img src="https://raw.githubusercontent.com/jo-duchan/tapflow/main/docs/public/logo-hero.svg" height="72" alt="tapflow" />
|
|
3
3
|
|
|
4
4
|
<h3>A self-hosted Appetize / BrowserStack alternative for mobile QA teams</h3>
|
|
5
5
|
|
|
6
6
|
<p>
|
|
7
|
-
Run iOS simulators and Android emulators in any browser — no toolchain setup, no device
|
|
8
|
-
|
|
7
|
+
Run iOS simulators and Android emulators in any browser — no toolchain setup, no device pool, no cloud uploads.<br />
|
|
8
|
+
Your builds, streams, and recordings stay on infrastructure you control.
|
|
9
9
|
</p>
|
|
10
10
|
|
|
11
11
|
<p>
|
|
12
|
-
<a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="MIT License" /></a>
|
|
12
|
+
<a href="https://github.com/jo-duchan/tapflow/blob/main/LICENSE"><img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="MIT License" /></a>
|
|
13
13
|
<a href="https://nodejs.org"><img src="https://img.shields.io/badge/node-%3E%3D20-brightgreen" alt="Node.js ≥ 20" /></a>
|
|
14
|
-
<img src="https://img.shields.io/badge/platform-macOS-lightgrey" alt="macOS Agent" />
|
|
15
|
-
<a href="https://github.com/jo-duchan/tapflow/
|
|
14
|
+
<img src="https://img.shields.io/badge/platform-macOS%20agent-lightgrey" alt="macOS Agent" />
|
|
15
|
+
<a href="https://github.com/jo-duchan/tapflow/releases"><img src="https://img.shields.io/github/v/release/jo-duchan/tapflow?include_prereleases&sort=semver" alt="Latest release" /></a>
|
|
16
|
+
<a href="https://github.com/jo-duchan/tapflow/commits/main"><img src="https://img.shields.io/github/last-commit/jo-duchan/tapflow" alt="Last commit" /></a>
|
|
17
|
+
<a href="https://github.com/jo-duchan/tapflow/blob/main/ROADMAP.md"><img src="https://img.shields.io/badge/roadmap-v0.x→v1.0-blueviolet" alt="Roadmap" /></a>
|
|
16
18
|
</p>
|
|
17
19
|
|
|
18
20
|
<p>
|
|
@@ -35,56 +37,46 @@
|
|
|
35
37
|
|
|
36
38
|
## Why tapflow?
|
|
37
39
|
|
|
38
|
-
|
|
40
|
+
Mobile QA usually depends on access to simulators, emulators, or physical devices — and that access is uneven across a team.
|
|
39
41
|
|
|
40
|
-
|
|
42
|
+
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:
|
|
41
43
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
> **Server / FE developer** — "How do I install the sandbox build to check what was deployed?"
|
|
44
|
+
> **Backend developer** — "How do I install the sandbox build to check what was deployed?"
|
|
45
45
|
>
|
|
46
|
-
> **Product manager** — "I keep
|
|
46
|
+
> **Product manager** — "I keep installing and removing versions just to compare behavior."
|
|
47
47
|
>
|
|
48
48
|
> **Designer** — "I need to check the layout across screen sizes, but I don't have the right devices."
|
|
49
49
|
|
|
50
|
-
Cloud simulator services
|
|
50
|
+
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.
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
We hit this exact problem, so we built tapflow.
|
|
53
53
|
|
|
54
|
-
| Solution
|
|
55
|
-
|
|
56
|
-
| Appetize / BrowserStack |
|
|
57
|
-
| Physical devices
|
|
58
|
-
| Xcode / Android Studio
|
|
59
|
-
| **tapflow**
|
|
54
|
+
| Solution | The catch |
|
|
55
|
+
|----------|-----------|
|
|
56
|
+
| Appetize / BrowserStack | Recurring cost — and app builds are uploaded to a third-party cloud |
|
|
57
|
+
| Physical devices | Cost, availability, OS coverage, management overhead |
|
|
58
|
+
| Xcode / Android Studio | Each teammate needs a Mac and a full mobile toolchain |
|
|
59
|
+
| **tapflow** | Reuse your own Macs — data stays on infrastructure you control, and the whole team does QA from a browser |
|
|
60
60
|
|
|
61
|
-
##
|
|
61
|
+
## What tapflow does
|
|
62
62
|
|
|
63
|
-
tapflow
|
|
63
|
+
tapflow connects three parts:
|
|
64
64
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
- **API-first** — REST endpoints and PATs built in, ready for CI/CD and AI agent workflows.
|
|
65
|
+
1. A **self-hosted relay** server (Linux or Mac)
|
|
66
|
+
2. A **macOS agent** that drives iOS simulators and Android emulators
|
|
67
|
+
3. A **browser dashboard** for the rest of the team
|
|
69
68
|
|
|
70
|
-
|
|
69
|
+
The agent connects outbound to the relay. Teammates open the dashboard, pick an available device, and interact with it remotely — while the simulators and emulators keep running on your own Macs.
|
|
71
70
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
- **Keyboard shortcuts** — simulator toolbar actions without leaving the keyboard
|
|
76
|
-
- **App Center** — upload `.app.zip` / `.apk`, track builds by status (Backlog / In Progress / Done / Rejected)
|
|
77
|
-
- **Session recordings** — record and share QA sessions, retained 72 hours
|
|
78
|
-
- **Screenshot REST endpoint** — `GET /api/v1/sessions/:sessionId/screenshot` for CI and AI agents
|
|
79
|
-
- **Mac resource monitoring** — CPU & RAM per agent, spot overloaded hosts before assigning sessions
|
|
80
|
-
- **Team management** — invite links, roles (Admin / Developer / QA / Viewer), Personal Access Tokens
|
|
81
|
-
- **MCP Server** *(experimental)* — `@tapflowio/mcp-server` lets Claude Code and other LLM agents control simulators as native tools.
|
|
71
|
+
## What tapflow is not
|
|
72
|
+
|
|
73
|
+
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 — it isn't an automation framework or a device farm.
|
|
82
74
|
|
|
83
75
|
## How it works
|
|
84
76
|
|
|
85
77
|
```
|
|
86
|
-
Browser (your team)
|
|
87
|
-
|
|
78
|
+
Browser (your team) ←─ WebSocket ─→ Relay Server ←─ WebSocket (outbound) ─→ Mac Agent
|
|
79
|
+
(Linux / Mac) (iOS · Android)
|
|
88
80
|
```
|
|
89
81
|
|
|
90
82
|
1. The **Mac Agent** connects _outbound_ to the relay — no inbound firewall rules needed.
|
|
@@ -113,7 +105,7 @@ This starts both the relay and the agent on the same Mac (local mode).
|
|
|
113
105
|
|
|
114
106
|
### 3. Create the first admin account
|
|
115
107
|
|
|
116
|
-
Open `http://localhost:4000` in your browser. tapflow redirects you to `/setup`
|
|
108
|
+
Open `http://localhost:4000` in your browser. tapflow redirects you to `/setup` to create the admin account.
|
|
117
109
|
|
|
118
110
|
> **Headless server?** Use `tapflow admin init` to create the admin account via CLI instead.
|
|
119
111
|
|
|
@@ -121,16 +113,59 @@ Open `http://localhost:4000` in your browser. tapflow redirects you to `/setup`
|
|
|
121
113
|
|
|
122
114
|
Navigate to `http://localhost:4000` and sign in with the account you just created.
|
|
123
115
|
|
|
124
|
-
> **Having issues?** Run `tapflow doctor` to auto-diagnose Node.js,
|
|
116
|
+
> **Having issues?** Run `tapflow doctor` to auto-diagnose Node.js, the iOS toolchain, `adb`, and other prerequisites.
|
|
125
117
|
|
|
126
118
|
## Requirements
|
|
127
119
|
|
|
128
|
-
| Component
|
|
129
|
-
|
|
130
|
-
| **Relay server**
|
|
131
|
-
| **iOS Agent**
|
|
132
|
-
| **Android Agent** | macOS, Android SDK (`adb` in `$PATH` or `$ANDROID_HOME` set), AVD with `google_apis/arm64-v8a` (android-34), Node.js ≥ 20 |
|
|
133
|
-
| **Browser**
|
|
120
|
+
| Component | Requirements |
|
|
121
|
+
|-----------|-------------|
|
|
122
|
+
| **Relay server** | Node.js ≥ 20, any OS (Linux/macOS), ~512 MB RAM |
|
|
123
|
+
| **iOS Agent** | macOS, Xcode with the iOS Simulator runtime, Node.js ≥ 20 |
|
|
124
|
+
| **Android Agent** | macOS, Android SDK (`adb` in `$PATH` or `$ANDROID_HOME` set), an AVD with `google_apis/arm64-v8a` (android-34), Node.js ≥ 20 |
|
|
125
|
+
| **Browser (QA)** | Any modern browser — Chrome, Firefox, Safari, Edge |
|
|
126
|
+
|
|
127
|
+
> Agents run on **macOS only** (they drive the iOS Simulator and Android emulator on a Mac). The relay runs anywhere.
|
|
128
|
+
|
|
129
|
+
## Features
|
|
130
|
+
|
|
131
|
+
- **No mobile toolchain for QA users** — teammates test from a browser without installing Xcode, Android Studio, or local simulator tooling.
|
|
132
|
+
- **Self-hosted by default** — app builds, device streams, recordings, and account data stay on infrastructure you control.
|
|
133
|
+
- **Use your existing Mac setup** — run agents on Macs that already have the iOS Simulator or Android emulator available.
|
|
134
|
+
- **API-first** — REST endpoints and Personal Access Tokens support CI/CD and AI-agent workflows.
|
|
135
|
+
|
|
136
|
+
What's included:
|
|
137
|
+
|
|
138
|
+
- **Browser streaming** — iOS & Android at ~30 fps, no extra app on the device. The iOS pipeline streams 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.<sup>[1](#latency-note)</sup>
|
|
139
|
+
- **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.
|
|
140
|
+
- **Touch, swipe & pinch** — real-time input forwarded to the simulator or emulator.
|
|
141
|
+
- **Deeplink toolbar** — open supported deeplinks directly from the QA toolbar.
|
|
142
|
+
- **Keyboard shortcuts** — trigger simulator toolbar actions from the keyboard.
|
|
143
|
+
- **App Center** — upload `.app.zip` / `.apk` and track builds by status (Backlog / In Progress / Done / Rejected).
|
|
144
|
+
- **Session recordings** — record and share QA sessions, kept on the relay for ~72 hours, then purged automatically.
|
|
145
|
+
- **Screenshot REST endpoint** — `GET /api/v1/sessions/:sessionId/screenshot` for CI and AI agents.
|
|
146
|
+
- **Mac resource monitoring** — CPU & RAM per agent, to spot overloaded hosts before assigning sessions.
|
|
147
|
+
- **Team management** — invite links, roles (Admin / Developer / QA / Viewer), and Personal Access Tokens.
|
|
148
|
+
- **MCP Server** *(experimental)* — `@tapflowio/mcp-server` lets Claude Code and other LLM agents control simulators as native tools.
|
|
149
|
+
|
|
150
|
+
<a name="latency-note"></a>
|
|
151
|
+
> <sup>1</sup> On localhost, decode-to-present is in the single-to-low-double-digit milliseconds (WebCodecs ~2.5 ms, WASM ~9–14 ms); end-to-end "glass-to-glass" latency also depends on your network. See the [streaming latency log](https://github.com/jo-duchan/tapflow/blob/main/contributing/streaming-latency-log.md) for the full pipeline analysis and measurements.
|
|
152
|
+
|
|
153
|
+
## Security & Privacy
|
|
154
|
+
|
|
155
|
+
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.
|
|
156
|
+
|
|
157
|
+
| Data | Where it stays |
|
|
158
|
+
|------|----------------|
|
|
159
|
+
| App binaries (`.app.zip` / `.apk`) | Relay storage |
|
|
160
|
+
| Device streams (video · touch) | The relay ↔ browser path you host |
|
|
161
|
+
| Session recordings | Relay storage; expire after 72h, then purged |
|
|
162
|
+
| Account & team data | The relay's SQLite DB |
|
|
163
|
+
| Third-party simulator cloud | Not required |
|
|
164
|
+
|
|
165
|
+
- **LAN-first** — the agent ↔ relay leg is internal traffic; the device stream never transits a third party.
|
|
166
|
+
- **PAT + roles** — Personal Access Tokens carry scopes (e.g. `builds:write` for CI uploads), and team roles (Admin / Developer / QA / Viewer) govern dashboard access.
|
|
167
|
+
|
|
168
|
+
Found a vulnerability? See [SECURITY.md](https://github.com/jo-duchan/tapflow/blob/main/SECURITY.md). For the full model, read [Security & Privacy](https://www.tapflow.dev/guide/security).
|
|
134
169
|
|
|
135
170
|
## Self-Hosting
|
|
136
171
|
|
|
@@ -165,19 +200,19 @@ tapflow agent start --relay wss://your-relay-url
|
|
|
165
200
|
|
|
166
201
|
## CLI Reference
|
|
167
202
|
|
|
168
|
-
| Command
|
|
169
|
-
|
|
170
|
-
| `tapflow start`
|
|
171
|
-
| `tapflow relay start`
|
|
172
|
-
| `tapflow agent start --relay <url>` | Start agent and connect to a relay
|
|
173
|
-
| `tapflow init`
|
|
174
|
-
| `tapflow admin init`
|
|
175
|
-
| `tapflow doctor`
|
|
176
|
-
| `tapflow devices`
|
|
177
|
-
| `tapflow boot <name\|udid>`
|
|
178
|
-
| `tapflow status`
|
|
179
|
-
| `tapflow reset`
|
|
180
|
-
| `tapflow logs`
|
|
203
|
+
| Command | Description |
|
|
204
|
+
|---------|-------------|
|
|
205
|
+
| `tapflow start` | Start relay + agent together (local mode) |
|
|
206
|
+
| `tapflow relay start` | Start relay only |
|
|
207
|
+
| `tapflow agent start --relay <url>` | Start agent and connect to a relay |
|
|
208
|
+
| `tapflow init` | Scaffold `tapflow.config.json` |
|
|
209
|
+
| `tapflow admin init` | Create the first admin account (CLI fallback) |
|
|
210
|
+
| `tapflow doctor` | Diagnose environment (Node, iOS toolchain, adb…) |
|
|
211
|
+
| `tapflow devices` | List available simulators and emulators |
|
|
212
|
+
| `tapflow boot <name\|udid>` | Boot a simulator or emulator |
|
|
213
|
+
| `tapflow status` | Show connected agents, devices, active sessions |
|
|
214
|
+
| `tapflow reset` | Shut down all simulators and emulators |
|
|
215
|
+
| `tapflow logs` | Show recent relay log entries |
|
|
181
216
|
|
|
182
217
|
Full reference → [CLI docs](https://www.tapflow.dev/reference/cli)
|
|
183
218
|
|
|
@@ -192,6 +227,7 @@ Full reference → [CLI docs](https://www.tapflow.dev/reference/cli)
|
|
|
192
227
|
|
|
193
228
|
**Setup**
|
|
194
229
|
- [Self-Hosting the Relay](https://www.tapflow.dev/guide/self-hosting)
|
|
230
|
+
- [Security & Privacy](https://www.tapflow.dev/guide/security)
|
|
195
231
|
- [Agent Setup](https://www.tapflow.dev/guide/agent)
|
|
196
232
|
- [Uploading Builds (CI/CD)](https://www.tapflow.dev/guide/upload-builds)
|
|
197
233
|
- [Scaling Mac Resources](https://www.tapflow.dev/guide/scaling)
|
|
@@ -200,17 +236,19 @@ Full reference → [CLI docs](https://www.tapflow.dev/reference/cli)
|
|
|
200
236
|
- [First-time Setup](https://www.tapflow.dev/dashboard/setup)
|
|
201
237
|
- [Dashboard Overview](https://www.tapflow.dev/dashboard/overview)
|
|
202
238
|
|
|
239
|
+
**AI Agent**
|
|
240
|
+
- [MCP Server](https://www.tapflow.dev/guide/mcp-server) *(experimental)*
|
|
241
|
+
|
|
203
242
|
**Reference**
|
|
204
243
|
- [CLI Reference](https://www.tapflow.dev/reference/cli)
|
|
205
244
|
- [Configuration](https://www.tapflow.dev/reference/configuration)
|
|
206
245
|
- [REST API](https://www.tapflow.dev/reference/api)
|
|
207
246
|
|
|
208
|
-
**AI Agent**
|
|
209
|
-
- [MCP Server](https://www.tapflow.dev/guide/mcp-server) *(experimental)*
|
|
210
|
-
|
|
211
247
|
**[Troubleshooting](https://www.tapflow.dev/guide/troubleshooting)**
|
|
212
248
|
|
|
213
|
-
##
|
|
249
|
+
## Contributing
|
|
250
|
+
|
|
251
|
+
tapflow is actively developed and PRs are welcome — see [CONTRIBUTING.md](https://github.com/jo-duchan/tapflow/blob/main/CONTRIBUTING.md) for branch strategy, commit conventions, and an architecture overview.
|
|
214
252
|
|
|
215
253
|
**Requirements**: Node.js ≥ 20, pnpm ≥ 9
|
|
216
254
|
|
|
@@ -221,10 +259,8 @@ pnpm install
|
|
|
221
259
|
pnpm dev
|
|
222
260
|
```
|
|
223
261
|
|
|
224
|
-
See [CONTRIBUTING.md](CONTRIBUTING.md) for branch strategy, commit conventions, and architecture overview.
|
|
225
|
-
|
|
226
262
|
## License
|
|
227
263
|
|
|
228
|
-
[MIT](LICENSE) — Copyright ©
|
|
264
|
+
[MIT](https://github.com/jo-duchan/tapflow/blob/main/LICENSE) — Copyright © 2026-present tapflow contributors
|
|
229
265
|
|
|
230
|
-
> tapflow bundles [scrcpy-server](https://github.com/Genymobile/scrcpy) (Apache-2.0) for Android screen streaming. See [NOTICE](NOTICE) for full attribution.
|
|
266
|
+
> tapflow bundles [scrcpy-server](https://github.com/Genymobile/scrcpy) (Apache-2.0) for Android screen streaming. See [NOTICE](https://github.com/jo-duchan/tapflow/blob/main/NOTICE) for full attribution.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"relay-start.d.ts","sourceRoot":"","sources":["../../src/commands/relay-start.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"relay-start.d.ts","sourceRoot":"","sources":["../../src/commands/relay-start.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,iBAAiB;IAChC,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAMD,wBAAsB,aAAa,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CA+C1E"}
|
|
@@ -3,8 +3,7 @@ import path from 'path';
|
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
import { RelayServer, initDb, config } from '@tapflowio/relay';
|
|
5
5
|
import { banner, step, warn } from '../lib/print.js';
|
|
6
|
-
import {
|
|
7
|
-
import { TailscaleTunnel } from '../lib/tailscale-tunnel.js';
|
|
6
|
+
import { startConfiguredTunnel } from '../lib/tunnel-runner.js';
|
|
8
7
|
const DEFAULT_PORT = config.local.port;
|
|
9
8
|
const portSchema = z.number().int().min(1).max(65535, 'port must be between 1 and 65535');
|
|
10
9
|
export async function cmdRelayStart(opts) {
|
|
@@ -29,50 +28,22 @@ export async function cmdRelayStart(opts) {
|
|
|
29
28
|
}
|
|
30
29
|
const tunnelCfg = config.tunnel;
|
|
31
30
|
let tunnel = null;
|
|
31
|
+
let publicUrl = null;
|
|
32
32
|
if (tunnelCfg != null || opts.tunnel) {
|
|
33
33
|
if (!tunnelCfg) {
|
|
34
34
|
banner('error', 'TUNNEL CONFIG ERROR', ['tunnel section is required in tapflow.config.json when using --tunnel']);
|
|
35
35
|
process.exit(1);
|
|
36
36
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
else {
|
|
41
|
-
const token = process.env.TAPFLOW_TUNNEL_TOKEN ?? '';
|
|
42
|
-
if (!token) {
|
|
43
|
-
banner('error', 'TUNNEL CONFIG ERROR', ['TAPFLOW_TUNNEL_TOKEN env var is required for rathole tunnel']);
|
|
44
|
-
process.exit(1);
|
|
45
|
-
}
|
|
46
|
-
tunnel = new RatholeTunnel({ serverAddr: tunnelCfg.serverAddr, publicUrl: tunnelCfg.publicUrl, token, ssh: tunnelCfg.ssh ?? undefined });
|
|
47
|
-
}
|
|
48
|
-
try {
|
|
49
|
-
await tunnel.setupServer();
|
|
50
|
-
const { publicUrl } = await tunnel.start(port);
|
|
51
|
-
step(`Tunnel ready — Public URL: ${publicUrl}`);
|
|
52
|
-
banner('success', 'TAPFLOW RELAY READY', [
|
|
53
|
-
`Relay : http://localhost:${port}`,
|
|
54
|
-
`Public : ${publicUrl}`,
|
|
55
|
-
`Connect Mac agents: tapflow start --relay ws://<host>:${port}`,
|
|
56
|
-
'Press Ctrl+C to stop.',
|
|
57
|
-
]);
|
|
58
|
-
}
|
|
59
|
-
catch (err) {
|
|
60
|
-
console.warn(`Tunnel failed to start: ${err instanceof Error ? err.message : String(err)}`);
|
|
61
|
-
tunnel = null;
|
|
62
|
-
banner('success', 'TAPFLOW RELAY READY', [
|
|
63
|
-
`Relay : http://localhost:${port}`,
|
|
64
|
-
`Connect Mac agents: tapflow start --relay ws://<host>:${port}`,
|
|
65
|
-
'Press Ctrl+C to stop.',
|
|
66
|
-
]);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
else {
|
|
70
|
-
banner('success', 'TAPFLOW RELAY READY', [
|
|
71
|
-
`Relay : http://localhost:${port}`,
|
|
72
|
-
`Connect Mac agents: tapflow start --relay ws://<host>:${port}`,
|
|
73
|
-
'Press Ctrl+C to stop.',
|
|
74
|
-
]);
|
|
37
|
+
const started = await startConfiguredTunnel(tunnelCfg, port);
|
|
38
|
+
tunnel = started.tunnel;
|
|
39
|
+
publicUrl = started.publicUrl;
|
|
75
40
|
}
|
|
41
|
+
banner('success', 'TAPFLOW RELAY READY', [
|
|
42
|
+
`Relay : http://localhost:${port}`,
|
|
43
|
+
...(publicUrl ? [`Public : ${publicUrl}`] : []),
|
|
44
|
+
`Connect Mac agents: tapflow agent start --relay ws://<host>:${port}`,
|
|
45
|
+
'Press Ctrl+C to stop.',
|
|
46
|
+
]);
|
|
76
47
|
process.on('SIGINT', () => {
|
|
77
48
|
void tunnel?.stop();
|
|
78
49
|
process.exit(0);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"relay-start.js","sourceRoot":"","sources":["../../src/commands/relay-start.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAA;AACnB,OAAO,IAAI,MAAM,MAAM,CAAA;AACvB,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAC9D,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAA;AACpD,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"relay-start.js","sourceRoot":"","sources":["../../src/commands/relay-start.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../../src/commands/start.ts"],"names":[],"mappings":"AAIA,OAAO,sBAAsB,CAAA;AAC7B,OAAO,0BAA0B,CAAA;
|
|
1
|
+
{"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../../src/commands/start.ts"],"names":[],"mappings":"AAIA,OAAO,sBAAsB,CAAA;AAC7B,OAAO,0BAA0B,CAAA;AAKjC,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAID,wBAAsB,QAAQ,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAkFhE"}
|
package/dist/commands/start.js
CHANGED
|
@@ -5,6 +5,7 @@ import path from 'path';
|
|
|
5
5
|
import '@tapflowio/ios-agent';
|
|
6
6
|
import '@tapflowio/android-agent';
|
|
7
7
|
import { banner, createSpinner, step, warn } from '../lib/print.js';
|
|
8
|
+
import { startConfiguredTunnel } from '../lib/tunnel-runner.js';
|
|
8
9
|
const RELAY_PORT = config.local.port;
|
|
9
10
|
export async function cmdStart(opts) {
|
|
10
11
|
const relayUrl = `ws://localhost:${RELAY_PORT}`;
|
|
@@ -31,19 +32,28 @@ export async function cmdStart(opts) {
|
|
|
31
32
|
const server = new RelayServer({ port: RELAY_PORT, uploadsDir: path.join(config.local.dataDir, 'uploads'), wsBackpressureBytes: config.local.wsBackpressureBytes });
|
|
32
33
|
await server.start();
|
|
33
34
|
step(`Relay started on ws://localhost:${RELAY_PORT}`);
|
|
34
|
-
// ── 2.
|
|
35
|
+
// ── 2. Tunnel (optional — publishes a public URL for teammates) ────────────
|
|
36
|
+
let tunnel = null;
|
|
37
|
+
let publicUrl = null;
|
|
38
|
+
if (config.tunnel) {
|
|
39
|
+
const started = await startConfiguredTunnel(config.tunnel, RELAY_PORT);
|
|
40
|
+
tunnel = started.tunnel;
|
|
41
|
+
publicUrl = started.publicUrl;
|
|
42
|
+
}
|
|
43
|
+
// ── 3. Agent availability check ───────────────────────────────────────────
|
|
35
44
|
if (platformsToRun.length === 0) {
|
|
36
45
|
banner('success', 'TAPFLOW RELAY READY', [
|
|
37
46
|
`Relay : http://localhost:${RELAY_PORT}`,
|
|
47
|
+
...(publicUrl ? [`Public : ${publicUrl}`] : []),
|
|
38
48
|
'No agent environment detected — running relay only.',
|
|
39
49
|
`Connect a Mac agent: tapflow agent start --relay ws://<this-ip>:${RELAY_PORT}`,
|
|
40
50
|
'Press Ctrl+C to stop.',
|
|
41
51
|
]);
|
|
42
|
-
process.on('SIGINT', () => process.exit(0));
|
|
52
|
+
process.on('SIGINT', () => { void tunnel?.stop(); process.exit(0); });
|
|
43
53
|
return;
|
|
44
54
|
}
|
|
45
55
|
const agents = [];
|
|
46
|
-
// ──
|
|
56
|
+
// ── 4. Connect each registered platform ──────────────────────────────────
|
|
47
57
|
for (const platform of platformsToRun) {
|
|
48
58
|
const spinner = createSpinner(`Connecting ${platform} agent…`);
|
|
49
59
|
spinner.start();
|
|
@@ -65,11 +75,13 @@ export async function cmdStart(opts) {
|
|
|
65
75
|
}
|
|
66
76
|
banner('success', 'TAPFLOW READY', [
|
|
67
77
|
`Relay : http://localhost:${RELAY_PORT}`,
|
|
68
|
-
`
|
|
78
|
+
...(publicUrl ? [`Public : ${publicUrl}`] : []),
|
|
79
|
+
`Open ${publicUrl ?? `http://localhost:${RELAY_PORT}`} in your browser.`,
|
|
69
80
|
'Press Ctrl+C to stop.',
|
|
70
81
|
]);
|
|
71
82
|
process.on('SIGINT', () => {
|
|
72
83
|
agents.forEach((a) => a.disconnect());
|
|
84
|
+
void tunnel?.stop();
|
|
73
85
|
process.exit(0);
|
|
74
86
|
});
|
|
75
87
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"start.js","sourceRoot":"","sources":["../../src/commands/start.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAA;AACrD,OAAO,EAAE,MAAM,IAAI,CAAA;AACnB,OAAO,IAAI,MAAM,MAAM,CAAA;AACvB,OAAO,sBAAsB,CAAA;AAC7B,OAAO,0BAA0B,CAAA;AACjC,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"start.js","sourceRoot":"","sources":["../../src/commands/start.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/doctor.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/lib/doctor.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/lib/doctor.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAA;IACb,EAAE,EAAE,OAAO,CAAA;IACX,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,WAAW,EAAE,CAAA;IACrB,GAAG,EAAE,WAAW,EAAE,GAAG,IAAI,CAAA;IACzB,OAAO,EAAE,WAAW,EAAE,GAAG,IAAI,CAAA;CAC9B;AAED,wBAAsB,eAAe,IAAI,OAAO,CAAC,YAAY,CAAC,CAS7D"}
|
package/dist/lib/doctor.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { execSync } from 'node:child_process';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
2
3
|
export async function runDoctorChecks() {
|
|
3
4
|
const isMac = process.platform === 'darwin';
|
|
4
5
|
const adbPath = resolveAdb();
|
|
@@ -15,6 +16,13 @@ function checkXcode() {
|
|
|
15
16
|
return { label: `Xcode ${version}`, ok: true };
|
|
16
17
|
}
|
|
17
18
|
catch {
|
|
19
|
+
if (existsSync('/Applications/Xcode.app')) {
|
|
20
|
+
return {
|
|
21
|
+
label: 'Xcode',
|
|
22
|
+
ok: false,
|
|
23
|
+
detail: 'Xcode is installed but xcode-select is not configured. Run: sudo xcode-select -s /Applications/Xcode.app/Contents/Developer',
|
|
24
|
+
};
|
|
25
|
+
}
|
|
18
26
|
return {
|
|
19
27
|
label: 'Xcode',
|
|
20
28
|
ok: false,
|
|
@@ -46,9 +54,9 @@ function checkBootedSimulator() {
|
|
|
46
54
|
}
|
|
47
55
|
const available = allDevices.find((d) => d.state === 'Shutdown');
|
|
48
56
|
const hint = available
|
|
49
|
-
? `Run: tapflow boot "${available.name}"`
|
|
50
|
-
: 'Run: tapflow devices
|
|
51
|
-
return { label: 'Simulator', ok: false, detail: hint };
|
|
57
|
+
? `No simulator is running. Run: tapflow boot "${available.name}"`
|
|
58
|
+
: 'No simulator is running. Run: tapflow devices to see available simulators, then: tapflow boot "<name>"';
|
|
59
|
+
return { label: 'Simulator', ok: false, warn: true, detail: hint };
|
|
52
60
|
}
|
|
53
61
|
catch {
|
|
54
62
|
return { label: 'Simulator', ok: false, detail: 'Could not query simulators. Is Xcode installed?' };
|
package/dist/lib/doctor.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/lib/doctor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;
|
|
1
|
+
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/lib/doctor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { TapflowConfig } from '@tapflowio/relay';
|
|
2
|
+
import type { TunnelPlugin } from './tunnel.js';
|
|
3
|
+
export type TunnelConfig = NonNullable<TapflowConfig['tunnel']>;
|
|
4
|
+
export interface StartedTunnel {
|
|
5
|
+
tunnel: TunnelPlugin | null;
|
|
6
|
+
publicUrl: string | null;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Build the configured tunnel, start it, and return its public URL.
|
|
10
|
+
* On startup failure the relay keeps running — returns nulls so callers fall back to local-only.
|
|
11
|
+
*/
|
|
12
|
+
export declare function startConfiguredTunnel(tunnelCfg: TunnelConfig, port: number): Promise<StartedTunnel>;
|
|
13
|
+
//# sourceMappingURL=tunnel-runner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tunnel-runner.d.ts","sourceRoot":"","sources":["../../src/lib/tunnel-runner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAIrD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAE/C,MAAM,MAAM,YAAY,GAAG,WAAW,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAA;AAE/D,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,YAAY,GAAG,IAAI,CAAA;IAC3B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;CACzB;AAED;;;GAGG;AACH,wBAAsB,qBAAqB,CAAC,SAAS,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAsBzG"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { RatholeTunnel } from './rathole-tunnel.js';
|
|
2
|
+
import { TailscaleTunnel } from './tailscale-tunnel.js';
|
|
3
|
+
import { step, warn } from './print.js';
|
|
4
|
+
/**
|
|
5
|
+
* Build the configured tunnel, start it, and return its public URL.
|
|
6
|
+
* On startup failure the relay keeps running — returns nulls so callers fall back to local-only.
|
|
7
|
+
*/
|
|
8
|
+
export async function startConfiguredTunnel(tunnelCfg, port) {
|
|
9
|
+
let tunnel;
|
|
10
|
+
if (tunnelCfg.provider === 'tailscale') {
|
|
11
|
+
tunnel = new TailscaleTunnel({ publicUrl: tunnelCfg.publicUrl });
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
const token = process.env.TAPFLOW_TUNNEL_TOKEN ?? '';
|
|
15
|
+
if (!token) {
|
|
16
|
+
warn('TAPFLOW_TUNNEL_TOKEN env var is required for rathole tunnel — continuing without a public tunnel.');
|
|
17
|
+
return { tunnel: null, publicUrl: null };
|
|
18
|
+
}
|
|
19
|
+
tunnel = new RatholeTunnel({ serverAddr: tunnelCfg.serverAddr, publicUrl: tunnelCfg.publicUrl, token, ssh: tunnelCfg.ssh ?? undefined });
|
|
20
|
+
}
|
|
21
|
+
try {
|
|
22
|
+
await tunnel.setupServer();
|
|
23
|
+
const { publicUrl } = await tunnel.start(port);
|
|
24
|
+
step(`Tunnel ready — Public URL: ${publicUrl}`);
|
|
25
|
+
return { tunnel, publicUrl };
|
|
26
|
+
}
|
|
27
|
+
catch (err) {
|
|
28
|
+
console.warn(`Tunnel failed to start: ${err instanceof Error ? err.message : String(err)}`);
|
|
29
|
+
return { tunnel: null, publicUrl: null };
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=tunnel-runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tunnel-runner.js","sourceRoot":"","sources":["../../src/lib/tunnel-runner.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AACvD,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AAUvC;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,SAAuB,EAAE,IAAY;IAC/E,IAAI,MAAoB,CAAA;IACxB,IAAI,SAAS,CAAC,QAAQ,KAAK,WAAW,EAAE,CAAC;QACvC,MAAM,GAAG,IAAI,eAAe,CAAC,EAAE,SAAS,EAAE,SAAS,CAAC,SAAS,EAAE,CAAC,CAAA;IAClE,CAAC;SAAM,CAAC;QACN,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB,IAAI,EAAE,CAAA;QACpD,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,IAAI,CAAC,mGAAmG,CAAC,CAAA;YACzG,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAA;QAC1C,CAAC;QACD,MAAM,GAAG,IAAI,aAAa,CAAC,EAAE,UAAU,EAAE,SAAS,CAAC,UAAU,EAAE,SAAS,EAAE,SAAS,CAAC,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,SAAS,CAAC,GAAG,IAAI,SAAS,EAAE,CAAC,CAAA;IAC1I,CAAC;IAED,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,WAAW,EAAE,CAAA;QAC1B,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QAC9C,IAAI,CAAC,8BAA8B,SAAS,EAAE,CAAC,CAAA;QAC/C,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,CAAA;IAC9B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,IAAI,CAAC,2BAA2B,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;QAC3F,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAA;IAC1C,CAAC;AACH,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tapflow",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Self-hosted iOS/Android simulator streaming for the whole team",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ios",
|
|
@@ -47,10 +47,10 @@
|
|
|
47
47
|
"cac": "^6.7.14",
|
|
48
48
|
"ws": "^8.0.0",
|
|
49
49
|
"zod": "^4.4.3",
|
|
50
|
-
"@tapflowio/agent-core": "0.
|
|
51
|
-
"@tapflowio/android-agent": "0.
|
|
52
|
-
"@tapflowio/ios-agent": "0.
|
|
53
|
-
"@tapflowio/relay": "0.
|
|
50
|
+
"@tapflowio/agent-core": "0.5.0",
|
|
51
|
+
"@tapflowio/android-agent": "0.5.0",
|
|
52
|
+
"@tapflowio/ios-agent": "0.5.0",
|
|
53
|
+
"@tapflowio/relay": "0.5.0"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
56
|
"@types/node": "^20.0.0",
|