@desplega.ai/qa-use 2.1.7 → 2.2.3
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/dist/lib/api/browser-types.d.ts +175 -0
- package/dist/lib/api/browser-types.d.ts.map +1 -0
- package/dist/lib/api/browser-types.js +5 -0
- package/dist/lib/api/browser-types.js.map +1 -0
- package/dist/lib/api/browser.d.ts +66 -0
- package/dist/lib/api/browser.d.ts.map +1 -0
- package/dist/lib/api/browser.js +223 -0
- package/dist/lib/api/browser.js.map +1 -0
- package/dist/lib/browser/index.d.ts +10 -0
- package/dist/lib/browser/index.d.ts.map +1 -1
- package/dist/lib/browser/index.js +29 -0
- package/dist/lib/browser/index.js.map +1 -1
- package/dist/package.json +2 -1
- package/dist/src/cli/commands/browser/back.d.ts +6 -0
- package/dist/src/cli/commands/browser/back.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/back.js +42 -0
- package/dist/src/cli/commands/browser/back.js.map +1 -0
- package/dist/src/cli/commands/browser/check.d.ts +6 -0
- package/dist/src/cli/commands/browser/check.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/check.js +62 -0
- package/dist/src/cli/commands/browser/check.js.map +1 -0
- package/dist/src/cli/commands/browser/click.d.ts +6 -0
- package/dist/src/cli/commands/browser/click.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/click.js +63 -0
- package/dist/src/cli/commands/browser/click.js.map +1 -0
- package/dist/src/cli/commands/browser/close.d.ts +6 -0
- package/dist/src/cli/commands/browser/close.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/close.js +44 -0
- package/dist/src/cli/commands/browser/close.js.map +1 -0
- package/dist/src/cli/commands/browser/create.d.ts +6 -0
- package/dist/src/cli/commands/browser/create.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/create.js +284 -0
- package/dist/src/cli/commands/browser/create.js.map +1 -0
- package/dist/src/cli/commands/browser/fill.d.ts +6 -0
- package/dist/src/cli/commands/browser/fill.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/fill.js +83 -0
- package/dist/src/cli/commands/browser/fill.js.map +1 -0
- package/dist/src/cli/commands/browser/forward.d.ts +6 -0
- package/dist/src/cli/commands/browser/forward.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/forward.js +42 -0
- package/dist/src/cli/commands/browser/forward.js.map +1 -0
- package/dist/src/cli/commands/browser/get-blocks.d.ts +6 -0
- package/dist/src/cli/commands/browser/get-blocks.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/get-blocks.js +35 -0
- package/dist/src/cli/commands/browser/get-blocks.js.map +1 -0
- package/dist/src/cli/commands/browser/goto.d.ts +6 -0
- package/dist/src/cli/commands/browser/goto.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/goto.js +53 -0
- package/dist/src/cli/commands/browser/goto.js.map +1 -0
- package/dist/src/cli/commands/browser/hover.d.ts +6 -0
- package/dist/src/cli/commands/browser/hover.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/hover.js +63 -0
- package/dist/src/cli/commands/browser/hover.js.map +1 -0
- package/dist/src/cli/commands/browser/index.d.ts +9 -0
- package/dist/src/cli/commands/browser/index.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/index.js +71 -0
- package/dist/src/cli/commands/browser/index.js.map +1 -0
- package/dist/src/cli/commands/browser/list.d.ts +6 -0
- package/dist/src/cli/commands/browser/list.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/list.js +85 -0
- package/dist/src/cli/commands/browser/list.js.map +1 -0
- package/dist/src/cli/commands/browser/press.d.ts +6 -0
- package/dist/src/cli/commands/browser/press.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/press.js +67 -0
- package/dist/src/cli/commands/browser/press.js.map +1 -0
- package/dist/src/cli/commands/browser/reload.d.ts +6 -0
- package/dist/src/cli/commands/browser/reload.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/reload.js +42 -0
- package/dist/src/cli/commands/browser/reload.js.map +1 -0
- package/dist/src/cli/commands/browser/run.d.ts +6 -0
- package/dist/src/cli/commands/browser/run.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/run.js +618 -0
- package/dist/src/cli/commands/browser/run.js.map +1 -0
- package/dist/src/cli/commands/browser/screenshot.d.ts +6 -0
- package/dist/src/cli/commands/browser/screenshot.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/screenshot.js +72 -0
- package/dist/src/cli/commands/browser/screenshot.js.map +1 -0
- package/dist/src/cli/commands/browser/scroll-into-view.d.ts +6 -0
- package/dist/src/cli/commands/browser/scroll-into-view.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/scroll-into-view.js +64 -0
- package/dist/src/cli/commands/browser/scroll-into-view.js.map +1 -0
- package/dist/src/cli/commands/browser/scroll.d.ts +6 -0
- package/dist/src/cli/commands/browser/scroll.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/scroll.js +63 -0
- package/dist/src/cli/commands/browser/scroll.js.map +1 -0
- package/dist/src/cli/commands/browser/select.d.ts +6 -0
- package/dist/src/cli/commands/browser/select.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/select.js +83 -0
- package/dist/src/cli/commands/browser/select.js.map +1 -0
- package/dist/src/cli/commands/browser/snapshot.d.ts +6 -0
- package/dist/src/cli/commands/browser/snapshot.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/snapshot.js +72 -0
- package/dist/src/cli/commands/browser/snapshot.js.map +1 -0
- package/dist/src/cli/commands/browser/status.d.ts +6 -0
- package/dist/src/cli/commands/browser/status.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/status.js +91 -0
- package/dist/src/cli/commands/browser/status.js.map +1 -0
- package/dist/src/cli/commands/browser/stream.d.ts +6 -0
- package/dist/src/cli/commands/browser/stream.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/stream.js +135 -0
- package/dist/src/cli/commands/browser/stream.js.map +1 -0
- package/dist/src/cli/commands/browser/tunnel.d.ts +13 -0
- package/dist/src/cli/commands/browser/tunnel.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/tunnel.js +225 -0
- package/dist/src/cli/commands/browser/tunnel.js.map +1 -0
- package/dist/src/cli/commands/browser/type.d.ts +6 -0
- package/dist/src/cli/commands/browser/type.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/type.js +61 -0
- package/dist/src/cli/commands/browser/type.js.map +1 -0
- package/dist/src/cli/commands/browser/uncheck.d.ts +6 -0
- package/dist/src/cli/commands/browser/uncheck.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/uncheck.js +62 -0
- package/dist/src/cli/commands/browser/uncheck.js.map +1 -0
- package/dist/src/cli/commands/browser/url.d.ts +6 -0
- package/dist/src/cli/commands/browser/url.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/url.js +40 -0
- package/dist/src/cli/commands/browser/url.js.map +1 -0
- package/dist/src/cli/commands/browser/wait-for-load.d.ts +6 -0
- package/dist/src/cli/commands/browser/wait-for-load.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/wait-for-load.js +50 -0
- package/dist/src/cli/commands/browser/wait-for-load.js.map +1 -0
- package/dist/src/cli/commands/browser/wait-for-selector.d.ts +6 -0
- package/dist/src/cli/commands/browser/wait-for-selector.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/wait-for-selector.js +52 -0
- package/dist/src/cli/commands/browser/wait-for-selector.js.map +1 -0
- package/dist/src/cli/commands/browser/wait.d.ts +6 -0
- package/dist/src/cli/commands/browser/wait.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/wait.js +60 -0
- package/dist/src/cli/commands/browser/wait.js.map +1 -0
- package/dist/src/cli/commands/info.d.ts +1 -1
- package/dist/src/cli/commands/info.d.ts.map +1 -1
- package/dist/src/cli/commands/info.js +178 -15
- package/dist/src/cli/commands/info.js.map +1 -1
- package/dist/src/cli/commands/install-deps.d.ts +6 -0
- package/dist/src/cli/commands/install-deps.d.ts.map +1 -0
- package/dist/src/cli/commands/install-deps.js +52 -0
- package/dist/src/cli/commands/install-deps.js.map +1 -0
- package/dist/src/cli/commands/test/run.d.ts.map +1 -1
- package/dist/src/cli/commands/test/run.js +32 -7
- package/dist/src/cli/commands/test/run.js.map +1 -1
- package/dist/src/cli/index.js +4 -0
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/cli/lib/browser-sessions.d.ts +72 -0
- package/dist/src/cli/lib/browser-sessions.d.ts.map +1 -0
- package/dist/src/cli/lib/browser-sessions.js +184 -0
- package/dist/src/cli/lib/browser-sessions.js.map +1 -0
- package/dist/src/cli/lib/browser.d.ts +5 -0
- package/dist/src/cli/lib/browser.d.ts.map +1 -1
- package/dist/src/cli/lib/browser.js +19 -0
- package/dist/src/cli/lib/browser.js.map +1 -1
- package/dist/src/cli/lib/config.d.ts.map +1 -1
- package/dist/src/cli/lib/config.js +3 -0
- package/dist/src/cli/lib/config.js.map +1 -1
- package/lib/api/browser-types.ts +278 -0
- package/lib/api/browser.test.ts +378 -0
- package/lib/api/browser.ts +279 -0
- package/lib/browser/index.ts +37 -0
- 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":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,SAAS,MAAM,IAAI,CAAC;AAC3B,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,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAMlD,mBAAmB;AACnB,MAAM,MAAM,GAAG;IACb,KAAK,EAAE,SAAS;IAChB,KAAK,EAAE,UAAU;IACjB,GAAG,EAAE,UAAU;IACf,MAAM,EAAE,UAAU;IAClB,IAAI,EAAE,UAAU;IAChB,IAAI,EAAE,UAAU;IAChB,IAAI,EAAE,UAAU;CACjB,CAAC;AAEF,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,OAAO,CAAC,QAAQ,CAAC;KAC/C,WAAW,CAAC,gDAAgD,CAAC;KAC7D,MAAM,CAAC,uBAAuB,EAAE,gDAAgD,CAAC;KACjF,MAAM,CAAC,KAAK,EAAE,OAAsB,EAAE,EAAE;IACvC,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,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,yBAAyB,QAAQ,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC;QAE7D,oBAAoB;QACpB,MAAM,KAAK,GAAG,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAE/C,8BAA8B;QAC9B,MAAM,EAAE,GAAG,IAAI,SAAS,CAAC,KAAK,EAAE;YAC9B,OAAO,EAAE;gBACP,aAAa,EAAE,UAAU,MAAM,CAAC,SAAS,EAAE,EAAE;aAC9C;SACF,CAAC,CAAC;QAEH,yBAAyB;QACzB,EAAE,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE;YACjB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,0CAA0C,CAAC,CAAC,CAAC;YAC9D,YAAY,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;QAEH,kBAAkB;QAClB,EAAE,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,IAAoB,EAAE,EAAE;YACxC,IAAI,CAAC;gBACH,mCAAmC;gBACnC,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;oBAC1B,OAAO,CAAC,GAAG,CACT,GAAG,MAAM,CAAC,IAAI,6BAA6B,IAAI,CAAC,MAAM,UAAU,MAAM,CAAC,KAAK,EAAE,CAC/E,CAAC;oBACF,OAAO;gBACT,CAAC;gBAED,qBAAqB;gBACrB,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;gBAC5C,WAAW,CAAC,OAAO,CAAC,CAAC;YACvB,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,IAAI,QAAQ,IAAI,CAAC,QAAQ,EAAE,GAAG,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;YACtE,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,gBAAgB;QAChB,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YACrB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,oBAAoB,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QACxD,CAAC,CAAC,CAAC;QAEH,eAAe;QACf,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE;YAC9B,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAChB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,uBAAuB,IAAI,aAAa,MAAM,CAAC,QAAQ,EAAE,IAAI,MAAM,GAAG,CAAC,CAAC,CAAC;YAC1F,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC,CAAC,CAAC;QAEH,gBAAgB;QAChB,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE;YACxB,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAChB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC,CAAC;YACtC,EAAE,CAAC,KAAK,EAAE,CAAC;QACb,CAAC,CAAC,CAAC;IACL,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,yBAAyB,CAAC,CAAC,CAAC;QACnF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC;AAEL;;GAEG;AACH,SAAS,WAAW,CAAC,KAA2D;IAC9E,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,kBAAkB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACxF,MAAM,YAAY,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,IAAI,IAAI,SAAS,IAAI,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IAErF,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;QACnB,KAAK,gBAAgB,CAAC,CAAC,CAAC;YACtB,MAAM,IAAI,GAAG,KAAK,CAAC,IAAoD,CAAC;YACxE,OAAO,CAAC,GAAG,CACT,GAAG,YAAY,GAAG,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,KAAK,oBAAoB,IAAI,CAAC,WAAW,IAAI,SAAS,EAAE,CACjG,CAAC;YACF,MAAM;QACR,CAAC;QAED,KAAK,kBAAkB,CAAC,CAAC,CAAC;YACxB,MAAM,IAAI,GAAG,KAAK,CAAC,IAA6C,CAAC;YACjE,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;gBACjB,OAAO,CAAC,GAAG,CAAC,GAAG,YAAY,GAAG,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,KAAK,mBAAmB,CAAC,CAAC;YACjF,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,GAAG,CACT,GAAG,YAAY,GAAG,MAAM,CAAC,GAAG,IAAI,MAAM,CAAC,KAAK,mBAAmB,IAAI,CAAC,KAAK,IAAI,eAAe,EAAE,CAC/F,CAAC;YACJ,CAAC;YACD,MAAM;QACR,CAAC;QAED,KAAK,gBAAgB,CAAC,CAAC,CAAC;YACtB,MAAM,IAAI,GAAG,KAAK,CAAC,IAA2B,CAAC;YAC/C,OAAO,CAAC,GAAG,CAAC,GAAG,YAAY,GAAG,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,KAAK,YAAY,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;YACpF,MAAM;QACR,CAAC;QAED,KAAK,OAAO,CAAC,CAAC,CAAC;YACb,MAAM,IAAI,GAAG,KAAK,CAAC,IAA2C,CAAC;YAC/D,OAAO,CAAC,GAAG,CACT,GAAG,YAAY,GAAG,MAAM,CAAC,GAAG,IAAI,MAAM,CAAC,KAAK,WAAW,IAAI,CAAC,OAAO,IAAI,SAAS,EAAE,CACnF,CAAC;YACF,MAAM;QACR,CAAC;QAED,KAAK,QAAQ,CAAC,CAAC,CAAC;YACd,MAAM,IAAI,GAAG,KAAK,CAAC,IAA2B,CAAC;YAC/C,OAAO,CAAC,GAAG,CACT,GAAG,YAAY,GAAG,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,KAAK,kBAAkB,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAC1G,CAAC;YACF,MAAM;QACR,CAAC;QAED,KAAK,MAAM;YACT,iCAAiC;YACjC,MAAM;QAER;YACE,OAAO,CAAC,GAAG,CACT,GAAG,YAAY,GAAG,MAAM,CAAC,IAAI,IAAI,KAAK,CAAC,IAAI,IAAI,MAAM,CAAC,KAAK,IAAI,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC,EAAE,CAClG,CAAC;IACN,CAAC;AACH,CAAC"}
|
|
@@ -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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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"}
|