devicely 2.2.1 → 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/lib/frontend/asset-manifest.json +3 -3
- package/lib/frontend/index.html +1 -1
- package/lib/frontend/static/js/main.223106ce.js +3 -0
- package/lib/frontend/static/js/main.26a24c5c.js +3 -0
- package/lib/frontend/static/js/main.26a24c5c.js.LICENSE.txt +48 -0
- package/lib/frontend/static/js/main.31418566.js +3 -0
- package/lib/frontend/static/js/main.31418566.js.LICENSE.txt +48 -0
- package/lib/frontend/static/js/main.a507b3fc.js +3 -0
- package/lib/frontend/static/js/main.a507b3fc.js.LICENSE.txt +48 -0
- package/lib/frontend/static/js/{main.5495c762.js → main.e6f081bf.js} +3 -3
- package/lib/frontend/static/js/main.e6f081bf.js.LICENSE.txt +48 -0
- package/lib/server.js +67 -45
- package/lib/server.js.bak +67 -45
- package/package.json +1 -1
- package/scripts/shell/android_device_control.enc +1 -1
- package/scripts/shell/connect_android_usb_multi_final.enc +1 -1
- package/scripts/shell/connect_android_wireless.enc +1 -1
- package/scripts/shell/connect_android_wireless_multi_final.enc +1 -1
- package/scripts/shell/connect_ios_usb_multi_final.enc +1 -1
- package/scripts/shell/connect_ios_wireless_multi_final.enc +1 -1
- package/scripts/shell/ios_device_control.enc +1 -1
- package/lib/scriptLoader.js +0 -75
- /package/lib/frontend/static/js/{main.5495c762.js.LICENSE.txt → main.223106ce.js.LICENSE.txt} +0 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license React
|
|
3
|
+
* react-dom.production.min.js
|
|
4
|
+
*
|
|
5
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
6
|
+
*
|
|
7
|
+
* This source code is licensed under the MIT license found in the
|
|
8
|
+
* LICENSE file in the root directory of this source tree.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @license React
|
|
13
|
+
* react-jsx-runtime.production.min.js
|
|
14
|
+
*
|
|
15
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
16
|
+
*
|
|
17
|
+
* This source code is licensed under the MIT license found in the
|
|
18
|
+
* LICENSE file in the root directory of this source tree.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @license React
|
|
23
|
+
* react.production.min.js
|
|
24
|
+
*
|
|
25
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
26
|
+
*
|
|
27
|
+
* This source code is licensed under the MIT license found in the
|
|
28
|
+
* LICENSE file in the root directory of this source tree.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @license React
|
|
33
|
+
* scheduler.production.min.js
|
|
34
|
+
*
|
|
35
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
36
|
+
*
|
|
37
|
+
* This source code is licensed under the MIT license found in the
|
|
38
|
+
* LICENSE file in the root directory of this source tree.
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
/** @license React v16.13.1
|
|
42
|
+
* react-is.production.min.js
|
|
43
|
+
*
|
|
44
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
45
|
+
*
|
|
46
|
+
* This source code is licensed under the MIT license found in the
|
|
47
|
+
* LICENSE file in the root directory of this source tree.
|
|
48
|
+
*/
|
package/lib/server.js
CHANGED
|
@@ -104,7 +104,7 @@ function findScriptFile(filename) {
|
|
|
104
104
|
|
|
105
105
|
for (const { path: scriptPath, encrypted } of pathsToTry) {
|
|
106
106
|
if (fsSync.existsSync(scriptPath)) {
|
|
107
|
-
|
|
107
|
+
logger.debug(`✅ Found script: ${filename} at ${scriptPath}${encrypted ? ' (encrypted)' : ''}`);
|
|
108
108
|
|
|
109
109
|
// If encrypted, decrypt to temp directory and return temp path
|
|
110
110
|
if (encrypted && scriptLoader) {
|
|
@@ -120,7 +120,7 @@ function findScriptFile(filename) {
|
|
|
120
120
|
try {
|
|
121
121
|
const content = scriptLoader.decryptScript(scriptPath);
|
|
122
122
|
fsSync.writeFileSync(tmpScript, content, { mode: 0o755 });
|
|
123
|
-
|
|
123
|
+
logger.debug(`🔓 Decrypted ${filename} to temp directory`);
|
|
124
124
|
} catch (error) {
|
|
125
125
|
console.error(`❌ Failed to decrypt ${filename}: ${error.message}`);
|
|
126
126
|
continue;
|
|
@@ -538,10 +538,10 @@ function resolveAppPreset(command, platform) {
|
|
|
538
538
|
// Execute command on device(s)
|
|
539
539
|
function executeCommand(deviceNames, command, wsClient) {
|
|
540
540
|
return new Promise(async (resolve, reject) => {
|
|
541
|
-
|
|
542
|
-
|
|
541
|
+
// separator removed
|
|
542
|
+
logger.debug(`Device names received: [${deviceNames.join(', ')}]`);
|
|
543
543
|
console.log(`Command: "${command}"`);
|
|
544
|
-
|
|
544
|
+
// separator removed
|
|
545
545
|
|
|
546
546
|
// Get full device objects from connectedDevices cache
|
|
547
547
|
// Match by name, udid, or serial
|
|
@@ -551,7 +551,7 @@ function executeCommand(deviceNames, command, wsClient) {
|
|
|
551
551
|
deviceNames.includes(d.serial)
|
|
552
552
|
);
|
|
553
553
|
|
|
554
|
-
|
|
554
|
+
logger.debug(`Cache contents:`, connectedDevices.map(d => `${d.name}(${d.status})`).join(', '));
|
|
555
555
|
|
|
556
556
|
if (devices.length === 0) {
|
|
557
557
|
console.error(`❌ No devices found for: [${deviceNames.join(', ')}]`);
|
|
@@ -595,7 +595,7 @@ function executeCommand(deviceNames, command, wsClient) {
|
|
|
595
595
|
if (iosUSBDevices.length > 0) {
|
|
596
596
|
const names = iosUSBDevices.map(d => d.name);
|
|
597
597
|
const iosCommand = resolveAppPreset(command, 'ios');
|
|
598
|
-
|
|
598
|
+
logger.debug(`Using iOS USB script for: ${names.join(', ')}`);
|
|
599
599
|
promises.push(executeWithScript(USB_SCRIPT_PATH, names, iosCommand, wsClient));
|
|
600
600
|
}
|
|
601
601
|
|
|
@@ -610,14 +610,14 @@ function executeCommand(deviceNames, command, wsClient) {
|
|
|
610
610
|
// Execute for Android USB devices
|
|
611
611
|
if (androidUSBDevices.length > 0) {
|
|
612
612
|
const androidCommand = resolveAppPreset(command, 'android');
|
|
613
|
-
|
|
613
|
+
logger.debug(`Using Android control script for USB devices: ${androidUSBDevices.map(d => d.name).join(', ')}`);
|
|
614
614
|
promises.push(executeAndroidDevices(ANDROID_USB_SCRIPT_PATH, androidUSBDevices, androidCommand, wsClient));
|
|
615
615
|
}
|
|
616
616
|
|
|
617
617
|
// Execute for Android wireless devices
|
|
618
618
|
if (androidWirelessDevices.length > 0) {
|
|
619
619
|
const androidCommand = resolveAppPreset(command, 'android');
|
|
620
|
-
|
|
620
|
+
logger.debug(`Using Android control script for wireless devices: ${androidWirelessDevices.map(d => d.name).join(', ')}`);
|
|
621
621
|
promises.push(executeAndroidDevices(ANDROID_WIRELESS_SCRIPT_PATH, androidWirelessDevices, androidCommand, wsClient));
|
|
622
622
|
}
|
|
623
623
|
|
|
@@ -631,7 +631,7 @@ function executeCommand(deviceNames, command, wsClient) {
|
|
|
631
631
|
const successCount = results.filter(r => r.status === 'fulfilled').length;
|
|
632
632
|
const failCount = results.filter(r => r.status === 'rejected').length;
|
|
633
633
|
|
|
634
|
-
|
|
634
|
+
logger.debug(`ExecuteCommand summary: ${successCount} succeeded, ${failCount} failed`);
|
|
635
635
|
|
|
636
636
|
resolve({
|
|
637
637
|
success: true,
|
|
@@ -713,9 +713,9 @@ function executeWithScript(scriptPath, deviceNames, command, wsClient) {
|
|
|
713
713
|
const successful = results.filter(r => r.status === 'fulfilled').length;
|
|
714
714
|
const failed = results.filter(r => r.status === 'rejected').length;
|
|
715
715
|
|
|
716
|
-
|
|
716
|
+
// separator removed
|
|
717
717
|
|
|
718
|
-
|
|
718
|
+
// separator removed
|
|
719
719
|
|
|
720
720
|
resolve({
|
|
721
721
|
success: true,
|
|
@@ -793,12 +793,12 @@ function executeAndroidDevices(scriptPath, devices, command, wsClient) {
|
|
|
793
793
|
|
|
794
794
|
Promise.allSettled(devicePromises)
|
|
795
795
|
.then(results => {
|
|
796
|
-
|
|
796
|
+
// separator removed
|
|
797
797
|
|
|
798
798
|
const successCount = results.filter(r => r.status === 'fulfilled' && r.value.success).length;
|
|
799
799
|
const failCount = results.length - successCount;
|
|
800
800
|
|
|
801
|
-
|
|
801
|
+
// separator removed
|
|
802
802
|
|
|
803
803
|
resolve({ success: true, results });
|
|
804
804
|
})
|
|
@@ -836,11 +836,11 @@ async function convertNaturalLanguageToCommand(text, devices, provider = null) {
|
|
|
836
836
|
async function executeCommandsSequentially(devices, commandsText, wsClient) {
|
|
837
837
|
const lines = commandsText.split('\n').map(line => line.trim()).filter(line => line);
|
|
838
838
|
|
|
839
|
-
|
|
839
|
+
// separator removed
|
|
840
840
|
|
|
841
841
|
console.log(`Total commands: ${lines.length}`);
|
|
842
842
|
console.log(`Commands:`, lines);
|
|
843
|
-
|
|
843
|
+
// separator removed
|
|
844
844
|
|
|
845
845
|
const results = [];
|
|
846
846
|
|
|
@@ -899,10 +899,10 @@ async function executeCommandsSequentially(devices, commandsText, wsClient) {
|
|
|
899
899
|
}
|
|
900
900
|
}
|
|
901
901
|
|
|
902
|
-
|
|
902
|
+
// separator removed
|
|
903
903
|
|
|
904
904
|
console.log(`Total: ${results.length}, Success: ${results.filter(r => r.success).length}, Failed: ${results.filter(r => !r.success).length}`);
|
|
905
|
-
|
|
905
|
+
// separator removed
|
|
906
906
|
|
|
907
907
|
return {
|
|
908
908
|
success: true,
|
|
@@ -2063,12 +2063,12 @@ async function replayRecording(filename, targetDevices, wsClient) {
|
|
|
2063
2063
|
? recording.devices
|
|
2064
2064
|
: targetDevices;
|
|
2065
2065
|
|
|
2066
|
-
|
|
2066
|
+
// separator removed
|
|
2067
2067
|
console.log(`REPLAYING: ${recording.name}`);
|
|
2068
2068
|
console.log(`Original devices: ${devices.join(', ')}`);
|
|
2069
2069
|
console.log(`Target devices: ${targetDevices.join(', ')}`);
|
|
2070
2070
|
console.log(`Commands to replay: ${commands.length}`);
|
|
2071
|
-
|
|
2071
|
+
// separator removed
|
|
2072
2072
|
|
|
2073
2073
|
// Reset abort flag at start
|
|
2074
2074
|
replayAborted = false;
|
|
@@ -2149,10 +2149,10 @@ async function replayRecording(filename, targetDevices, wsClient) {
|
|
|
2149
2149
|
|
|
2150
2150
|
isReplaying = false;
|
|
2151
2151
|
|
|
2152
|
-
|
|
2152
|
+
// separator removed
|
|
2153
2153
|
console.log(`REPLAY COMPLETE`);
|
|
2154
2154
|
console.log(`Total: ${results.length}, Success: ${results.filter(r => r.success).length}, Failed: ${results.filter(r => !r.success).length}`);
|
|
2155
|
-
|
|
2155
|
+
// separator removed
|
|
2156
2156
|
|
|
2157
2157
|
return {
|
|
2158
2158
|
success: true,
|
|
@@ -2555,7 +2555,7 @@ app.post('/api/locators', async (req, res) => {
|
|
|
2555
2555
|
return res.status(400).json({ success: false, error: 'Device name required' });
|
|
2556
2556
|
}
|
|
2557
2557
|
|
|
2558
|
-
|
|
2558
|
+
logger.debug(`Getting locators for device: ${deviceName}`);
|
|
2559
2559
|
|
|
2560
2560
|
// Find the device info
|
|
2561
2561
|
const device = connectedDevices.find(d => d.name === deviceName);
|
|
@@ -2592,8 +2592,8 @@ app.post('/api/locators', async (req, res) => {
|
|
|
2592
2592
|
// Execute getLocators command - this uses the fast shell script implementation
|
|
2593
2593
|
const result = await executeCommand([deviceName], 'getLocators', null);
|
|
2594
2594
|
|
|
2595
|
-
|
|
2596
|
-
|
|
2595
|
+
logger.debug(`Result success: ${result.success}`);
|
|
2596
|
+
logger.debug(`Results array length: ${result.results?.length || 0}`);
|
|
2597
2597
|
|
|
2598
2598
|
if (result.success && result.results) {
|
|
2599
2599
|
let output = '';
|
|
@@ -2604,18 +2604,18 @@ app.post('/api/locators', async (req, res) => {
|
|
|
2604
2604
|
// allSettledResults contains: { status: 'fulfilled', value: { success, output, device } }
|
|
2605
2605
|
|
|
2606
2606
|
for (const scriptResult of result.results) {
|
|
2607
|
-
|
|
2607
|
+
logger.debug(`Script result:`, scriptResult);
|
|
2608
2608
|
|
|
2609
2609
|
// Check if scriptResult has value.results (fulfilled promise)
|
|
2610
2610
|
if (scriptResult.status === 'fulfilled' && scriptResult.value && scriptResult.value.results) {
|
|
2611
2611
|
// This is the result from executeAndroidDevices/executeWithScript
|
|
2612
2612
|
for (const r of scriptResult.value.results) {
|
|
2613
|
-
|
|
2613
|
+
logger.debug(`Result status: ${r.status}`);
|
|
2614
2614
|
if (r.status === 'fulfilled' && r.value) {
|
|
2615
2615
|
// Check if it's a successful execution
|
|
2616
2616
|
if (r.value.success && r.value.output) {
|
|
2617
2617
|
output = r.value.output;
|
|
2618
|
-
|
|
2618
|
+
logger.debug(`Found output: ${output.length} characters`);
|
|
2619
2619
|
break;
|
|
2620
2620
|
} else if (r.value.error) {
|
|
2621
2621
|
// Script executed but returned an error
|
|
@@ -2693,11 +2693,11 @@ app.post('/api/locators/click', async (req, res) => {
|
|
|
2693
2693
|
|
|
2694
2694
|
// Helper function to parse locators output
|
|
2695
2695
|
function parseLocatorsOutput(output) {
|
|
2696
|
-
|
|
2697
|
-
|
|
2696
|
+
logger.debug('🔍 Parsing locators output...');
|
|
2697
|
+
logger.debug('Output length:', output?.length || 0);
|
|
2698
2698
|
|
|
2699
2699
|
if (!output) {
|
|
2700
|
-
|
|
2700
|
+
logger.error('❌ No output to parse');
|
|
2701
2701
|
return [];
|
|
2702
2702
|
}
|
|
2703
2703
|
|
|
@@ -2708,19 +2708,19 @@ function parseLocatorsOutput(output) {
|
|
|
2708
2708
|
if (trimmed.startsWith('[') || trimmed.startsWith('{')) {
|
|
2709
2709
|
try {
|
|
2710
2710
|
const jsonData = JSON.parse(trimmed);
|
|
2711
|
-
|
|
2711
|
+
logger.debug('✅ Parsed as JSON, elements:', Array.isArray(jsonData) ? jsonData.length : 'not array');
|
|
2712
2712
|
return Array.isArray(jsonData) ? jsonData : [];
|
|
2713
2713
|
} catch (err) {
|
|
2714
|
-
|
|
2714
|
+
logger.debug('⚠️ JSON parse failed:', err.message);
|
|
2715
2715
|
}
|
|
2716
2716
|
}
|
|
2717
2717
|
}
|
|
2718
2718
|
|
|
2719
|
-
|
|
2719
|
+
logger.debug('📝 Fallback to text parsing...');
|
|
2720
2720
|
|
|
2721
2721
|
// Fallback to old text parsing
|
|
2722
2722
|
const locators = [];
|
|
2723
|
-
|
|
2723
|
+
logger.debug('Total lines:', lines.length);
|
|
2724
2724
|
|
|
2725
2725
|
for (const line of lines) {
|
|
2726
2726
|
// New format: " 1. 📍 ButtonName │ [Button] │ Label:LabelText │ Val:ValueText │ @ (x,y) WxH │ ✓Vis ✓En"
|
|
@@ -2822,7 +2822,7 @@ function parseLocatorsOutput(output) {
|
|
|
2822
2822
|
|
|
2823
2823
|
// Only add if we have meaningful data
|
|
2824
2824
|
if (locator.name || locator.label || locator.text) {
|
|
2825
|
-
|
|
2825
|
+
logger.debug('Parsed locator:', locator);
|
|
2826
2826
|
locators.push(locator);
|
|
2827
2827
|
}
|
|
2828
2828
|
} catch (e) {
|
|
@@ -2848,13 +2848,13 @@ wss.on('connection', (ws) => {
|
|
|
2848
2848
|
case 'execute_command':
|
|
2849
2849
|
const { devices, command, useAI, aiProvider } = data;
|
|
2850
2850
|
|
|
2851
|
-
|
|
2851
|
+
// separator removed
|
|
2852
2852
|
console.log(`WEBSOCKET: Received execute_command`);
|
|
2853
2853
|
console.log(`Devices received: [${devices.join(', ')}]`);
|
|
2854
2854
|
console.log(`Command: "${command}"`);
|
|
2855
2855
|
console.log(`Use AI: ${useAI}`);
|
|
2856
2856
|
if (aiProvider) console.log(`AI Provider: ${aiProvider}`);
|
|
2857
|
-
|
|
2857
|
+
// separator removed
|
|
2858
2858
|
|
|
2859
2859
|
let finalCommand = command;
|
|
2860
2860
|
|
|
@@ -3469,12 +3469,12 @@ function startServer(options = {}) {
|
|
|
3469
3469
|
|
|
3470
3470
|
return new Promise((resolve, reject) => {
|
|
3471
3471
|
server = app.listen(port, () => {
|
|
3472
|
-
console.log(
|
|
3473
|
-
console.log(
|
|
3472
|
+
console.log(`✅ Server running on port ${port}`);
|
|
3473
|
+
console.log(`✅ WebSocket server ready`);
|
|
3474
3474
|
|
|
3475
3475
|
// Initial device discovery
|
|
3476
3476
|
discoverDevices().then(devices => {
|
|
3477
|
-
console.log(
|
|
3477
|
+
console.log(`✅ Discovered ${devices.length} devices`);
|
|
3478
3478
|
|
|
3479
3479
|
// Open browser if not disabled
|
|
3480
3480
|
if (shouldOpenBrowser) {
|
|
@@ -3485,17 +3485,30 @@ function startServer(options = {}) {
|
|
|
3485
3485
|
});
|
|
3486
3486
|
}
|
|
3487
3487
|
|
|
3488
|
-
resolve(
|
|
3489
|
-
}).catch(
|
|
3488
|
+
resolve();
|
|
3489
|
+
}).catch(err => {
|
|
3490
|
+
logger.error('Device discovery failed:', err.message);
|
|
3491
|
+
resolve();
|
|
3492
|
+
});
|
|
3490
3493
|
});
|
|
3491
3494
|
|
|
3495
|
+
// Handle WebSocket upgrades
|
|
3492
3496
|
server.on('upgrade', (request, socket, head) => {
|
|
3493
3497
|
wss.handleUpgrade(request, socket, head, (ws) => {
|
|
3494
3498
|
wss.emit('connection', ws, request);
|
|
3495
3499
|
});
|
|
3496
3500
|
});
|
|
3497
3501
|
|
|
3498
|
-
server.on('error',
|
|
3502
|
+
server.on('error', (err) => {
|
|
3503
|
+
if (err.code === 'EADDRINUSE') {
|
|
3504
|
+
logger.error(`\n❌ Port ${port} is already in use!`);
|
|
3505
|
+
logger.error(`\nTry one of these solutions:`);
|
|
3506
|
+
logger.error(` 1. Kill the process using port ${port}: lsof -ti:${port} | xargs kill -9`);
|
|
3507
|
+
logger.error(` 2. Use a different port: PORT=3002 devicely`);
|
|
3508
|
+
logger.error(` 3. Set PORT in .env file\n`);
|
|
3509
|
+
}
|
|
3510
|
+
reject(err);
|
|
3511
|
+
});
|
|
3499
3512
|
});
|
|
3500
3513
|
}
|
|
3501
3514
|
|
|
@@ -3518,7 +3531,16 @@ module.exports = {
|
|
|
3518
3531
|
// Start server if run directly (not imported as module)
|
|
3519
3532
|
if (require.main === module) {
|
|
3520
3533
|
startServer().catch(err => {
|
|
3521
|
-
|
|
3534
|
+
if (err.code === 'EADDRINUSE') {
|
|
3535
|
+
logger.error(`\n❌ Port 3001 is already in use!`);
|
|
3536
|
+
logger.error(`\nTry one of these solutions:`);
|
|
3537
|
+
logger.error(` 1. Kill the process using port 3001: lsof -ti:3001 | xargs kill -9`);
|
|
3538
|
+
logger.error(` 2. Use a different port: PORT=3002 devicely`);
|
|
3539
|
+
logger.error(` Note: Vercel frontend connects to port 3001 by default`);
|
|
3540
|
+
logger.error(` 3. Use local frontend at http://localhost:3001\n`);
|
|
3541
|
+
} else {
|
|
3542
|
+
console.error('Failed to start server:', err.message);
|
|
3543
|
+
}
|
|
3522
3544
|
process.exit(1);
|
|
3523
3545
|
});
|
|
3524
3546
|
}
|