apptuner 0.1.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 ADDED
@@ -0,0 +1,101 @@
1
+ # AppTuner
2
+
3
+ **Instant hot reload for React Native** - See your changes in real-time on physical devices.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/apptuner.svg)](https://www.npmjs.com/package/apptuner)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ ## What is AppTuner?
9
+
10
+ AppTuner brings **instant hot reload** to React Native development. Edit your code, save the file, and see changes on your physical device in seconds - no manual refresh needed.
11
+
12
+ ### Why AppTuner?
13
+
14
+ - ⚡ **Instant Updates** - Changes appear on your device automatically
15
+ - 📱 **Real Devices** - Test on actual iPhones and Android phones, not simulators
16
+ - 🌍 **Works Anywhere** - Cloud relay means it works on any WiFi network
17
+ - 🎯 **Zero Config** - Works with existing React Native projects
18
+ - 🚀 **Metro Integration** - Uses the official React Native bundler
19
+
20
+ ## Installation
21
+
22
+ ```bash
23
+ npm install -g apptuner
24
+ ```
25
+
26
+ ## Quick Start
27
+
28
+ 1. **Install the mobile app** (coming soon to App Store & Play Store)
29
+
30
+ 2. **Navigate to your React Native project:**
31
+ ```bash
32
+ cd your-react-native-project
33
+ ```
34
+
35
+ 3. **Start AppTuner:**
36
+ ```bash
37
+ apptuner start
38
+ ```
39
+
40
+ 4. **Scan the QR code** with the AppTuner mobile app
41
+
42
+ 5. **Start coding!** Every save automatically updates your device ✨
43
+
44
+ ## Commands
45
+
46
+ ### `apptuner start`
47
+
48
+ Starts the AppTuner development server with hot reload enabled.
49
+
50
+ ```bash
51
+ apptuner start
52
+ ```
53
+
54
+ The CLI will:
55
+ - Start Metro bundler
56
+ - Start file watcher
57
+ - Connect to cloud relay
58
+ - Display a QR code
59
+ - Automatically bundle and send changes to your device
60
+
61
+ ### `apptuner status`
62
+
63
+ Check connection status.
64
+
65
+ ### `apptuner stop`
66
+
67
+ Stop all AppTuner services.
68
+
69
+ ## How It Works
70
+
71
+ 1. **File Watcher** detects when you save a file
72
+ 2. **Metro Bundler** compiles your React Native code
73
+ 3. **Cloud Relay** sends the bundle to your phone
74
+ 4. **Mobile App** hot reloads with your changes
75
+
76
+ All automatic. No button presses. No manual refreshes.
77
+
78
+ ## Requirements
79
+
80
+ - **Node.js** 18 or higher
81
+ - **React Native** project (0.70+)
82
+ - **AppTuner mobile app** (iOS/Android)
83
+
84
+ ## Pricing
85
+
86
+ - **7-day free trial** - Full features, no credit card
87
+ - **$29/month** - Cancel anytime
88
+ - **$99/year** - Save $249/year
89
+ - **$199 lifetime** - Pay once, use forever
90
+
91
+ [Start your free trial →](https://apptuner.io)
92
+
93
+ ## Support
94
+
95
+ - **Website:** [apptuner.io](https://apptuner.io)
96
+ - **Issues:** [github.com/pepijnvanderknaap/apptuner/issues](https://github.com/pepijnvanderknaap/apptuner/issues)
97
+ - **Email:** support@apptuner.io
98
+
99
+ ## License
100
+
101
+ MIT © Pepijn van der Knaap
package/dist/cli.js ADDED
@@ -0,0 +1,360 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src-cli/cli.ts
4
+ import { Command } from "commander";
5
+
6
+ // src-cli/commands/start.ts
7
+ import { spawn } from "child_process";
8
+ import path from "path";
9
+ import { fileURLToPath } from "url";
10
+ import fs from "fs/promises";
11
+ import chalk from "chalk";
12
+ import ora from "ora";
13
+ import { WebSocket } from "ws";
14
+ import { exec } from "child_process";
15
+ var __filename = fileURLToPath(import.meta.url);
16
+ var __dirname = path.dirname(__filename);
17
+ function generateSessionId() {
18
+ const chars = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
19
+ let result = "";
20
+ for (let i = 0; i < 6; i++) {
21
+ result += chars.charAt(Math.floor(Math.random() * chars.length));
22
+ }
23
+ return result;
24
+ }
25
+ var RelayAdapter = class {
26
+ ws = null;
27
+ updateWebSocket(ws) {
28
+ this.ws = ws;
29
+ }
30
+ sendBundleUpdate(bundleCode) {
31
+ if (!this.ws || this.ws.readyState !== this.ws.OPEN) {
32
+ console.warn(chalk.yellow("\u26A0\uFE0F Cannot send bundle - relay not connected"));
33
+ return;
34
+ }
35
+ this.ws.send(JSON.stringify({
36
+ type: "bundle_update",
37
+ payload: { code: bundleCode },
38
+ timestamp: Date.now()
39
+ }));
40
+ }
41
+ };
42
+ async function startCommand(options) {
43
+ const spinner = ora();
44
+ console.log(chalk.blue.bold("\n\u{1F680} AppTuner\n"));
45
+ spinner.start("Validating React Native project...");
46
+ const projectPath = path.resolve(options.project);
47
+ const packageJsonPath = path.join(projectPath, "package.json");
48
+ try {
49
+ await fs.access(packageJsonPath);
50
+ } catch {
51
+ spinner.fail("No package.json found");
52
+ console.error(chalk.red(`
53
+ \u274C Could not find package.json at ${projectPath}`));
54
+ console.log(chalk.gray("\nMake sure you're in a React Native project directory.\n"));
55
+ process.exit(1);
56
+ }
57
+ const packageJson = JSON.parse(await fs.readFile(packageJsonPath, "utf-8"));
58
+ if (!packageJson.dependencies?.["react-native"]) {
59
+ spinner.fail("Not a React Native project");
60
+ console.error(chalk.red("\n\u274C This is not a React Native project"));
61
+ console.log(chalk.gray("No react-native dependency found in package.json\n"));
62
+ process.exit(1);
63
+ }
64
+ spinner.succeed(`Project validated: ${chalk.cyan(packageJson.name || "Unnamed Project")}`);
65
+ const sessionId = generateSessionId();
66
+ spinner.succeed(`Session ID: ${chalk.cyan(sessionId)}`);
67
+ console.log(chalk.white("\nStarting local services...\n"));
68
+ const rootDir = path.join(__dirname, "..");
69
+ spinner.start("Starting Metro bundler...");
70
+ const metroProcess = spawn("node", [path.join(rootDir, "metro-server.cjs")], {
71
+ cwd: projectPath,
72
+ stdio: "inherit",
73
+ detached: false
74
+ });
75
+ await new Promise((resolve) => setTimeout(resolve, 2e3));
76
+ spinner.succeed("Metro bundler started (port 3031)");
77
+ spinner.start("Starting file watcher...");
78
+ const watcherProcess = spawn("node", [path.join(rootDir, "watcher-server.cjs")], {
79
+ cwd: projectPath,
80
+ stdio: "inherit",
81
+ detached: false
82
+ });
83
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
84
+ spinner.succeed("File watcher started (port 3030)");
85
+ const relayUrl = process.env.APPTUNER_RELAY_URL || "wss://apptuner-relay.falling-bird-3f63.workers.dev";
86
+ const isLocalTesting = relayUrl.includes("localhost") || relayUrl.includes("127.0.0.1");
87
+ const dashboardUrl = isLocalTesting ? `http://localhost:1420/?session=${sessionId}` : `https://apptuner-dashboard.pages.dev/?session=${sessionId}`;
88
+ let isShuttingDown = false;
89
+ let isFirstConnect = true;
90
+ let reconnectTimeout = null;
91
+ let currentWs = null;
92
+ const relayAdapter = new RelayAdapter();
93
+ let autoReloadStarted = false;
94
+ async function startAutoReload() {
95
+ if (autoReloadStarted) return;
96
+ autoReloadStarted = true;
97
+ let isBundling = false;
98
+ let pendingBundle = false;
99
+ async function requestBundle() {
100
+ if (isBundling) {
101
+ pendingBundle = true;
102
+ return;
103
+ }
104
+ isBundling = true;
105
+ pendingBundle = false;
106
+ const startTime = Date.now();
107
+ console.log(chalk.gray("\u{1F4E6} Bundling..."));
108
+ try {
109
+ const code = await new Promise((resolve, reject) => {
110
+ const metroWs = new WebSocket("ws://localhost:3031");
111
+ metroWs.on("open", () => {
112
+ metroWs.send(JSON.stringify({
113
+ type: "bundle",
114
+ projectPath,
115
+ entryPoint: "App.tsx"
116
+ }));
117
+ });
118
+ metroWs.on("message", (data) => {
119
+ const msg = JSON.parse(data.toString());
120
+ if (msg.type === "bundle_ready") {
121
+ metroWs.close();
122
+ if (msg.fromCache) {
123
+ console.log(chalk.gray("\u26A1 No changes \u2014 using cached bundle"));
124
+ }
125
+ resolve(msg.code);
126
+ } else if (msg.type === "bundle_error") {
127
+ metroWs.close();
128
+ reject(new Error(
129
+ typeof msg.error === "object" ? msg.error.message : msg.error || "Bundle failed"
130
+ ));
131
+ }
132
+ });
133
+ metroWs.on("error", (err) => reject(err));
134
+ setTimeout(() => reject(new Error("Bundle timeout (60s)")), 6e4);
135
+ });
136
+ const sizeKB = Math.round(code.length / 1024);
137
+ const timeMs = Date.now() - startTime;
138
+ relayAdapter.sendBundleUpdate(code);
139
+ console.log(chalk.cyan(`\u{1F4E6} Bundle sent: ${chalk.white(sizeKB + " KB")} in ${chalk.white(timeMs + "ms")}`));
140
+ } catch (error) {
141
+ console.error(chalk.red("\u274C Bundle error:"), error.message);
142
+ }
143
+ isBundling = false;
144
+ if (pendingBundle) {
145
+ requestBundle();
146
+ }
147
+ }
148
+ const watcherWs = new WebSocket("ws://localhost:3030");
149
+ watcherWs.on("open", () => {
150
+ watcherWs.send(JSON.stringify({
151
+ type: "watch",
152
+ path: projectPath,
153
+ extensions: [".js", ".jsx", ".ts", ".tsx", ".json"]
154
+ }));
155
+ });
156
+ watcherWs.on("message", async (data) => {
157
+ const msg = JSON.parse(data.toString());
158
+ if (msg.type === "watcher_ready") {
159
+ console.log(chalk.green("\u2705 Auto-reload active - edit your files to see changes!\n"));
160
+ requestBundle();
161
+ } else if (msg.type === "file_changed") {
162
+ console.log(chalk.yellow(`\u{1F4DD} ${msg.relativePath} changed`));
163
+ requestBundle();
164
+ }
165
+ });
166
+ watcherWs.on("error", (err) => {
167
+ console.error(chalk.red("Watcher error:"), err.message);
168
+ });
169
+ watcherWs.on("close", () => {
170
+ if (!isShuttingDown) {
171
+ console.log(chalk.yellow("Watcher disconnected"));
172
+ }
173
+ });
174
+ }
175
+ function connectToRelay() {
176
+ if (isShuttingDown) return;
177
+ spinner.start("Connecting to relay server...");
178
+ const ws = new WebSocket(`${relayUrl}/cli/${sessionId}`);
179
+ currentWs = ws;
180
+ let pingInterval = null;
181
+ ws.on("open", async () => {
182
+ relayAdapter.updateWebSocket(ws);
183
+ if (isFirstConnect) {
184
+ spinner.succeed("Connected to relay");
185
+ isFirstConnect = false;
186
+ console.log(chalk.green.bold("\n\u2705 AppTuner is running!\n"));
187
+ console.log(chalk.white(`Dashboard: ${chalk.cyan(dashboardUrl)}`));
188
+ console.log(chalk.gray("\nOpening browser...\n"));
189
+ const openCommand = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
190
+ exec(`${openCommand} "${dashboardUrl}"`);
191
+ await startAutoReload();
192
+ } else {
193
+ spinner.succeed("Reconnected to relay");
194
+ console.log(chalk.green("\u2705 Relay reconnected\n"));
195
+ }
196
+ pingInterval = setInterval(() => {
197
+ if (ws.readyState === ws.OPEN) {
198
+ ws.send(JSON.stringify({ type: "ping", timestamp: Date.now() }));
199
+ }
200
+ }, 2e4);
201
+ });
202
+ ws.on("message", (data) => {
203
+ try {
204
+ const message = JSON.parse(data.toString());
205
+ if (message.type === "mobile_connected") {
206
+ console.log(chalk.green(`
207
+ \u{1F4F1} Mobile device connected: ${message.deviceName || "Unknown"}`));
208
+ }
209
+ if (message.type === "mobile_disconnected") {
210
+ console.log(chalk.yellow(`
211
+ \u{1F4F1} Mobile device disconnected`));
212
+ }
213
+ } catch (error) {
214
+ console.error("Error parsing relay message:", error);
215
+ }
216
+ });
217
+ ws.on("error", (error) => {
218
+ if (isFirstConnect) {
219
+ spinner.fail("Relay connection failed");
220
+ console.error(chalk.red("\n\u274C Could not connect to relay server"));
221
+ console.error(chalk.gray(error.message));
222
+ }
223
+ });
224
+ ws.on("close", (code) => {
225
+ if (pingInterval) {
226
+ clearInterval(pingInterval);
227
+ pingInterval = null;
228
+ }
229
+ if (isShuttingDown) return;
230
+ if (isFirstConnect) {
231
+ console.error(chalk.red(`
232
+ \u274C Failed to connect to relay (code ${code})`));
233
+ process.exit(1);
234
+ }
235
+ console.log(chalk.yellow(`
236
+ \u26A0\uFE0F Relay disconnected (code ${code}), reconnecting in 5s...`));
237
+ reconnectTimeout = setTimeout(connectToRelay, 5e3);
238
+ });
239
+ }
240
+ connectToRelay();
241
+ const pidFile = path.join(process.cwd(), ".apptuner-pids.json");
242
+ await fs.writeFile(pidFile, JSON.stringify({
243
+ metro: metroProcess.pid,
244
+ watcher: watcherProcess.pid,
245
+ sessionId
246
+ }, null, 2));
247
+ process.on("SIGINT", async () => {
248
+ console.log(chalk.yellow("\n\n\u23F9\uFE0F Stopping AppTuner..."));
249
+ isShuttingDown = true;
250
+ if (reconnectTimeout) {
251
+ clearTimeout(reconnectTimeout);
252
+ reconnectTimeout = null;
253
+ }
254
+ if (currentWs) {
255
+ currentWs.close();
256
+ }
257
+ if (metroProcess.pid) {
258
+ process.kill(metroProcess.pid, "SIGTERM");
259
+ }
260
+ if (watcherProcess.pid) {
261
+ process.kill(watcherProcess.pid, "SIGTERM");
262
+ }
263
+ await fs.unlink(pidFile).catch(() => {
264
+ });
265
+ console.log(chalk.gray("All services stopped.\n"));
266
+ process.exit(0);
267
+ });
268
+ }
269
+
270
+ // src-cli/commands/stop.ts
271
+ import fs2 from "fs/promises";
272
+ import path2 from "path";
273
+ import chalk2 from "chalk";
274
+ async function stopCommand() {
275
+ console.log(chalk2.yellow("\n\u23F9\uFE0F Stopping AppTuner...\n"));
276
+ const pidFile = path2.join(process.cwd(), ".apptuner-pids.json");
277
+ try {
278
+ const pidsData = await fs2.readFile(pidFile, "utf-8");
279
+ const pids = JSON.parse(pidsData);
280
+ if (pids.metro) {
281
+ try {
282
+ process.kill(-pids.metro, "SIGTERM");
283
+ console.log(chalk2.gray("\u2713 Metro server stopped"));
284
+ } catch (error) {
285
+ console.log(chalk2.gray("\u2713 Metro server already stopped"));
286
+ }
287
+ }
288
+ if (pids.watcher) {
289
+ try {
290
+ process.kill(-pids.watcher, "SIGTERM");
291
+ console.log(chalk2.gray("\u2713 File watcher stopped"));
292
+ } catch (error) {
293
+ console.log(chalk2.gray("\u2713 File watcher already stopped"));
294
+ }
295
+ }
296
+ await fs2.unlink(pidFile);
297
+ console.log(chalk2.green("\n\u2705 All services stopped\n"));
298
+ } catch (error) {
299
+ console.log(chalk2.gray("No running AppTuner services found.\n"));
300
+ }
301
+ }
302
+
303
+ // src-cli/commands/status.ts
304
+ import fs3 from "fs/promises";
305
+ import path3 from "path";
306
+ import chalk3 from "chalk";
307
+ async function statusCommand() {
308
+ console.log(chalk3.blue.bold("\n\u{1F4CA} AppTuner Status\n"));
309
+ const pidFile = path3.join(process.cwd(), ".apptuner-pids.json");
310
+ try {
311
+ const pidsData = await fs3.readFile(pidFile, "utf-8");
312
+ const pids = JSON.parse(pidsData);
313
+ console.log(chalk3.white("Services:"));
314
+ let metroRunning = false;
315
+ let watcherRunning = false;
316
+ if (pids.metro) {
317
+ try {
318
+ process.kill(pids.metro, 0);
319
+ metroRunning = true;
320
+ } catch {
321
+ metroRunning = false;
322
+ }
323
+ }
324
+ if (pids.watcher) {
325
+ try {
326
+ process.kill(pids.watcher, 0);
327
+ watcherRunning = true;
328
+ } catch {
329
+ watcherRunning = false;
330
+ }
331
+ }
332
+ console.log(
333
+ ` ${metroRunning ? chalk3.green("\u25CF") : chalk3.red("\u25CF")} Metro bundler ${metroRunning ? chalk3.gray("(port 3031)") : chalk3.gray("(stopped)")}`
334
+ );
335
+ console.log(
336
+ ` ${watcherRunning ? chalk3.green("\u25CF") : chalk3.red("\u25CF")} File watcher ${watcherRunning ? chalk3.gray("(port 3030)") : chalk3.gray("(stopped)")}`
337
+ );
338
+ if (pids.sessionId) {
339
+ console.log(chalk3.white(`
340
+ Session: ${chalk3.cyan(pids.sessionId)}`));
341
+ }
342
+ if (metroRunning && watcherRunning) {
343
+ console.log(chalk3.green("\n\u2705 AppTuner is running\n"));
344
+ } else {
345
+ console.log(chalk3.yellow("\n\u26A0\uFE0F Some services are not running\n"));
346
+ }
347
+ } catch (error) {
348
+ console.log(chalk3.gray(" No running services\n"));
349
+ console.log(chalk3.gray("Run `apptuner start` to begin.\n"));
350
+ }
351
+ }
352
+
353
+ // src-cli/cli.ts
354
+ var program = new Command();
355
+ program.name("apptuner").description("Hot reload React Native apps instantly").version("0.1.0");
356
+ program.command("start").description("Start AppTuner development server").option("-p, --project <path>", "Path to your React Native project", process.cwd()).option("--no-qr", "Disable QR code display").action(startCommand);
357
+ program.command("stop").description("Stop all AppTuner services").action(stopCommand);
358
+ program.command("status").description("Show AppTuner connection status").action(statusCommand);
359
+ program.parse();
360
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src-cli/cli.ts", "../src-cli/commands/start.ts", "../src-cli/commands/stop.ts", "../src-cli/commands/status.ts"],
4
+ "sourcesContent": ["#!/usr/bin/env node\n\nimport { Command } from 'commander';\nimport chalk from 'chalk';\nimport { startCommand } from './commands/start.js';\nimport { stopCommand } from './commands/stop.js';\nimport { statusCommand } from './commands/status.js';\n\nconst program = new Command();\n\nprogram\n .name('apptuner')\n .description('Hot reload React Native apps instantly')\n .version('0.1.0');\n\nprogram\n .command('start')\n .description('Start AppTuner development server')\n .option('-p, --project <path>', 'Path to your React Native project', process.cwd())\n .option('--no-qr', 'Disable QR code display')\n .action(startCommand);\n\nprogram\n .command('stop')\n .description('Stop all AppTuner services')\n .action(stopCommand);\n\nprogram\n .command('status')\n .description('Show AppTuner connection status')\n .action(statusCommand);\n\nprogram.parse();\n", "import { spawn } from 'child_process';\nimport path from 'path';\nimport { fileURLToPath } from 'url';\nimport fs from 'fs/promises';\nimport chalk from 'chalk';\nimport ora from 'ora';\nimport { WebSocket } from 'ws';\nimport { exec } from 'child_process';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\ninterface StartOptions {\n project: string;\n qr: boolean;\n}\n\n// Generate random 6-character session ID\nfunction generateSessionId(): string {\n const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';\n let result = '';\n for (let i = 0; i < 6; i++) {\n result += chars.charAt(Math.floor(Math.random() * chars.length));\n }\n return result;\n}\n\n// Adapter that wraps the relay WebSocket for sending bundles - supports hot-swapping on reconnect\nclass RelayAdapter {\n private ws: WebSocket | null = null;\n\n updateWebSocket(ws: WebSocket): void {\n this.ws = ws;\n }\n\n sendBundleUpdate(bundleCode: string): void {\n if (!this.ws || this.ws.readyState !== this.ws.OPEN) {\n console.warn(chalk.yellow('\u26A0\uFE0F Cannot send bundle - relay not connected'));\n return;\n }\n this.ws.send(JSON.stringify({\n type: 'bundle_update',\n payload: { code: bundleCode },\n timestamp: Date.now(),\n }));\n }\n}\n\nexport async function startCommand(options: StartOptions) {\n const spinner = ora();\n\n console.log(chalk.blue.bold('\\n\uD83D\uDE80 AppTuner\\n'));\n\n // Step 1: Validate project\n spinner.start('Validating React Native project...');\n const projectPath = path.resolve(options.project);\n const packageJsonPath = path.join(projectPath, 'package.json');\n\n try {\n await fs.access(packageJsonPath);\n } catch {\n spinner.fail('No package.json found');\n console.error(chalk.red(`\\n\u274C Could not find package.json at ${projectPath}`));\n console.log(chalk.gray('\\nMake sure you\\'re in a React Native project directory.\\n'));\n process.exit(1);\n }\n\n const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf-8'));\n\n if (!packageJson.dependencies?.['react-native']) {\n spinner.fail('Not a React Native project');\n console.error(chalk.red('\\n\u274C This is not a React Native project'));\n console.log(chalk.gray('No react-native dependency found in package.json\\n'));\n process.exit(1);\n }\n\n spinner.succeed(`Project validated: ${chalk.cyan(packageJson.name || 'Unnamed Project')}`);\n\n // Step 2: Generate session ID (stays the same across reconnects)\n const sessionId = generateSessionId();\n spinner.succeed(`Session ID: ${chalk.cyan(sessionId)}`);\n\n // Step 3: Start local services\n console.log(chalk.white('\\nStarting local services...\\n'));\n\n // __dirname in compiled dist/cli.js is the dist/ folder, so go up one level to project root\n const rootDir = path.join(__dirname, '..');\n\n // Start Metro bundler\n spinner.start('Starting Metro bundler...');\n const metroProcess = spawn('node', [path.join(rootDir, 'metro-server.cjs')], {\n cwd: projectPath,\n stdio: 'inherit',\n detached: false,\n });\n await new Promise(resolve => setTimeout(resolve, 2000));\n spinner.succeed('Metro bundler started (port 3031)');\n\n // Start file watcher\n spinner.start('Starting file watcher...');\n const watcherProcess = spawn('node', [path.join(rootDir, 'watcher-server.cjs')], {\n cwd: projectPath,\n stdio: 'inherit',\n detached: false,\n });\n await new Promise(resolve => setTimeout(resolve, 1000));\n spinner.succeed('File watcher started (port 3030)');\n\n // Step 4: Connect to relay with auto-reconnect\n const relayUrl = process.env.APPTUNER_RELAY_URL || 'wss://apptuner-relay.falling-bird-3f63.workers.dev';\n const isLocalTesting = relayUrl.includes('localhost') || relayUrl.includes('127.0.0.1');\n const dashboardUrl = isLocalTesting\n ? `http://localhost:1420/?session=${sessionId}`\n : `https://apptuner-dashboard.pages.dev/?session=${sessionId}`;\n\n let isShuttingDown = false;\n let isFirstConnect = true;\n let reconnectTimeout: ReturnType<typeof setTimeout> | null = null;\n let currentWs: WebSocket | null = null;\n const relayAdapter = new RelayAdapter();\n\n // Step 5: Setup direct connections to local watcher + metro servers\n let autoReloadStarted = false;\n\n async function startAutoReload() {\n if (autoReloadStarted) return;\n autoReloadStarted = true;\n\n let isBundling = false;\n let pendingBundle = false;\n\n async function requestBundle(): Promise<void> {\n if (isBundling) {\n pendingBundle = true;\n return;\n }\n isBundling = true;\n pendingBundle = false;\n\n const startTime = Date.now();\n console.log(chalk.gray('\uD83D\uDCE6 Bundling...'));\n\n try {\n const code = await new Promise<string>((resolve, reject) => {\n const metroWs = new WebSocket('ws://localhost:3031');\n\n metroWs.on('open', () => {\n metroWs.send(JSON.stringify({\n type: 'bundle',\n projectPath,\n entryPoint: 'App.tsx',\n }));\n });\n\n metroWs.on('message', (data) => {\n const msg = JSON.parse(data.toString());\n if (msg.type === 'bundle_ready') {\n metroWs.close();\n if (msg.fromCache) {\n console.log(chalk.gray('\u26A1 No changes \u2014 using cached bundle'));\n }\n resolve(msg.code);\n } else if (msg.type === 'bundle_error') {\n metroWs.close();\n reject(new Error(\n typeof msg.error === 'object' ? msg.error.message : (msg.error || 'Bundle failed')\n ));\n }\n });\n\n metroWs.on('error', (err) => reject(err));\n\n // 60s timeout for large projects\n setTimeout(() => reject(new Error('Bundle timeout (60s)')), 60000);\n });\n\n const sizeKB = Math.round(code.length / 1024);\n const timeMs = Date.now() - startTime;\n relayAdapter.sendBundleUpdate(code);\n console.log(chalk.cyan(`\uD83D\uDCE6 Bundle sent: ${chalk.white(sizeKB + ' KB')} in ${chalk.white(timeMs + 'ms')}`));\n } catch (error: any) {\n console.error(chalk.red('\u274C Bundle error:'), error.message);\n }\n\n isBundling = false;\n\n // If another change came in while bundling, run again\n if (pendingBundle) {\n requestBundle();\n }\n }\n\n // Connect to watcher server\n const watcherWs = new WebSocket('ws://localhost:3030');\n\n watcherWs.on('open', () => {\n // Tell watcher to watch the project directory\n watcherWs.send(JSON.stringify({\n type: 'watch',\n path: projectPath,\n extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'],\n }));\n });\n\n watcherWs.on('message', async (data) => {\n const msg = JSON.parse(data.toString());\n\n if (msg.type === 'watcher_ready') {\n console.log(chalk.green('\u2705 Auto-reload active - edit your files to see changes!\\n'));\n // Send initial bundle\n requestBundle();\n } else if (msg.type === 'file_changed') {\n console.log(chalk.yellow(`\uD83D\uDCDD ${msg.relativePath} changed`));\n requestBundle();\n }\n });\n\n watcherWs.on('error', (err) => {\n console.error(chalk.red('Watcher error:'), err.message);\n });\n\n watcherWs.on('close', () => {\n if (!isShuttingDown) {\n console.log(chalk.yellow('Watcher disconnected'));\n }\n });\n }\n\n function connectToRelay() {\n if (isShuttingDown) return;\n\n spinner.start('Connecting to relay server...');\n const ws = new WebSocket(`${relayUrl}/cli/${sessionId}`);\n currentWs = ws;\n\n let pingInterval: ReturnType<typeof setInterval> | null = null;\n\n ws.on('open', async () => {\n relayAdapter.updateWebSocket(ws);\n\n if (isFirstConnect) {\n spinner.succeed('Connected to relay');\n isFirstConnect = false;\n\n console.log(chalk.green.bold('\\n\u2705 AppTuner is running!\\n'));\n console.log(chalk.white(`Dashboard: ${chalk.cyan(dashboardUrl)}`));\n console.log(chalk.gray('\\nOpening browser...\\n'));\n\n const openCommand = process.platform === 'darwin' ? 'open' :\n process.platform === 'win32' ? 'start' : 'xdg-open';\n exec(`${openCommand} \"${dashboardUrl}\"`);\n\n await startAutoReload();\n } else {\n spinner.succeed('Reconnected to relay');\n console.log(chalk.green('\u2705 Relay reconnected\\n'));\n }\n\n // Keepalive pings every 20s (Cloudflare drops idle WS after ~30s)\n pingInterval = setInterval(() => {\n if (ws.readyState === ws.OPEN) {\n ws.send(JSON.stringify({ type: 'ping', timestamp: Date.now() }));\n }\n }, 20000);\n });\n\n ws.on('message', (data) => {\n try {\n const message = JSON.parse(data.toString());\n if (message.type === 'mobile_connected') {\n console.log(chalk.green(`\\n\uD83D\uDCF1 Mobile device connected: ${message.deviceName || 'Unknown'}`));\n }\n if (message.type === 'mobile_disconnected') {\n console.log(chalk.yellow(`\\n\uD83D\uDCF1 Mobile device disconnected`));\n }\n } catch (error) {\n console.error('Error parsing relay message:', error);\n }\n });\n\n ws.on('error', (error) => {\n if (isFirstConnect) {\n spinner.fail('Relay connection failed');\n console.error(chalk.red('\\n\u274C Could not connect to relay server'));\n console.error(chalk.gray(error.message));\n }\n // close event fires after error, triggering reconnect\n });\n\n ws.on('close', (code) => {\n if (pingInterval) {\n clearInterval(pingInterval);\n pingInterval = null;\n }\n\n if (isShuttingDown) return;\n\n if (isFirstConnect) {\n console.error(chalk.red(`\\n\u274C Failed to connect to relay (code ${code})`));\n process.exit(1);\n }\n\n console.log(chalk.yellow(`\\n\u26A0\uFE0F Relay disconnected (code ${code}), reconnecting in 5s...`));\n reconnectTimeout = setTimeout(connectToRelay, 5000);\n });\n }\n\n // Start relay connection\n connectToRelay();\n\n // Save PIDs for cleanup\n const pidFile = path.join(process.cwd(), '.apptuner-pids.json');\n await fs.writeFile(pidFile, JSON.stringify({\n metro: metroProcess.pid,\n watcher: watcherProcess.pid,\n sessionId,\n }, null, 2));\n\n // Graceful shutdown\n process.on('SIGINT', async () => {\n console.log(chalk.yellow('\\n\\n\u23F9\uFE0F Stopping AppTuner...'));\n\n isShuttingDown = true;\n\n if (reconnectTimeout) {\n clearTimeout(reconnectTimeout);\n reconnectTimeout = null;\n }\n\n if (currentWs) {\n currentWs.close();\n }\n\n if (metroProcess.pid) {\n process.kill(metroProcess.pid, 'SIGTERM');\n }\n\n if (watcherProcess.pid) {\n process.kill(watcherProcess.pid, 'SIGTERM');\n }\n\n await fs.unlink(pidFile).catch(() => {});\n\n console.log(chalk.gray('All services stopped.\\n'));\n process.exit(0);\n });\n}\n", "import fs from 'fs/promises';\nimport path from 'path';\nimport chalk from 'chalk';\n\nexport async function stopCommand() {\n console.log(chalk.yellow('\\n\u23F9\uFE0F Stopping AppTuner...\\n'));\n\n const pidFile = path.join(process.cwd(), '.apptuner-pids.json');\n\n try {\n const pidsData = await fs.readFile(pidFile, 'utf-8');\n const pids = JSON.parse(pidsData);\n\n if (pids.metro) {\n try {\n process.kill(-pids.metro, 'SIGTERM');\n console.log(chalk.gray('\u2713 Metro server stopped'));\n } catch (error) {\n console.log(chalk.gray('\u2713 Metro server already stopped'));\n }\n }\n\n if (pids.watcher) {\n try {\n process.kill(-pids.watcher, 'SIGTERM');\n console.log(chalk.gray('\u2713 File watcher stopped'));\n } catch (error) {\n console.log(chalk.gray('\u2713 File watcher already stopped'));\n }\n }\n\n await fs.unlink(pidFile);\n\n console.log(chalk.green('\\n\u2705 All services stopped\\n'));\n } catch (error) {\n console.log(chalk.gray('No running AppTuner services found.\\n'));\n }\n}\n", "import fs from 'fs/promises';\nimport path from 'path';\nimport chalk from 'chalk';\n\nexport async function statusCommand() {\n console.log(chalk.blue.bold('\\n\uD83D\uDCCA AppTuner Status\\n'));\n\n const pidFile = path.join(process.cwd(), '.apptuner-pids.json');\n\n try {\n const pidsData = await fs.readFile(pidFile, 'utf-8');\n const pids = JSON.parse(pidsData);\n\n console.log(chalk.white('Services:'));\n\n // Check if processes are actually running\n let metroRunning = false;\n let watcherRunning = false;\n\n if (pids.metro) {\n try {\n process.kill(pids.metro, 0); // Check if process exists\n metroRunning = true;\n } catch {\n metroRunning = false;\n }\n }\n\n if (pids.watcher) {\n try {\n process.kill(pids.watcher, 0);\n watcherRunning = true;\n } catch {\n watcherRunning = false;\n }\n }\n\n console.log(\n ` ${metroRunning ? chalk.green('\u25CF') : chalk.red('\u25CF')} Metro bundler ${\n metroRunning ? chalk.gray('(port 3031)') : chalk.gray('(stopped)')\n }`\n );\n\n console.log(\n ` ${watcherRunning ? chalk.green('\u25CF') : chalk.red('\u25CF')} File watcher ${\n watcherRunning ? chalk.gray('(port 3030)') : chalk.gray('(stopped)')\n }`\n );\n\n if (pids.sessionId) {\n console.log(chalk.white(`\\nSession: ${chalk.cyan(pids.sessionId)}`));\n }\n\n if (metroRunning && watcherRunning) {\n console.log(chalk.green('\\n\u2705 AppTuner is running\\n'));\n } else {\n console.log(chalk.yellow('\\n\u26A0\uFE0F Some services are not running\\n'));\n }\n } catch (error) {\n console.log(chalk.gray(' No running services\\n'));\n console.log(chalk.gray('Run `apptuner start` to begin.\\n'));\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["fs", "path", "chalk", "fs", "path", "chalk"]
7
+ }