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 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 management, no cloud uploads.<br />
8
- App data never leaves your network.
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/blob/main/CONTRIBUTING.md"><img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg" alt="PRs welcome" /></a>
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
- If you work on a mobile product, you've probably seen this.
40
+ Mobile QA usually depends on access to simulators, emulators, or physical devices — and that access is uneven across a team.
39
41
 
40
- Physical devices are never enough. Covering every OS version is even harder iOS doesn't support downgrading, so maintaining a range of versions means managing a pool of locked devices, which is overhead nobody wants.
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
- But the bigger friction is access. Simulators only run on a developer's Mac, behind complex developer toolchains. Anyone on the team who isn't a mobile developer has to ask one every single time they need to verify something:
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 having to install and remove different versions just to compare behavior."
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 exist. But uploading internal app builds to an external service and paying monthly fees for simulators already running on Macs you own was never something we wanted to do.
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
- So we built tapflow.
52
+ We hit this exact problem, so we built tapflow.
53
53
 
54
- | Solution | Problem |
55
- | ----------------------- | ------------------------------------------------------------------------ |
56
- | Appetize / BrowserStack | Expensive — app data leaves your network |
57
- | Physical devices | Cost, loss, management overhead |
58
- | Xcode / Android Studio | Every QA member needs their own Mac + full toolchain setup |
59
- | **tapflow** | Use the Mac you already own — data stays on-prem, whole team does QA from a browser |
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
- ## Features
61
+ ## What tapflow does
62
62
 
63
- tapflow focuses on:
63
+ tapflow connects three parts:
64
64
 
65
- - **Zero setup for QA** — any browser, no toolchain. Designers, PMs, and server devs test without asking a mobile developer.
66
- - **Data on-premises** app binaries and session recordings never leave your network.
67
- - **Your existing Mac** no new hardware, no monthly cloud subscription.
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
- What's included:
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
- - **iOS & Android streaming** — ~30 fps, no additional app required on the device
73
- - **Touch, swipe & pinch** — real-time input forwarded to the simulator
74
- - **Deeplink** jump directly to any screen from the QA toolbar, no manual navigation
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) WebSocket Relay Server WebSocket (outbound) Mac Agent
87
- (Linux / Mac) (iOS · Android)
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` where you can create the admin account.
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, Xcode, `adb`, and other prerequisites.
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 | Requirements |
129
- | ----------------- | ------------------------------------------------------------------------------------------------------------------------- |
130
- | **Relay server** | Node.js ≥ 20, any OS (Linux/macOS), ~512 MB RAM |
131
- | **iOS Agent** | macOS, Xcode with iOS Simulator Runtime, Node.js ≥ 20 |
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** | Any modern browser — Chrome, Firefox, Safari, Edge |
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 | Description |
169
- | ----------------------------------- | ----------------------------------------------- |
170
- | `tapflow start` | Start relay + agent together (local mode) |
171
- | `tapflow relay start` | Start relay only |
172
- | `tapflow agent start --relay <url>` | Start agent and connect to a relay |
173
- | `tapflow init` | Scaffold `tapflow.config.json` |
174
- | `tapflow admin init` | Create the first admin account (CLI fallback) |
175
- | `tapflow doctor` | Diagnose environment (Node, Xcode, adb…) |
176
- | `tapflow devices` | List available simulators and emulators |
177
- | `tapflow boot <name\|udid>` | Boot a simulator or emulator |
178
- | `tapflow status` | Show connected agents, devices, active sessions |
179
- | `tapflow reset` | Shut down all simulators and emulators |
180
- | `tapflow logs` | Show recent relay log entries |
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
- ## Development
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 © 2025-present tapflow contributors
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":"AASA,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,CAuE1E"}
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 { RatholeTunnel } from '../lib/rathole-tunnel.js';
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
- if (tunnelCfg.provider === 'tailscale') {
38
- tunnel = new TailscaleTunnel({ publicUrl: tunnelCfg.publicUrl });
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":"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"}
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;AAGjC,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,CAsEhE"}
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"}
@@ -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. Agent availability check ───────────────────────────────────────────
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
- // ── 3. Connect each registered platform ──────────────────────────────────
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
- `Open http://localhost:${RELAY_PORT} in your browser.`,
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":"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"}
1
+ {"version":3,"file":"start.js","sourceRoot":"","sources":["../../src/commands/start.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/lib/doctor.ts"],"names":[],"mappings":"AAEA,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"}
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"}
@@ -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 to see available simulators, then: tapflow boot "<name>"';
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?' };
@@ -1 +1 @@
1
- {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/lib/doctor.ts"],"names":[],"mappings":"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"}
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.4.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.4.0",
51
- "@tapflowio/android-agent": "0.4.0",
52
- "@tapflowio/ios-agent": "0.4.0",
53
- "@tapflowio/relay": "0.4.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",