@xtest-cli/cli
Version:
CLI for xtest.ing - AI-powered test generation platform
327 lines โข 12.3 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.dualInteractiveCommand = void 0;
exports.dualInteractive = dualInteractive;
const playwright_1 = require("playwright");
const readline = __importStar(require("readline"));
const axios_1 = __importDefault(require("axios"));
const ws_1 = __importDefault(require("ws"));
const commander_1 = require("commander");
const config_1 = require("../utils/config");
exports.dualInteractiveCommand = new commander_1.Command('interactive')
.alias('i')
.description('Interactive dual browser control with persistent state')
.action(dualInteractive);
async function dualInteractive() {
console.log('๐ Starting interactive dual browser control...\n');
const config = await (0, config_1.getConfig)();
if (!config.apiKey) {
console.error('โ Not authenticated. Please run "xtest auth" first.');
process.exit(1);
}
const API_BASE_URL = config.serverUrl;
const WS_BASE_URL = config.serverUrl.replace('https://', 'wss://').replace('http://', 'ws://');
const state = {
localBrowser: null,
localPage: null,
cloudSessionId: null,
ws: null,
apiClient: axios_1.default.create({
baseURL: API_BASE_URL,
headers: {
'Authorization': `Bearer ${config.apiKey}`,
'Content-Type': 'application/json',
},
}),
};
// Initialize browsers
try {
// Launch local browser
console.log('๐ฅ๏ธ Launching local browser...');
state.localBrowser = await playwright_1.chromium.launch({
headless: false,
args: ['--start-maximized'],
});
const context = await state.localBrowser.newContext({
viewport: null,
});
state.localPage = await context.newPage();
console.log('โ
Local browser ready');
// Create cloud session
console.log('โ๏ธ Creating cloud browser session...');
state.cloudSessionId = `cloud-interactive-${Date.now()}`;
await state.apiClient.post('/api/enhanced-browser/create', {
sessionId: state.cloudSessionId,
url: 'about:blank',
});
console.log('โ
Cloud browser ready');
// Connect WebSocket for real-time updates
state.ws = new ws_1.default(`${WS_BASE_URL}/ws/cli`, {
headers: {
'Authorization': `Bearer ${config.apiKey}`,
'x-session-id': state.cloudSessionId,
},
});
state.ws.on('open', () => {
console.log('โ
Connected to cloud control');
});
state.ws.on('message', (data) => {
const message = JSON.parse(data.toString());
if (message.type === 'navigation') {
console.log(`โ๏ธ Cloud navigated to: ${message.url}`);
}
});
console.log('\nโจ Both browsers ready for independent control!');
console.log('๐ Local browser: Direct control');
console.log('โ๏ธ Cloud browser: Remote control');
console.log('\nAvailable commands:');
console.log(' local <url> - Navigate local browser');
console.log(' cloud <url> - Navigate cloud browser');
console.log(' local click <selector> - Click in local browser');
console.log(' cloud click <selector> - Click in cloud browser');
console.log(' local type <selector> <text> - Type in local browser');
console.log(' cloud type <selector> <text> - Type in cloud browser');
console.log(' status - Show browser states');
console.log(' exit - Close browsers and exit');
console.log('');
// Start REPL
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
prompt: 'xtest> ',
});
rl.prompt();
rl.on('line', async (line) => {
const parts = line.trim().split(' ');
const command = parts[0];
try {
switch (command) {
case 'local':
await handleLocalCommand(state, parts.slice(1));
break;
case 'cloud':
await handleCloudCommand(state, parts.slice(1));
break;
case 'status':
await showStatus(state);
break;
case 'exit':
await cleanup(state);
rl.close();
process.exit(0);
break;
default:
console.log('Unknown command. Type "help" for available commands.');
}
}
catch (error) {
console.error('Error:', error.message || error);
}
rl.prompt();
});
rl.on('close', async () => {
await cleanup(state);
process.exit(0);
});
}
catch (error) {
console.error('Failed to initialize:', error);
await cleanup(state);
process.exit(1);
}
}
async function handleLocalCommand(state, args) {
if (!state.localPage) {
console.error('Local browser not initialized');
return;
}
const action = args[0];
switch (action) {
case 'goto':
case 'navigate': {
const url = args[1];
if (!url) {
console.error('Please provide a URL');
return;
}
console.log(`๐ Navigating local browser to: ${url}`);
await state.localPage.goto(url.startsWith('http') ? url : `https://${url}`);
console.log('โ
Local navigation complete');
break;
}
case 'click': {
const clickSelector = args[1];
if (!clickSelector) {
console.error('Please provide a selector');
return;
}
await state.localPage.click(clickSelector);
console.log(`โ
Clicked ${clickSelector} in local browser`);
break;
}
case 'type': {
const typeSelector = args[1];
const text = args.slice(2).join(' ');
if (!typeSelector || !text) {
console.error('Please provide selector and text');
return;
}
await state.localPage.fill(typeSelector, text);
console.log(`โ
Typed in ${typeSelector} in local browser`);
break;
}
default: {
// If no action specified, treat it as navigation
if (args[0]) {
const url = args[0];
console.log(`๐ Navigating local browser to: ${url}`);
await state.localPage.goto(url.startsWith('http') ? url : `https://${url}`);
console.log('โ
Local navigation complete');
}
}
}
}
async function handleCloudCommand(state, args) {
if (!state.cloudSessionId) {
console.error('Cloud browser not initialized');
return;
}
const action = args[0];
switch (action) {
case 'goto':
case 'navigate': {
const url = args[1];
if (!url) {
console.error('Please provide a URL');
return;
}
console.log(`โ๏ธ Navigating cloud browser to: ${url}`);
await state.apiClient.post('/api/enhanced-browser/navigate', {
sessionId: state.cloudSessionId,
url: url.startsWith('http') ? url : `https://${url}`,
});
console.log('โ
Cloud navigation complete');
break;
}
case 'click': {
const clickSelector = args[1];
if (!clickSelector) {
console.error('Please provide a selector');
return;
}
await state.apiClient.post('/api/enhanced-browser/click', {
sessionId: state.cloudSessionId,
selector: clickSelector,
});
console.log(`โ
Clicked ${clickSelector} in cloud browser`);
break;
}
case 'type': {
const typeSelector = args[1];
const text = args.slice(2).join(' ');
if (!typeSelector || !text) {
console.error('Please provide selector and text');
return;
}
await state.apiClient.post('/api/enhanced-browser/type', {
sessionId: state.cloudSessionId,
selector: typeSelector,
text: text,
});
console.log(`โ
Typed in ${typeSelector} in cloud browser`);
break;
}
default: {
// If no action specified, treat it as navigation
if (args[0]) {
const url = args[0];
console.log(`โ๏ธ Navigating cloud browser to: ${url}`);
await state.apiClient.post('/api/enhanced-browser/navigate', {
sessionId: state.cloudSessionId,
url: url.startsWith('http') ? url : `https://${url}`,
});
console.log('โ
Cloud navigation complete');
}
}
}
}
async function showStatus(state) {
console.log('\n๐ Browser Status:');
if (state.localPage) {
const localUrl = state.localPage.url();
console.log(`๐ Local browser: ${localUrl}`);
}
else {
console.log('๐ Local browser: Not initialized');
}
if (state.cloudSessionId) {
try {
const response = await state.apiClient.get(`/api/enhanced-browser/session/${state.cloudSessionId}`);
console.log(`โ๏ธ Cloud browser: ${response.data.url || 'about:blank'}`);
}
catch (error) {
console.log(`โ๏ธ Cloud browser: Session ${state.cloudSessionId} (status unknown)`);
}
}
else {
console.log('โ๏ธ Cloud browser: Not initialized');
}
console.log('');
}
async function cleanup(state) {
console.log('\n๐งน Cleaning up...');
if (state.ws) {
state.ws.close();
}
if (state.localBrowser) {
await state.localBrowser.close();
}
if (state.cloudSessionId) {
try {
await state.apiClient.post('/api/enhanced-browser/close', {
sessionId: state.cloudSessionId,
});
}
catch (error) {
// Ignore errors during cleanup
}
}
console.log('โ
Cleanup complete');
}
//# sourceMappingURL=dual-interactive.js.map