tapflow 0.1.0-alpha.1 → 0.1.0-alpha.2

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.
Files changed (66) hide show
  1. package/README.md +5 -11
  2. package/dist/commands/agent-start.d.ts +7 -0
  3. package/dist/commands/agent-start.d.ts.map +1 -0
  4. package/dist/commands/agent-start.js +78 -0
  5. package/dist/commands/agent-start.js.map +1 -0
  6. package/dist/commands/boot.d.ts +2 -0
  7. package/dist/commands/boot.d.ts.map +1 -0
  8. package/dist/commands/boot.js +47 -0
  9. package/dist/commands/boot.js.map +1 -0
  10. package/dist/commands/devices.d.ts +2 -0
  11. package/dist/commands/devices.d.ts.map +1 -0
  12. package/dist/commands/devices.js +85 -0
  13. package/dist/commands/devices.js.map +1 -0
  14. package/dist/commands/doctor.d.ts +2 -0
  15. package/dist/commands/doctor.d.ts.map +1 -0
  16. package/dist/commands/doctor.js +41 -0
  17. package/dist/commands/doctor.js.map +1 -0
  18. package/dist/commands/init.d.ts +5 -0
  19. package/dist/commands/init.d.ts.map +1 -0
  20. package/dist/commands/init.js +64 -0
  21. package/dist/commands/init.js.map +1 -0
  22. package/dist/commands/logs.d.ts +5 -0
  23. package/dist/commands/logs.d.ts.map +1 -0
  24. package/dist/commands/logs.js +22 -0
  25. package/dist/commands/logs.js.map +1 -0
  26. package/dist/commands/relay-start.d.ts +5 -0
  27. package/dist/commands/relay-start.d.ts.map +1 -0
  28. package/dist/commands/relay-start.js +24 -0
  29. package/dist/commands/relay-start.js.map +1 -0
  30. package/dist/commands/reset.d.ts +2 -0
  31. package/dist/commands/reset.d.ts.map +1 -0
  32. package/dist/commands/reset.js +49 -0
  33. package/dist/commands/reset.js.map +1 -0
  34. package/dist/commands/start.d.ts +6 -0
  35. package/dist/commands/start.d.ts.map +1 -0
  36. package/dist/commands/start.js +77 -0
  37. package/dist/commands/start.js.map +1 -0
  38. package/dist/commands/status.d.ts +4 -0
  39. package/dist/commands/status.d.ts.map +1 -0
  40. package/dist/commands/status.js +56 -0
  41. package/dist/commands/status.js.map +1 -0
  42. package/dist/index.d.ts +2 -0
  43. package/dist/index.d.ts.map +1 -0
  44. package/dist/index.js +56 -0
  45. package/dist/index.js.map +1 -0
  46. package/dist/lib/doctor.d.ts +12 -0
  47. package/dist/lib/doctor.d.ts.map +1 -0
  48. package/dist/lib/doctor.js +118 -0
  49. package/dist/lib/doctor.js.map +1 -0
  50. package/dist/lib/ios-boot.d.ts +3 -0
  51. package/dist/lib/ios-boot.d.ts.map +1 -0
  52. package/dist/lib/ios-boot.js +44 -0
  53. package/dist/lib/ios-boot.js.map +1 -0
  54. package/dist/lib/platform.d.ts +2 -0
  55. package/dist/lib/platform.d.ts.map +1 -0
  56. package/dist/lib/platform.js +10 -0
  57. package/dist/lib/platform.js.map +1 -0
  58. package/dist/lib/print.d.ts +13 -0
  59. package/dist/lib/print.d.ts.map +1 -0
  60. package/dist/lib/print.js +55 -0
  61. package/dist/lib/print.js.map +1 -0
  62. package/dist/lib/tapflow-dir.d.ts +3 -0
  63. package/dist/lib/tapflow-dir.d.ts.map +1 -0
  64. package/dist/lib/tapflow-dir.js +8 -0
  65. package/dist/lib/tapflow-dir.js.map +1 -0
  66. package/package.json +7 -5
@@ -0,0 +1,77 @@
1
+ import { RelayServer } from '@tapflowio/relay';
2
+ import { AndroidAgent } from '@tapflowio/android-agent';
3
+ import { banner, createSpinner, step } from '../lib/print.js';
4
+ import { hasAdb } from '../lib/platform.js';
5
+ import { resolveAndBootIOSDevice } from '../lib/ios-boot.js';
6
+ const RELAY_PORT = 4000;
7
+ export async function cmdStart(opts) {
8
+ const isMac = process.platform === 'darwin';
9
+ const relayUrl = `ws://localhost:${RELAY_PORT}`;
10
+ const explicit = opts.platform;
11
+ const runIOS = explicit === 'ios' || explicit === 'all' || (!explicit && isMac);
12
+ const runAndroid = explicit === 'android' || explicit === 'all' || (!explicit && hasAdb());
13
+ // ── 1. Relay (always local) ───────────────────────────────────────────────
14
+ const server = new RelayServer({ port: RELAY_PORT });
15
+ await server.start();
16
+ step(`Relay started on ws://localhost:${RELAY_PORT}`);
17
+ // ── 2. Agent availability check ───────────────────────────────────────────
18
+ if (!runIOS && !runAndroid) {
19
+ banner('success', 'TAPFLOW RELAY READY', [
20
+ `Relay : http://localhost:${RELAY_PORT}`,
21
+ 'No agent environment detected — running relay only.',
22
+ `Connect a Mac agent: tapflow agent start --relay ws://<this-ip>:${RELAY_PORT}`,
23
+ 'Press Ctrl+C to stop.',
24
+ ]);
25
+ process.on('SIGINT', () => process.exit(0));
26
+ return;
27
+ }
28
+ const agents = [];
29
+ // ── 3. iOS Agent ──────────────────────────────────────────────────────────
30
+ if (runIOS) {
31
+ const iosAgent = await resolveAndBootIOSDevice(opts.device);
32
+ const iosSpinner = createSpinner('Connecting iOS agent…');
33
+ iosSpinner.start();
34
+ try {
35
+ await iosAgent.connect(relayUrl);
36
+ iosSpinner.stop(true);
37
+ agents.push(iosAgent);
38
+ }
39
+ catch (e) {
40
+ iosSpinner.stop(false);
41
+ banner('error', 'IOS CONNECTION FAILED', [e.message]);
42
+ process.exit(1);
43
+ }
44
+ }
45
+ // ── 4. Android Agent ──────────────────────────────────────────────────────
46
+ if (runAndroid) {
47
+ const androidAgent = new AndroidAgent({ deviceFilter: opts.device });
48
+ const androidSpinner = createSpinner('Connecting Android agent…');
49
+ androidSpinner.start();
50
+ try {
51
+ await androidAgent.connect(relayUrl);
52
+ androidSpinner.stop(true);
53
+ agents.push(androidAgent);
54
+ }
55
+ catch (e) {
56
+ androidSpinner.stop(false);
57
+ // Android 연결 실패 시 iOS가 이미 연결됐으면 경고만, 아니면 종료
58
+ if (runIOS && agents.length > 0) {
59
+ console.log(` ⚠ Android: ${e.message}`);
60
+ }
61
+ else {
62
+ banner('error', 'ANDROID CONNECTION FAILED', [e.message]);
63
+ process.exit(1);
64
+ }
65
+ }
66
+ }
67
+ banner('success', 'TAPFLOW READY', [
68
+ `Relay : http://localhost:${RELAY_PORT}`,
69
+ `Open http://localhost:${RELAY_PORT} in your browser.`,
70
+ 'Press Ctrl+C to stop.',
71
+ ]);
72
+ process.on('SIGINT', () => {
73
+ agents.forEach((a) => a.disconnect());
74
+ process.exit(0);
75
+ });
76
+ }
77
+ //# sourceMappingURL=start.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"start.js","sourceRoot":"","sources":["../../src/commands/start.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export declare function cmdStatus(opts: {
2
+ relay?: string;
3
+ }): Promise<void>;
4
+ //# sourceMappingURL=status.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAGA,wBAAsB,SAAS,CAAC,IAAI,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA2DvE"}
@@ -0,0 +1,56 @@
1
+ import { WebSocket } from 'ws';
2
+ import { DIM, BOLD, GREEN, YELLOW, RED, R } from '../lib/print.js';
3
+ export async function cmdStatus(opts) {
4
+ const relayUrl = (opts.relay ?? 'ws://localhost:4000').replace(/^http/, 'ws');
5
+ console.log(`\n Connecting to ${relayUrl}…\n`);
6
+ console.log(` ${DIM}● agent ◉ in use ○ idle${R}\n`);
7
+ await new Promise((resolve, reject) => {
8
+ const ws = new WebSocket(relayUrl);
9
+ const timeout = setTimeout(() => {
10
+ ws.terminate();
11
+ reject(new Error(`Could not connect to relay at ${relayUrl} (5s timeout)`));
12
+ }, 5000);
13
+ ws.on('open', () => ws.send(JSON.stringify({ type: 'agents:list' })));
14
+ ws.on('message', (data) => {
15
+ try {
16
+ const msg = JSON.parse(data.toString());
17
+ if (msg.type !== 'agents:listed')
18
+ return;
19
+ clearTimeout(timeout);
20
+ ws.close();
21
+ const sessions = msg.sessions ?? [];
22
+ if (sessions.length === 0) {
23
+ console.log(` ${DIM}No agents connected.${R}\n`);
24
+ }
25
+ else {
26
+ let totalDevices = 0;
27
+ let activeSessions = 0;
28
+ for (const session of sessions) {
29
+ console.log(` ${BOLD}${GREEN}●${R} ${BOLD}${session.agentName}${R}`);
30
+ for (const device of session.devices ?? []) {
31
+ const occupied = !!device.joinedBy;
32
+ const dot = occupied ? `${YELLOW}◉${R}` : `${DIM}○${R}`;
33
+ const who = occupied ? ` ${DIM}← ${device.joinedBy}${R}` : '';
34
+ console.log(` ${dot} ${device.name}${who}`);
35
+ totalDevices++;
36
+ if (occupied)
37
+ activeSessions++;
38
+ }
39
+ console.log();
40
+ }
41
+ console.log(` ${DIM}${sessions.length} agent(s) · ${totalDevices} device(s) · ${activeSessions} active session(s)${R}\n`);
42
+ }
43
+ resolve();
44
+ }
45
+ catch { /* ignore non-agents:listed messages */ }
46
+ });
47
+ ws.on('error', (err) => {
48
+ clearTimeout(timeout);
49
+ reject(err);
50
+ });
51
+ }).catch((err) => {
52
+ console.error(`\n ${RED}✗${R} ${err.message}\n`);
53
+ process.exit(1);
54
+ });
55
+ }
56
+ //# sourceMappingURL=status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,56 @@
1
+ import { cac } from 'cac';
2
+ import { cmdInit } from './commands/init.js';
3
+ import { cmdDoctor } from './commands/doctor.js';
4
+ import { cmdDevices } from './commands/devices.js';
5
+ import { cmdBoot } from './commands/boot.js';
6
+ import { cmdStart } from './commands/start.js';
7
+ import { cmdRelayStart } from './commands/relay-start.js';
8
+ import { cmdAgentStart } from './commands/agent-start.js';
9
+ import { cmdReset } from './commands/reset.js';
10
+ import { cmdStatus } from './commands/status.js';
11
+ import { cmdLogs } from './commands/logs.js';
12
+ const cli = cac('tapflow');
13
+ cli
14
+ .command('init', 'Create the first admin account on the relay')
15
+ .option('--relay <url>', 'Relay URL (default: http://localhost:4000)')
16
+ .action((opts) => cmdInit(opts));
17
+ cli
18
+ .command('doctor', 'Check system prerequisites')
19
+ .action(() => cmdDoctor());
20
+ cli
21
+ .command('devices', 'List available iOS simulators and Android AVDs')
22
+ .action(() => cmdDevices());
23
+ cli
24
+ .command('boot <name>', 'Boot a simulator by name or UDID')
25
+ .action((name) => cmdBoot(name));
26
+ cli
27
+ .command('start', 'Start relay and available agents locally (local dev shortcut)')
28
+ .option('--platform <platform>', 'Platform to start: ios | android | all (default: auto-detect)')
29
+ .option('--device <name>', 'iOS Simulator name or UDID to use')
30
+ .action((opts) => cmdStart(opts));
31
+ cli
32
+ .command('relay start', 'Start the relay server only (no agents)')
33
+ .option('--port <n>', 'Port to listen on (default: 4000)')
34
+ .action((opts) => cmdRelayStart(opts));
35
+ cli
36
+ .command('agent start', 'Start agents and connect to a relay (no local relay spawned)')
37
+ .option('--relay <url>', 'Relay WebSocket URL (default: ws://localhost:4000)')
38
+ .option('--platform <platform>', 'Platform to start: ios | android | all (default: auto-detect)')
39
+ .option('--device <name>', 'iOS Simulator name or UDID to use')
40
+ .action((opts) => cmdAgentStart(opts));
41
+ cli
42
+ .command('reset', 'Shut down all simulators')
43
+ .action(() => cmdReset());
44
+ cli
45
+ .command('status', 'Show connected agents, devices, and active sessions')
46
+ .option('--relay <url>', 'Relay URL (default: ws://localhost:4000)')
47
+ .action((opts) => cmdStatus(opts));
48
+ cli
49
+ .command('logs', 'Show recent relay log entries')
50
+ .option('--relay <url>', 'Relay URL (default: http://localhost:4000)')
51
+ .option('--lines <n>', 'Number of lines to show (default: 100)', { default: 100 })
52
+ .action((opts) => cmdLogs(opts));
53
+ cli.help();
54
+ cli.version('0.1.0');
55
+ cli.parse();
56
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ export interface DoctorCheck {
2
+ label: string;
3
+ ok: boolean;
4
+ detail?: string;
5
+ }
6
+ export interface DoctorResult {
7
+ common: DoctorCheck[];
8
+ ios: DoctorCheck[] | null;
9
+ android: DoctorCheck[] | null;
10
+ }
11
+ export declare function runDoctorChecks(): Promise<DoctorResult>;
12
+ //# sourceMappingURL=doctor.d.ts.map
@@ -0,0 +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,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"}
@@ -0,0 +1,118 @@
1
+ import { execSync } from 'node:child_process';
2
+ export async function runDoctorChecks() {
3
+ const isMac = process.platform === 'darwin';
4
+ const adbPath = resolveAdb();
5
+ return {
6
+ common: [checkNodeVersion()],
7
+ ios: isMac ? [checkXcode(), checkSimctl(), checkBootedSimulator()] : null,
8
+ android: adbPath !== null ? [checkAdb(adbPath), checkBootedAvd()] : null,
9
+ };
10
+ }
11
+ function checkXcode() {
12
+ try {
13
+ const out = execSync('xcodebuild -version', { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] });
14
+ const version = out.split('\n')[0]?.replace('Xcode ', '') ?? '';
15
+ return { label: `Xcode ${version}`, ok: true };
16
+ }
17
+ catch {
18
+ return {
19
+ label: 'Xcode',
20
+ ok: false,
21
+ detail: 'Install Xcode from https://developer.apple.com/xcode/ or the Mac App Store.',
22
+ };
23
+ }
24
+ }
25
+ function checkSimctl() {
26
+ try {
27
+ execSync('xcrun simctl list --json', { stdio: 'pipe' });
28
+ return { label: 'xcrun simctl', ok: true };
29
+ }
30
+ catch {
31
+ return {
32
+ label: 'xcrun simctl',
33
+ ok: false,
34
+ detail: 'Run: xcode-select --install',
35
+ };
36
+ }
37
+ }
38
+ function checkBootedSimulator() {
39
+ try {
40
+ const raw = execSync('xcrun simctl list devices --json', { encoding: 'utf8', stdio: 'pipe' });
41
+ const data = JSON.parse(raw);
42
+ const allDevices = Object.values(data.devices).flat();
43
+ const booted = allDevices.find((d) => d.state === 'Booted');
44
+ if (booted) {
45
+ return { label: `Simulator booted: ${booted.name}`, ok: true };
46
+ }
47
+ const available = allDevices.find((d) => d.state === 'Shutdown');
48
+ 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 };
52
+ }
53
+ catch {
54
+ return { label: 'Simulator', ok: false, detail: 'Could not query simulators. Is Xcode installed?' };
55
+ }
56
+ }
57
+ function checkNodeVersion() {
58
+ const version = process.version;
59
+ const [, major] = version.match(/^v(\d+)/) ?? [];
60
+ const ok = Number(major) >= 20;
61
+ return {
62
+ label: `Node ${version}`,
63
+ ok,
64
+ detail: ok ? undefined : 'Node ≥ 20 required. Install from https://nodejs.org/',
65
+ };
66
+ }
67
+ function resolveAdb() {
68
+ try {
69
+ const path = execSync('which adb', { encoding: 'utf8', stdio: 'pipe' }).trim();
70
+ return path || null;
71
+ }
72
+ catch {
73
+ return null;
74
+ }
75
+ }
76
+ function checkAdb(path) {
77
+ return { label: `adb found: ${path}`, ok: true };
78
+ }
79
+ function checkBootedAvd() {
80
+ try {
81
+ const out = execSync('adb devices', { encoding: 'utf8', stdio: 'pipe' });
82
+ const lines = out.trim().split('\n').slice(1).filter(Boolean);
83
+ const emulator = lines.find((l) => l.startsWith('emulator-'));
84
+ if (!emulator) {
85
+ const hint = listAvdHint();
86
+ return {
87
+ label: 'AVD',
88
+ ok: false,
89
+ detail: hint
90
+ ? `No running emulator. Run: emulator @${hint}`
91
+ : 'No running emulator. Start an AVD from Android Studio > Device Manager.',
92
+ };
93
+ }
94
+ const serial = emulator.split('\t')[0]?.trim() ?? '';
95
+ try {
96
+ const avdName = execSync(`adb -s ${serial} emu avd name`, { encoding: 'utf8', stdio: 'pipe' })
97
+ .split('\n')[0]
98
+ ?.trim() ?? serial;
99
+ return { label: `AVD: ${avdName}`, ok: true };
100
+ }
101
+ catch {
102
+ return { label: `AVD: ${serial}`, ok: true };
103
+ }
104
+ }
105
+ catch {
106
+ return { label: 'AVD', ok: false, detail: 'Could not query running emulators.' };
107
+ }
108
+ }
109
+ function listAvdHint() {
110
+ try {
111
+ const out = execSync('emulator -list-avds', { encoding: 'utf8', stdio: 'pipe' }).trim();
112
+ return out.split('\n')[0]?.trim() || null;
113
+ }
114
+ catch {
115
+ return null;
116
+ }
117
+ }
118
+ //# sourceMappingURL=doctor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/lib/doctor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { IOSAgent } from '@tapflowio/ios-agent';
2
+ export declare function resolveAndBootIOSDevice(deviceFilter?: string): Promise<IOSAgent>;
3
+ //# sourceMappingURL=ios-boot.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ios-boot.d.ts","sourceRoot":"","sources":["../../src/lib/ios-boot.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AAG/C,wBAAsB,uBAAuB,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAuCtF"}
@@ -0,0 +1,44 @@
1
+ import { execSync } from 'node:child_process';
2
+ import { IOSAgent } from '@tapflowio/ios-agent';
3
+ import { banner, createSpinner, step } from './print.js';
4
+ export async function resolveAndBootIOSDevice(deviceFilter) {
5
+ const agent = new IOSAgent();
6
+ const devices = await agent.listDevices();
7
+ if (deviceFilter) {
8
+ const match = devices.find((d) => d.name === deviceFilter || d.id === deviceFilter);
9
+ if (!match) {
10
+ banner('error', 'DEVICE NOT FOUND', [
11
+ `"${deviceFilter}" does not match any simulator.`,
12
+ 'Run `tapflow devices` to see available simulators.',
13
+ ]);
14
+ process.exit(1);
15
+ }
16
+ if (match.status !== 'booted') {
17
+ const spinner = createSpinner(`Booting ${match.name}…`);
18
+ spinner.start();
19
+ execSync(`xcrun simctl boot ${match.id}`, { stdio: 'pipe' });
20
+ spinner.stop(true);
21
+ }
22
+ step(`iOS Simulator: ${match.name}`);
23
+ }
24
+ else {
25
+ const booted = devices.find((d) => d.status === 'booted');
26
+ if (booted) {
27
+ step(`iOS Simulator: ${booted.name}`);
28
+ }
29
+ else {
30
+ const first = devices[0];
31
+ if (!first) {
32
+ banner('error', 'NO SIMULATOR FOUND', ['Create one in Xcode → Window → Devices and Simulators.']);
33
+ process.exit(1);
34
+ }
35
+ const spinner = createSpinner(`Booting ${first.name}…`);
36
+ spinner.start();
37
+ execSync(`xcrun simctl boot ${first.id}`, { stdio: 'pipe' });
38
+ spinner.stop(true);
39
+ step(`iOS Simulator: ${first.name}`);
40
+ }
41
+ }
42
+ return agent;
43
+ }
44
+ //# sourceMappingURL=ios-boot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ios-boot.js","sourceRoot":"","sources":["../../src/lib/ios-boot.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AAC/C,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AAExD,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAC,YAAqB;IACjE,MAAM,KAAK,GAAG,IAAI,QAAQ,EAAE,CAAA;IAC5B,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,WAAW,EAAE,CAAA;IAEzC,IAAI,YAAY,EAAE,CAAC;QACjB,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,YAAY,IAAI,CAAC,CAAC,EAAE,KAAK,YAAY,CAAC,CAAA;QACnF,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,CAAC,OAAO,EAAE,kBAAkB,EAAE;gBAClC,IAAI,YAAY,iCAAiC;gBACjD,oDAAoD;aACrD,CAAC,CAAA;YACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QACjB,CAAC;QACD,IAAI,KAAK,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;YAC9B,MAAM,OAAO,GAAG,aAAa,CAAC,WAAW,KAAK,CAAC,IAAI,GAAG,CAAC,CAAA;YACvD,OAAO,CAAC,KAAK,EAAE,CAAA;YACf,QAAQ,CAAC,qBAAqB,KAAK,CAAC,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAA;YAC5D,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACpB,CAAC;QACD,IAAI,CAAC,kBAAkB,KAAK,CAAC,IAAI,EAAE,CAAC,CAAA;IACtC,CAAC;SAAM,CAAC;QACN,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAA;QACzD,IAAI,MAAM,EAAE,CAAC;YACX,IAAI,CAAC,kBAAkB,MAAM,CAAC,IAAI,EAAE,CAAC,CAAA;QACvC,CAAC;aAAM,CAAC;YACN,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;YACxB,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,MAAM,CAAC,OAAO,EAAE,oBAAoB,EAAE,CAAC,wDAAwD,CAAC,CAAC,CAAA;gBACjG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;YACjB,CAAC;YACD,MAAM,OAAO,GAAG,aAAa,CAAC,WAAW,KAAK,CAAC,IAAI,GAAG,CAAC,CAAA;YACvD,OAAO,CAAC,KAAK,EAAE,CAAA;YACf,QAAQ,CAAC,qBAAqB,KAAK,CAAC,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAA;YAC5D,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YAClB,IAAI,CAAC,kBAAkB,KAAK,CAAC,IAAI,EAAE,CAAC,CAAA;QACtC,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAA;AACd,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function hasAdb(): boolean;
2
+ //# sourceMappingURL=platform.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"platform.d.ts","sourceRoot":"","sources":["../../src/lib/platform.ts"],"names":[],"mappings":"AAEA,wBAAgB,MAAM,IAAI,OAAO,CAMhC"}
@@ -0,0 +1,10 @@
1
+ import { execSync } from 'node:child_process';
2
+ export function hasAdb() {
3
+ try {
4
+ return execSync('which adb', { encoding: 'utf8', stdio: 'pipe' }).trim().length > 0;
5
+ }
6
+ catch {
7
+ return false;
8
+ }
9
+ }
10
+ //# sourceMappingURL=platform.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"platform.js","sourceRoot":"","sources":["../../src/lib/platform.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAE7C,MAAM,UAAU,MAAM;IACpB,IAAI,CAAC;QACH,OAAO,QAAQ,CAAC,WAAW,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAA;IACrF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAA;IACd,CAAC;AACH,CAAC"}
@@ -0,0 +1,13 @@
1
+ export declare const R = "\u001B[0m";
2
+ export declare const BOLD = "\u001B[1m";
3
+ export declare const DIM = "\u001B[2m";
4
+ export declare const GREEN = "\u001B[32m";
5
+ export declare const RED = "\u001B[31m";
6
+ export declare const YELLOW = "\u001B[33m";
7
+ export declare function banner(type: 'success' | 'error', title: string, lines?: string[]): void;
8
+ export declare function createSpinner(msg: string): {
9
+ start(): void;
10
+ stop(ok: boolean): void;
11
+ };
12
+ export declare function step(msg: string): void;
13
+ //# sourceMappingURL=print.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"print.d.ts","sourceRoot":"","sources":["../../src/lib/print.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,CAAC,cAAY,CAAA;AAC1B,eAAO,MAAM,IAAI,cAAY,CAAA;AAC7B,eAAO,MAAM,GAAG,cAAY,CAAA;AAC5B,eAAO,MAAM,KAAK,eAAa,CAAA;AAC/B,eAAO,MAAM,GAAG,eAAa,CAAA;AAC7B,eAAO,MAAM,MAAM,eAAa,CAAA;AAahC,wBAAgB,MAAM,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,GAAE,MAAM,EAAO,GAAG,IAAI,CAkB3F;AAID,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM;;aAY5B,OAAO;EAMnB;AAED,wBAAgB,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAEtC"}
@@ -0,0 +1,55 @@
1
+ export const R = '\x1b[0m';
2
+ export const BOLD = '\x1b[1m';
3
+ export const DIM = '\x1b[2m';
4
+ export const GREEN = '\x1b[32m';
5
+ export const RED = '\x1b[31m';
6
+ export const YELLOW = '\x1b[33m';
7
+ const MAX_WIDTH = 72;
8
+ function wrap(line, maxWidth) {
9
+ if (line.length <= maxWidth)
10
+ return [line];
11
+ const chunks = [];
12
+ for (let i = 0; i < line.length; i += maxWidth) {
13
+ chunks.push(line.slice(i, i + maxWidth));
14
+ }
15
+ return chunks;
16
+ }
17
+ export function banner(type, title, lines = []) {
18
+ const color = type === 'success' ? GREEN : RED;
19
+ const icon = type === 'success' ? '✓' : '✗';
20
+ const wrappedLines = lines.flatMap((l) => wrap(l, MAX_WIDTH));
21
+ const contentWidth = Math.min(MAX_WIDTH, Math.max(title.length + 5, ...wrappedLines.map((l) => l.length + 2), 40));
22
+ const bar = '─'.repeat(contentWidth);
23
+ console.log();
24
+ console.log(`${color}${BOLD} ┌${bar}┐${R}`);
25
+ console.log(`${color}${BOLD} │ ${icon} ${title.padEnd(contentWidth - 5)}│${R}`);
26
+ console.log(`${color}${BOLD} └${bar}┘${R}`);
27
+ for (const line of wrappedLines) {
28
+ console.log(`${DIM} ${line}${R}`);
29
+ }
30
+ console.log();
31
+ }
32
+ const SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
33
+ export function createSpinner(msg) {
34
+ let frame = 0;
35
+ let id = null;
36
+ return {
37
+ start() {
38
+ process.stdout.write(`\n ${SPINNER_FRAMES[0]} ${msg}`);
39
+ id = setInterval(() => {
40
+ frame = (frame + 1) % SPINNER_FRAMES.length;
41
+ process.stdout.write(`\r ${SPINNER_FRAMES[frame]} ${msg}`);
42
+ }, 80);
43
+ },
44
+ stop(ok) {
45
+ if (id)
46
+ clearInterval(id);
47
+ const icon = ok ? `${GREEN}✓${R}` : `${RED}✗${R}`;
48
+ process.stdout.write(`\r ${icon} ${msg}\n`);
49
+ },
50
+ };
51
+ }
52
+ export function step(msg) {
53
+ console.log(` ${DIM}→${R} ${msg}`);
54
+ }
55
+ //# sourceMappingURL=print.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"print.js","sourceRoot":"","sources":["../../src/lib/print.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export declare const TAPFLOW_DIR: string;
2
+ export declare function ensureTapflowDir(): void;
3
+ //# sourceMappingURL=tapflow-dir.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tapflow-dir.d.ts","sourceRoot":"","sources":["../../src/lib/tapflow-dir.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,WAAW,QAA8B,CAAA;AAEtD,wBAAgB,gBAAgB,IAAI,IAAI,CAEvC"}
@@ -0,0 +1,8 @@
1
+ import { mkdirSync } from 'node:fs';
2
+ import { homedir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ export const TAPFLOW_DIR = join(homedir(), '.tapflow');
5
+ export function ensureTapflowDir() {
6
+ mkdirSync(TAPFLOW_DIR, { recursive: true });
7
+ }
8
+ //# sourceMappingURL=tapflow-dir.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tapflow-dir.js","sourceRoot":"","sources":["../../src/lib/tapflow-dir.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AACnC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAEhC,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,UAAU,CAAC,CAAA;AAEtD,MAAM,UAAU,gBAAgB;IAC9B,SAAS,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;AAC7C,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tapflow",
3
- "version": "0.1.0-alpha.1",
3
+ "version": "0.1.0-alpha.2",
4
4
  "description": "Self-hosted iOS/Android simulator streaming for QA teams",
5
5
  "keywords": [
6
6
  "ios",
@@ -45,10 +45,11 @@
45
45
  "dependencies": {
46
46
  "cac": "^6.7.14",
47
47
  "ws": "^8.0.0",
48
- "@tapflow/agent-core": "0.1.0-alpha.1",
49
- "@tapflow/ios-agent": "0.1.0-alpha.1",
50
- "@tapflow/android-agent": "0.1.0-alpha.1",
51
- "@tapflow/relay": "0.1.0-alpha.1"
48
+ "zod": "^4.4.3",
49
+ "@tapflowio/agent-core": "0.1.0-alpha.2",
50
+ "@tapflowio/android-agent": "0.1.0-alpha.2",
51
+ "@tapflowio/ios-agent": "0.1.0-alpha.2",
52
+ "@tapflowio/relay": "0.1.0-alpha.2"
52
53
  },
53
54
  "devDependencies": {
54
55
  "@types/node": "^20.0.0",
@@ -59,6 +60,7 @@
59
60
  },
60
61
  "scripts": {
61
62
  "build": "tsc",
63
+ "typecheck": "tsc --noEmit",
62
64
  "dev": "tsx src/index.ts",
63
65
  "test": "vitest run",
64
66
  "lint": "eslint src"