@desplega.ai/qa-use 2.1.5 → 2.2.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 (145) hide show
  1. package/dist/lib/api/browser-types.d.ts +175 -0
  2. package/dist/lib/api/browser-types.d.ts.map +1 -0
  3. package/dist/lib/api/browser-types.js +5 -0
  4. package/dist/lib/api/browser-types.js.map +1 -0
  5. package/dist/lib/api/browser.d.ts +66 -0
  6. package/dist/lib/api/browser.d.ts.map +1 -0
  7. package/dist/lib/api/browser.js +223 -0
  8. package/dist/lib/api/browser.js.map +1 -0
  9. package/dist/package.json +2 -1
  10. package/dist/src/cli/commands/browser/back.d.ts +6 -0
  11. package/dist/src/cli/commands/browser/back.d.ts.map +1 -0
  12. package/dist/src/cli/commands/browser/back.js +42 -0
  13. package/dist/src/cli/commands/browser/back.js.map +1 -0
  14. package/dist/src/cli/commands/browser/check.d.ts +6 -0
  15. package/dist/src/cli/commands/browser/check.d.ts.map +1 -0
  16. package/dist/src/cli/commands/browser/check.js +62 -0
  17. package/dist/src/cli/commands/browser/check.js.map +1 -0
  18. package/dist/src/cli/commands/browser/click.d.ts +6 -0
  19. package/dist/src/cli/commands/browser/click.d.ts.map +1 -0
  20. package/dist/src/cli/commands/browser/click.js +63 -0
  21. package/dist/src/cli/commands/browser/click.js.map +1 -0
  22. package/dist/src/cli/commands/browser/close.d.ts +6 -0
  23. package/dist/src/cli/commands/browser/close.d.ts.map +1 -0
  24. package/dist/src/cli/commands/browser/close.js +44 -0
  25. package/dist/src/cli/commands/browser/close.js.map +1 -0
  26. package/dist/src/cli/commands/browser/create.d.ts +6 -0
  27. package/dist/src/cli/commands/browser/create.d.ts.map +1 -0
  28. package/dist/src/cli/commands/browser/create.js +281 -0
  29. package/dist/src/cli/commands/browser/create.js.map +1 -0
  30. package/dist/src/cli/commands/browser/fill.d.ts +6 -0
  31. package/dist/src/cli/commands/browser/fill.d.ts.map +1 -0
  32. package/dist/src/cli/commands/browser/fill.js +83 -0
  33. package/dist/src/cli/commands/browser/fill.js.map +1 -0
  34. package/dist/src/cli/commands/browser/forward.d.ts +6 -0
  35. package/dist/src/cli/commands/browser/forward.d.ts.map +1 -0
  36. package/dist/src/cli/commands/browser/forward.js +42 -0
  37. package/dist/src/cli/commands/browser/forward.js.map +1 -0
  38. package/dist/src/cli/commands/browser/get-blocks.d.ts +6 -0
  39. package/dist/src/cli/commands/browser/get-blocks.d.ts.map +1 -0
  40. package/dist/src/cli/commands/browser/get-blocks.js +35 -0
  41. package/dist/src/cli/commands/browser/get-blocks.js.map +1 -0
  42. package/dist/src/cli/commands/browser/goto.d.ts +6 -0
  43. package/dist/src/cli/commands/browser/goto.d.ts.map +1 -0
  44. package/dist/src/cli/commands/browser/goto.js +53 -0
  45. package/dist/src/cli/commands/browser/goto.js.map +1 -0
  46. package/dist/src/cli/commands/browser/hover.d.ts +6 -0
  47. package/dist/src/cli/commands/browser/hover.d.ts.map +1 -0
  48. package/dist/src/cli/commands/browser/hover.js +63 -0
  49. package/dist/src/cli/commands/browser/hover.js.map +1 -0
  50. package/dist/src/cli/commands/browser/index.d.ts +9 -0
  51. package/dist/src/cli/commands/browser/index.d.ts.map +1 -0
  52. package/dist/src/cli/commands/browser/index.js +71 -0
  53. package/dist/src/cli/commands/browser/index.js.map +1 -0
  54. package/dist/src/cli/commands/browser/list.d.ts +6 -0
  55. package/dist/src/cli/commands/browser/list.d.ts.map +1 -0
  56. package/dist/src/cli/commands/browser/list.js +85 -0
  57. package/dist/src/cli/commands/browser/list.js.map +1 -0
  58. package/dist/src/cli/commands/browser/press.d.ts +6 -0
  59. package/dist/src/cli/commands/browser/press.d.ts.map +1 -0
  60. package/dist/src/cli/commands/browser/press.js +67 -0
  61. package/dist/src/cli/commands/browser/press.js.map +1 -0
  62. package/dist/src/cli/commands/browser/reload.d.ts +6 -0
  63. package/dist/src/cli/commands/browser/reload.d.ts.map +1 -0
  64. package/dist/src/cli/commands/browser/reload.js +42 -0
  65. package/dist/src/cli/commands/browser/reload.js.map +1 -0
  66. package/dist/src/cli/commands/browser/run.d.ts +6 -0
  67. package/dist/src/cli/commands/browser/run.d.ts.map +1 -0
  68. package/dist/src/cli/commands/browser/run.js +618 -0
  69. package/dist/src/cli/commands/browser/run.js.map +1 -0
  70. package/dist/src/cli/commands/browser/screenshot.d.ts +6 -0
  71. package/dist/src/cli/commands/browser/screenshot.d.ts.map +1 -0
  72. package/dist/src/cli/commands/browser/screenshot.js +72 -0
  73. package/dist/src/cli/commands/browser/screenshot.js.map +1 -0
  74. package/dist/src/cli/commands/browser/scroll-into-view.d.ts +6 -0
  75. package/dist/src/cli/commands/browser/scroll-into-view.d.ts.map +1 -0
  76. package/dist/src/cli/commands/browser/scroll-into-view.js +64 -0
  77. package/dist/src/cli/commands/browser/scroll-into-view.js.map +1 -0
  78. package/dist/src/cli/commands/browser/scroll.d.ts +6 -0
  79. package/dist/src/cli/commands/browser/scroll.d.ts.map +1 -0
  80. package/dist/src/cli/commands/browser/scroll.js +63 -0
  81. package/dist/src/cli/commands/browser/scroll.js.map +1 -0
  82. package/dist/src/cli/commands/browser/select.d.ts +6 -0
  83. package/dist/src/cli/commands/browser/select.d.ts.map +1 -0
  84. package/dist/src/cli/commands/browser/select.js +83 -0
  85. package/dist/src/cli/commands/browser/select.js.map +1 -0
  86. package/dist/src/cli/commands/browser/snapshot.d.ts +6 -0
  87. package/dist/src/cli/commands/browser/snapshot.d.ts.map +1 -0
  88. package/dist/src/cli/commands/browser/snapshot.js +72 -0
  89. package/dist/src/cli/commands/browser/snapshot.js.map +1 -0
  90. package/dist/src/cli/commands/browser/status.d.ts +6 -0
  91. package/dist/src/cli/commands/browser/status.d.ts.map +1 -0
  92. package/dist/src/cli/commands/browser/status.js +91 -0
  93. package/dist/src/cli/commands/browser/status.js.map +1 -0
  94. package/dist/src/cli/commands/browser/stream.d.ts +6 -0
  95. package/dist/src/cli/commands/browser/stream.d.ts.map +1 -0
  96. package/dist/src/cli/commands/browser/stream.js +135 -0
  97. package/dist/src/cli/commands/browser/stream.js.map +1 -0
  98. package/dist/src/cli/commands/browser/tunnel.d.ts +13 -0
  99. package/dist/src/cli/commands/browser/tunnel.d.ts.map +1 -0
  100. package/dist/src/cli/commands/browser/tunnel.js +225 -0
  101. package/dist/src/cli/commands/browser/tunnel.js.map +1 -0
  102. package/dist/src/cli/commands/browser/type.d.ts +6 -0
  103. package/dist/src/cli/commands/browser/type.d.ts.map +1 -0
  104. package/dist/src/cli/commands/browser/type.js +61 -0
  105. package/dist/src/cli/commands/browser/type.js.map +1 -0
  106. package/dist/src/cli/commands/browser/uncheck.d.ts +6 -0
  107. package/dist/src/cli/commands/browser/uncheck.d.ts.map +1 -0
  108. package/dist/src/cli/commands/browser/uncheck.js +62 -0
  109. package/dist/src/cli/commands/browser/uncheck.js.map +1 -0
  110. package/dist/src/cli/commands/browser/url.d.ts +6 -0
  111. package/dist/src/cli/commands/browser/url.d.ts.map +1 -0
  112. package/dist/src/cli/commands/browser/url.js +40 -0
  113. package/dist/src/cli/commands/browser/url.js.map +1 -0
  114. package/dist/src/cli/commands/browser/wait-for-load.d.ts +6 -0
  115. package/dist/src/cli/commands/browser/wait-for-load.d.ts.map +1 -0
  116. package/dist/src/cli/commands/browser/wait-for-load.js +50 -0
  117. package/dist/src/cli/commands/browser/wait-for-load.js.map +1 -0
  118. package/dist/src/cli/commands/browser/wait-for-selector.d.ts +6 -0
  119. package/dist/src/cli/commands/browser/wait-for-selector.d.ts.map +1 -0
  120. package/dist/src/cli/commands/browser/wait-for-selector.js +52 -0
  121. package/dist/src/cli/commands/browser/wait-for-selector.js.map +1 -0
  122. package/dist/src/cli/commands/browser/wait.d.ts +6 -0
  123. package/dist/src/cli/commands/browser/wait.d.ts.map +1 -0
  124. package/dist/src/cli/commands/browser/wait.js +60 -0
  125. package/dist/src/cli/commands/browser/wait.js.map +1 -0
  126. package/dist/src/cli/commands/test/run.d.ts.map +1 -1
  127. package/dist/src/cli/commands/test/run.js +39 -8
  128. package/dist/src/cli/commands/test/run.js.map +1 -1
  129. package/dist/src/cli/index.js +2 -0
  130. package/dist/src/cli/index.js.map +1 -1
  131. package/dist/src/cli/lib/browser-sessions.d.ts +72 -0
  132. package/dist/src/cli/lib/browser-sessions.d.ts.map +1 -0
  133. package/dist/src/cli/lib/browser-sessions.js +184 -0
  134. package/dist/src/cli/lib/browser-sessions.js.map +1 -0
  135. package/dist/src/cli/lib/download.d.ts +2 -2
  136. package/dist/src/cli/lib/download.d.ts.map +1 -1
  137. package/dist/src/cli/lib/download.js +21 -5
  138. package/dist/src/cli/lib/download.js.map +1 -1
  139. package/dist/src/cli/lib/output.d.ts.map +1 -1
  140. package/dist/src/cli/lib/output.js +11 -4
  141. package/dist/src/cli/lib/output.js.map +1 -1
  142. package/lib/api/browser-types.ts +278 -0
  143. package/lib/api/browser.test.ts +378 -0
  144. package/lib/api/browser.ts +279 -0
  145. package/package.json +2 -1
@@ -0,0 +1,135 @@
1
+ /**
2
+ * qa-use browser stream - Real-time WebSocket event streaming
3
+ */
4
+ import { Command } from 'commander';
5
+ import WebSocket from 'ws';
6
+ import { BrowserApiClient } from '../../../../lib/api/browser.js';
7
+ import { resolveSessionId, touchSession } from '../../lib/browser-sessions.js';
8
+ import { loadConfig } from '../../lib/config.js';
9
+ import { error, info } from '../../lib/output.js';
10
+ // ANSI color codes
11
+ const colors = {
12
+ reset: '\x1b[0m',
13
+ green: '\x1b[32m',
14
+ red: '\x1b[31m',
15
+ yellow: '\x1b[33m',
16
+ blue: '\x1b[34m',
17
+ gray: '\x1b[90m',
18
+ cyan: '\x1b[36m',
19
+ };
20
+ export const streamCommand = new Command('stream')
21
+ .description('Stream real-time events from a browser session')
22
+ .option('-s, --session-id <id>', 'Session ID (auto-resolved if only one session)')
23
+ .action(async (options) => {
24
+ try {
25
+ // Load configuration
26
+ const config = await loadConfig();
27
+ if (!config.api_key) {
28
+ console.log(error('API key not configured. Run `qa-use setup` first.'));
29
+ process.exit(1);
30
+ }
31
+ // Create client and set API key
32
+ const client = new BrowserApiClient(config.api_url);
33
+ client.setApiKey(config.api_key);
34
+ // Resolve session ID
35
+ const resolved = await resolveSessionId({
36
+ explicitId: options.sessionId,
37
+ client,
38
+ });
39
+ console.log(info(`Connecting to session ${resolved.id}...`));
40
+ // Get WebSocket URL
41
+ const wsUrl = client.getStreamUrl(resolved.id);
42
+ // Create WebSocket connection
43
+ const ws = new WebSocket(wsUrl, {
44
+ headers: {
45
+ Authorization: `Bearer ${client.getApiKey()}`,
46
+ },
47
+ });
48
+ // Handle connection open
49
+ ws.on('open', () => {
50
+ console.log(info('Connected. Press Ctrl+C to disconnect.\n'));
51
+ touchSession(resolved.id);
52
+ });
53
+ // Handle messages
54
+ ws.on('message', (data) => {
55
+ try {
56
+ // Handle binary data (screenshots)
57
+ if (Buffer.isBuffer(data)) {
58
+ console.log(`${colors.gray}📷 Binary frame received (${data.length} bytes)${colors.reset}`);
59
+ return;
60
+ }
61
+ // Parse JSON message
62
+ const message = JSON.parse(data.toString());
63
+ formatEvent(message);
64
+ }
65
+ catch (err) {
66
+ console.log(`${colors.gray}Raw: ${data.toString()}${colors.reset}`);
67
+ }
68
+ });
69
+ // Handle errors
70
+ ws.on('error', (err) => {
71
+ console.log(error(`WebSocket error: ${err.message}`));
72
+ });
73
+ // Handle close
74
+ ws.on('close', (code, reason) => {
75
+ console.log('');
76
+ console.log(info(`Disconnected (code: ${code}, reason: ${reason.toString() || 'none'})`));
77
+ process.exit(0);
78
+ });
79
+ // Handle Ctrl+C
80
+ process.on('SIGINT', () => {
81
+ console.log('');
82
+ console.log(info('Disconnecting...'));
83
+ ws.close();
84
+ });
85
+ }
86
+ catch (err) {
87
+ console.log(error(err instanceof Error ? err.message : 'Failed to stream events'));
88
+ process.exit(1);
89
+ }
90
+ });
91
+ /**
92
+ * Format and print a WebSocket event
93
+ */
94
+ function formatEvent(event) {
95
+ const timestamp = event.timestamp ? new Date(event.timestamp).toLocaleTimeString() : '';
96
+ const timestampStr = timestamp ? `${colors.gray}[${timestamp}]${colors.reset} ` : '';
97
+ switch (event.type) {
98
+ case 'action_started': {
99
+ const data = event.data;
100
+ console.log(`${timestampStr}${colors.blue}▶${colors.reset} Action started: ${data.action_type || 'unknown'}`);
101
+ break;
102
+ }
103
+ case 'action_completed': {
104
+ const data = event.data;
105
+ if (data.success) {
106
+ console.log(`${timestampStr}${colors.green}✓${colors.reset} Action completed`);
107
+ }
108
+ else {
109
+ console.log(`${timestampStr}${colors.red}✗${colors.reset} Action failed: ${data.error || 'unknown error'}`);
110
+ }
111
+ break;
112
+ }
113
+ case 'status_changed': {
114
+ const data = event.data;
115
+ console.log(`${timestampStr}${colors.cyan}⚡${colors.reset} Status: ${data.status}`);
116
+ break;
117
+ }
118
+ case 'error': {
119
+ const data = event.data;
120
+ console.log(`${timestampStr}${colors.red}✗${colors.reset} Error: ${data.message || 'unknown'}`);
121
+ break;
122
+ }
123
+ case 'closed': {
124
+ const data = event.data;
125
+ console.log(`${timestampStr}${colors.yellow}●${colors.reset} Session closed${data?.reason ? `: ${data.reason}` : ''}`);
126
+ break;
127
+ }
128
+ case 'pong':
129
+ // Silently ignore pong responses
130
+ break;
131
+ default:
132
+ console.log(`${timestampStr}${colors.gray}[${event.type}]${colors.reset} ${JSON.stringify(event.data || {})}`);
133
+ }
134
+ }
135
+ //# sourceMappingURL=stream.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream.js","sourceRoot":"","sources":["../../../../../src/cli/commands/browser/stream.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * qa-use browser tunnel - Start a local browser with tunnel and create API session
3
+ *
4
+ * This command:
5
+ * 1. Starts a local Playwright browser
6
+ * 2. Creates a tunnel to expose the browser WebSocket
7
+ * 3. Creates an API session with the tunneled ws_url
8
+ * 4. Keeps the tunnel alive with heartbeat
9
+ * 5. Cleans up on exit (close session, stop tunnel, stop browser)
10
+ */
11
+ import { Command } from 'commander';
12
+ export declare const tunnelCommand: Command;
13
+ //# sourceMappingURL=tunnel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tunnel.d.ts","sourceRoot":"","sources":["../../../../../src/cli/commands/browser/tunnel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAoBpC,eAAO,MAAM,aAAa,SAuPtB,CAAC"}
@@ -0,0 +1,225 @@
1
+ /**
2
+ * qa-use browser tunnel - Start a local browser with tunnel and create API session
3
+ *
4
+ * This command:
5
+ * 1. Starts a local Playwright browser
6
+ * 2. Creates a tunnel to expose the browser WebSocket
7
+ * 3. Creates an API session with the tunneled ws_url
8
+ * 4. Keeps the tunnel alive with heartbeat
9
+ * 5. Cleans up on exit (close session, stop tunnel, stop browser)
10
+ */
11
+ import { Command } from 'commander';
12
+ import { BrowserManager } from '../../../../lib/browser/index.js';
13
+ import { TunnelManager } from '../../../../lib/tunnel/index.js';
14
+ import { BrowserApiClient } from '../../../../lib/api/browser.js';
15
+ import { storeSession, createStoredSession, removeStoredSession, } from '../../lib/browser-sessions.js';
16
+ import { loadConfig } from '../../lib/config.js';
17
+ import { success, error, info, warning } from '../../lib/output.js';
18
+ export const tunnelCommand = new Command('tunnel')
19
+ .description('Start a local browser with tunnel and create API session')
20
+ .option('--headless', 'Run browser in headless mode (default: false for tunnel)', false)
21
+ .option('--no-headless', 'Run browser with visible UI')
22
+ .option('--visible', 'Run browser with visible UI (alias for --no-headless)')
23
+ .option('--viewport <type>', 'Viewport type: desktop, mobile, or tablet (default: desktop)', 'desktop')
24
+ .option('--timeout <seconds>', 'Session timeout in seconds (default: 300)', '300')
25
+ .option('-s, --subdomain <name>', 'Custom tunnel subdomain (overrides deterministic)')
26
+ .action(async (options) => {
27
+ // Track resources for cleanup
28
+ let browser = null;
29
+ let tunnel = null;
30
+ let sessionId = null;
31
+ let client = null;
32
+ let heartbeatIntervalId = null;
33
+ // Cleanup function
34
+ const cleanup = async (exitCode = 0) => {
35
+ console.log('');
36
+ console.log(info('Shutting down...'));
37
+ // Stop heartbeat
38
+ if (heartbeatIntervalId) {
39
+ clearInterval(heartbeatIntervalId);
40
+ console.log(success('Heartbeat stopped'));
41
+ }
42
+ // Close API session and remove from local storage
43
+ if (sessionId && client) {
44
+ try {
45
+ await client.deleteSession(sessionId);
46
+ await removeStoredSession(sessionId);
47
+ console.log(success('API session closed'));
48
+ }
49
+ catch (err) {
50
+ console.log(warning(`Session cleanup: ${err instanceof Error ? err.message : 'Unknown error'}`));
51
+ }
52
+ }
53
+ // Stop tunnel
54
+ if (tunnel) {
55
+ try {
56
+ await tunnel.stopTunnel();
57
+ console.log(success('Tunnel closed'));
58
+ }
59
+ catch (err) {
60
+ console.log(warning(`Tunnel cleanup: ${err instanceof Error ? err.message : 'Unknown error'}`));
61
+ }
62
+ }
63
+ // Stop browser
64
+ if (browser) {
65
+ try {
66
+ await browser.stopBrowser();
67
+ console.log(success('Browser closed'));
68
+ }
69
+ catch (err) {
70
+ console.log(warning(`Browser cleanup: ${err instanceof Error ? err.message : 'Unknown error'}`));
71
+ }
72
+ }
73
+ console.log('');
74
+ console.log(info('Tunnel mode stopped'));
75
+ process.exit(exitCode);
76
+ };
77
+ // Handle signals for graceful shutdown
78
+ process.on('SIGINT', () => cleanup(0));
79
+ process.on('SIGTERM', () => cleanup(0));
80
+ try {
81
+ // Load configuration
82
+ const config = await loadConfig();
83
+ if (!config.api_key) {
84
+ console.log(error('API key not configured. Run `qa-use setup` first.'));
85
+ process.exit(1);
86
+ }
87
+ // Create client and set API key
88
+ client = new BrowserApiClient(config.api_url);
89
+ client.setApiKey(config.api_key);
90
+ // Validate viewport type
91
+ const validViewports = ['desktop', 'mobile', 'tablet'];
92
+ const viewport = (options.viewport || 'desktop');
93
+ if (!validViewports.includes(viewport)) {
94
+ console.log(error(`Invalid viewport: ${viewport}. Must be one of: ${validViewports.join(', ')}`));
95
+ process.exit(1);
96
+ }
97
+ // Parse timeout
98
+ const timeout = parseInt(String(options.timeout), 10);
99
+ if (isNaN(timeout) || timeout < 60 || timeout > 3600) {
100
+ console.log(error('Timeout must be between 60 and 3600 seconds'));
101
+ process.exit(1);
102
+ }
103
+ // Handle --visible as alias for --no-headless
104
+ const headless = options.headless !== false && !options.visible;
105
+ console.log('');
106
+ console.log(info('Starting browser tunnel...'));
107
+ console.log(`Mode: ${headless ? 'Headless' : 'Visible Browser'}`);
108
+ console.log('');
109
+ // Step 1: Start local browser
110
+ console.log(info('Starting browser...'));
111
+ browser = new BrowserManager();
112
+ const browserResult = await browser.startBrowser({ headless });
113
+ const wsEndpoint = browserResult.wsEndpoint;
114
+ console.log(success('Browser started'));
115
+ // Step 2: Start tunnel with deterministic subdomain
116
+ console.log(info('Creating tunnel...'));
117
+ tunnel = new TunnelManager();
118
+ const wsUrl = new URL(wsEndpoint);
119
+ const browserPort = parseInt(wsUrl.port);
120
+ await tunnel.startTunnel(browserPort, {
121
+ subdomain: options.subdomain,
122
+ apiKey: config.api_key,
123
+ sessionIndex: 0, // Use index 0 for browser tunnel command
124
+ });
125
+ console.log(success('Tunnel created'));
126
+ // Step 3: Get tunneled WebSocket URL
127
+ const localWsUrl = browser.getWebSocketEndpoint();
128
+ if (!localWsUrl) {
129
+ console.log(error('Failed to get browser WebSocket endpoint'));
130
+ await cleanup(1);
131
+ return;
132
+ }
133
+ const tunneledWsUrl = tunnel.getWebSocketUrl(localWsUrl);
134
+ if (!tunneledWsUrl) {
135
+ console.log(error('Failed to create tunneled WebSocket URL'));
136
+ await cleanup(1);
137
+ return;
138
+ }
139
+ console.log(info(`Tunneled WebSocket: ${tunneledWsUrl}`));
140
+ console.log('');
141
+ // Step 4: Create API session with tunneled ws_url
142
+ console.log(info('Creating API session...'));
143
+ const session = await client.createSession({
144
+ headless,
145
+ viewport,
146
+ timeout,
147
+ ws_url: tunneledWsUrl,
148
+ });
149
+ sessionId = session.id;
150
+ console.log(info(`Session created: ${session.id}`));
151
+ // Wait for session to become active if starting
152
+ if (session.status === 'starting') {
153
+ console.log(info('Waiting for session to become active...'));
154
+ await client.waitForStatus(session.id, 'active', 60000);
155
+ }
156
+ console.log(success('Session is active'));
157
+ // Store session locally
158
+ const storedSession = createStoredSession(session.id);
159
+ await storeSession(storedSession);
160
+ // Print session info
161
+ console.log('');
162
+ console.log('='.repeat(50));
163
+ console.log(success('Browser tunnel ready!'));
164
+ console.log('='.repeat(50));
165
+ console.log('');
166
+ console.log(`Session ID: ${session.id}`);
167
+ console.log(`Viewport: ${viewport}`);
168
+ console.log(`Headless: ${headless}`);
169
+ console.log(`Timeout: ${timeout}s`);
170
+ console.log(`WebSocket URL: ${tunneledWsUrl}`);
171
+ console.log('');
172
+ console.log(info('Use this session with other browser commands:'));
173
+ console.log(` qa-use browser goto ${session.id} https://example.com`);
174
+ console.log(` qa-use browser screenshot ${session.id}`);
175
+ console.log(` qa-use browser snapshot ${session.id}`);
176
+ console.log('');
177
+ console.log(info('Press Ctrl+C to stop'));
178
+ console.log('');
179
+ // Step 5: Set up heartbeat to check session and tunnel health
180
+ let heartbeatCount = 0;
181
+ const heartbeatInterval = 30000; // 30 seconds
182
+ heartbeatIntervalId = setInterval(async () => {
183
+ heartbeatCount++;
184
+ const timestamp = new Date().toLocaleTimeString();
185
+ try {
186
+ // Check if API session still exists
187
+ const sessionStatus = await client.getSession(sessionId);
188
+ if (sessionStatus.status === 'closed') {
189
+ console.log(`[${timestamp}] Session closed externally, shutting down...`);
190
+ await removeStoredSession(sessionId);
191
+ sessionId = null; // Prevent cleanup from trying to delete already-closed session
192
+ await cleanup(0);
193
+ return;
194
+ }
195
+ // Check tunnel health
196
+ const tunnelHealthy = await tunnel.checkHealth();
197
+ if (tunnelHealthy) {
198
+ console.log(`[${timestamp}] Heartbeat #${heartbeatCount} - healthy`);
199
+ }
200
+ else {
201
+ console.log(`[${timestamp}] ${warning(`Heartbeat #${heartbeatCount} - tunnel unhealthy`)}`);
202
+ }
203
+ }
204
+ catch (err) {
205
+ const errMessage = err instanceof Error ? err.message : 'Unknown error';
206
+ // If session not found, it was closed externally
207
+ if (errMessage.includes('not found') || errMessage.includes('404')) {
208
+ console.log(`[${timestamp}] Session closed externally, shutting down...`);
209
+ await removeStoredSession(sessionId);
210
+ sessionId = null; // Prevent cleanup from trying to delete already-closed session
211
+ await cleanup(0);
212
+ return;
213
+ }
214
+ console.log(`[${timestamp}] ${warning(`Heartbeat error: ${errMessage}`)}`);
215
+ }
216
+ }, heartbeatInterval);
217
+ // Keep the process running
218
+ // The interval and signal handlers will keep it alive
219
+ }
220
+ catch (err) {
221
+ console.log(error(err instanceof Error ? err.message : 'Failed to start tunnel'));
222
+ await cleanup(1);
223
+ }
224
+ });
225
+ //# sourceMappingURL=tunnel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tunnel.js","sourceRoot":"","sources":["../../../../../src/cli/commands/browser/tunnel.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * qa-use browser type - Type text with keystroke delays
3
+ */
4
+ import { Command } from 'commander';
5
+ export declare const typeCommand: Command;
6
+ //# sourceMappingURL=type.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"type.d.ts","sourceRoot":"","sources":["../../../../../src/cli/commands/browser/type.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAiBpC,eAAO,MAAM,WAAW,SAgDpB,CAAC"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * qa-use browser type - Type text with keystroke delays
3
+ */
4
+ import { Command } from 'commander';
5
+ import { BrowserApiClient } from '../../../../lib/api/browser.js';
6
+ import { resolveSessionId, touchSession } from '../../lib/browser-sessions.js';
7
+ import { loadConfig } from '../../lib/config.js';
8
+ import { success, error } from '../../lib/output.js';
9
+ /**
10
+ * Normalize ref by stripping leading @ if present
11
+ */
12
+ function normalizeRef(ref) {
13
+ return ref.startsWith('@') ? ref.slice(1) : ref;
14
+ }
15
+ export const typeCommand = new Command('type')
16
+ .description('Type text into an element with keystroke delays')
17
+ .argument('<ref>', 'Element ref (e.g., "e4" or "@e4")')
18
+ .argument('<text>', 'Text to type')
19
+ .option('-s, --session-id <id>', 'Session ID (auto-resolved if only one session)')
20
+ .action(async (ref, text, options) => {
21
+ try {
22
+ // Load configuration
23
+ const config = await loadConfig();
24
+ if (!config.api_key) {
25
+ console.log(error('API key not configured. Run `qa-use setup` first.'));
26
+ process.exit(1);
27
+ }
28
+ // Create client and set API key
29
+ const client = new BrowserApiClient(config.api_url);
30
+ client.setApiKey(config.api_key);
31
+ // Resolve session ID
32
+ const resolved = await resolveSessionId({
33
+ explicitId: options.sessionId,
34
+ client,
35
+ });
36
+ // Normalize ref
37
+ const normalizedRef = normalizeRef(ref);
38
+ // Execute type action
39
+ const result = await client.executeAction(resolved.id, {
40
+ type: 'type',
41
+ ref: normalizedRef,
42
+ text,
43
+ });
44
+ if (result.success) {
45
+ // Truncate text for display if too long
46
+ const displayText = text.length > 50 ? text.slice(0, 47) + '...' : text;
47
+ console.log(success(`Typed "${displayText}" into ${normalizedRef}`));
48
+ await touchSession(resolved.id);
49
+ }
50
+ else {
51
+ const hint = result.error || 'Type failed';
52
+ console.log(error(`${hint}. Use 'qa-use browser snapshot' to see available elements.`));
53
+ process.exit(1);
54
+ }
55
+ }
56
+ catch (err) {
57
+ console.log(error(err instanceof Error ? err.message : 'Failed to type text'));
58
+ process.exit(1);
59
+ }
60
+ });
61
+ //# sourceMappingURL=type.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"type.js","sourceRoot":"","sources":["../../../../../src/cli/commands/browser/type.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * qa-use browser uncheck - Uncheck a checkbox by ref or semantic text
3
+ */
4
+ import { Command } from 'commander';
5
+ export declare const uncheckCommand: Command;
6
+ //# sourceMappingURL=uncheck.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uncheck.d.ts","sourceRoot":"","sources":["../../../../../src/cli/commands/browser/uncheck.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAkBpC,eAAO,MAAM,cAAc,SAiDvB,CAAC"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * qa-use browser uncheck - Uncheck a checkbox by ref or semantic text
3
+ */
4
+ import { Command } from 'commander';
5
+ import { BrowserApiClient } from '../../../../lib/api/browser.js';
6
+ import { resolveSessionId, touchSession } from '../../lib/browser-sessions.js';
7
+ import { loadConfig } from '../../lib/config.js';
8
+ import { success, error } from '../../lib/output.js';
9
+ /**
10
+ * Normalize ref by stripping leading @ if present
11
+ */
12
+ function normalizeRef(ref) {
13
+ return ref.startsWith('@') ? ref.slice(1) : ref;
14
+ }
15
+ export const uncheckCommand = new Command('uncheck')
16
+ .description('Uncheck a checkbox by ref or semantic description')
17
+ .argument('[ref]', 'Element ref from snapshot (e.g., "e3" or "@e3")')
18
+ .option('-s, --session-id <id>', 'Session ID (auto-resolved if only one session)')
19
+ .option('-t, --text <description>', 'Semantic element description (AI-based, slower)')
20
+ .action(async (ref, options) => {
21
+ try {
22
+ // Validate that either ref or --text is provided
23
+ if (!ref && !options.text) {
24
+ console.log(error('Either <ref> argument or --text option is required'));
25
+ process.exit(1);
26
+ }
27
+ const config = await loadConfig();
28
+ if (!config.api_key) {
29
+ console.log(error('API key not configured. Run `qa-use setup` first.'));
30
+ process.exit(1);
31
+ }
32
+ const client = new BrowserApiClient(config.api_url);
33
+ client.setApiKey(config.api_key);
34
+ const resolved = await resolveSessionId({
35
+ explicitId: options.sessionId,
36
+ client,
37
+ });
38
+ // Build action with either ref or text
39
+ const action = { type: 'uncheck' };
40
+ if (ref) {
41
+ action.ref = normalizeRef(ref);
42
+ }
43
+ else if (options.text) {
44
+ action.text = options.text;
45
+ }
46
+ const result = await client.executeAction(resolved.id, action);
47
+ if (result.success) {
48
+ const target = ref ? `checkbox ${normalizeRef(ref)}` : `"${options.text}"`;
49
+ console.log(success(`Unchecked ${target}`));
50
+ await touchSession(resolved.id);
51
+ }
52
+ else {
53
+ console.log(error(result.error || 'Uncheck failed'));
54
+ process.exit(1);
55
+ }
56
+ }
57
+ catch (err) {
58
+ console.log(error(err instanceof Error ? err.message : 'Failed to uncheck checkbox'));
59
+ process.exit(1);
60
+ }
61
+ });
62
+ //# sourceMappingURL=uncheck.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uncheck.js","sourceRoot":"","sources":["../../../../../src/cli/commands/browser/uncheck.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * qa-use browser url - Get current page URL
3
+ */
4
+ import { Command } from 'commander';
5
+ export declare const urlCommand: Command;
6
+ //# sourceMappingURL=url.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"url.d.ts","sourceRoot":"","sources":["../../../../../src/cli/commands/browser/url.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAUpC,eAAO,MAAM,UAAU,SAkCnB,CAAC"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * qa-use browser url - Get current page URL
3
+ */
4
+ import { Command } from 'commander';
5
+ import { BrowserApiClient } from '../../../../lib/api/browser.js';
6
+ import { resolveSessionId, touchSession } from '../../lib/browser-sessions.js';
7
+ import { loadConfig } from '../../lib/config.js';
8
+ import { error } from '../../lib/output.js';
9
+ export const urlCommand = new Command('url')
10
+ .description('Get the current page URL')
11
+ .option('-s, --session-id <id>', 'Session ID (auto-resolved if only one session)')
12
+ .action(async (options) => {
13
+ try {
14
+ // Load configuration
15
+ const config = await loadConfig();
16
+ if (!config.api_key) {
17
+ console.log(error('API key not configured. Run `qa-use setup` first.'));
18
+ process.exit(1);
19
+ }
20
+ // Create client and set API key
21
+ const client = new BrowserApiClient(config.api_url);
22
+ client.setApiKey(config.api_key);
23
+ // Resolve session ID
24
+ const resolved = await resolveSessionId({
25
+ explicitId: options.sessionId,
26
+ client,
27
+ });
28
+ // Get URL
29
+ const url = await client.getUrl(resolved.id);
30
+ // Output just the URL
31
+ console.log(url);
32
+ // Update session timestamp
33
+ await touchSession(resolved.id);
34
+ }
35
+ catch (err) {
36
+ console.log(error(err instanceof Error ? err.message : 'Failed to get URL'));
37
+ process.exit(1);
38
+ }
39
+ });
40
+ //# sourceMappingURL=url.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"url.js","sourceRoot":"","sources":["../../../../../src/cli/commands/browser/url.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAClE,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAC/E,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAM5C,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,OAAO,CAAC,KAAK,CAAC;KACzC,WAAW,CAAC,0BAA0B,CAAC;KACvC,MAAM,CAAC,uBAAuB,EAAE,gDAAgD,CAAC;KACjF,MAAM,CAAC,KAAK,EAAE,OAAmB,EAAE,EAAE;IACpC,IAAI,CAAC;QACH,qBAAqB;QACrB,MAAM,MAAM,GAAG,MAAM,UAAU,EAAE,CAAC;QAClC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACpB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,mDAAmD,CAAC,CAAC,CAAC;YACxE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,gCAAgC;QAChC,MAAM,MAAM,GAAG,IAAI,gBAAgB,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACpD,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAEjC,qBAAqB;QACrB,MAAM,QAAQ,GAAG,MAAM,gBAAgB,CAAC;YACtC,UAAU,EAAE,OAAO,CAAC,SAAS;YAC7B,MAAM;SACP,CAAC,CAAC;QAEH,UAAU;QACV,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAE7C,sBAAsB;QACtB,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAEjB,2BAA2B;QAC3B,MAAM,YAAY,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IAClC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC;QAC7E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * qa-use browser wait-for-load - Wait for page load state
3
+ */
4
+ import { Command } from 'commander';
5
+ export declare const waitForLoadCommand: Command;
6
+ //# sourceMappingURL=wait-for-load.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wait-for-load.d.ts","sourceRoot":"","sources":["../../../../../src/cli/commands/browser/wait-for-load.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAYpC,eAAO,MAAM,kBAAkB,SA2C3B,CAAC"}