@testdroid-ai/cli 0.1.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.
- package/dist/adb/adb-path-resolver.d.ts +6 -0
- package/dist/adb/adb-path-resolver.d.ts.map +1 -0
- package/dist/adb/adb-path-resolver.js +93 -0
- package/dist/adb/adb-path-resolver.js.map +1 -0
- package/dist/adb/adb-shell.d.ts +29 -0
- package/dist/adb/adb-shell.d.ts.map +1 -0
- package/dist/adb/adb-shell.js +128 -0
- package/dist/adb/adb-shell.js.map +1 -0
- package/dist/adb/detector.d.ts +12 -0
- package/dist/adb/detector.d.ts.map +1 -0
- package/dist/adb/detector.js +44 -0
- package/dist/adb/detector.js.map +1 -0
- package/dist/adb/device-tracker.d.ts +23 -0
- package/dist/adb/device-tracker.d.ts.map +1 -0
- package/dist/adb/device-tracker.js +134 -0
- package/dist/adb/device-tracker.js.map +1 -0
- package/dist/adb/emulator-detector.d.ts +11 -0
- package/dist/adb/emulator-detector.d.ts.map +1 -0
- package/dist/adb/emulator-detector.js +54 -0
- package/dist/adb/emulator-detector.js.map +1 -0
- package/dist/appium/health-check.d.ts +6 -0
- package/dist/appium/health-check.d.ts.map +1 -0
- package/dist/appium/health-check.js +61 -0
- package/dist/appium/health-check.js.map +1 -0
- package/dist/appium/session-manager.d.ts +15 -0
- package/dist/appium/session-manager.d.ts.map +1 -0
- package/dist/appium/session-manager.js +82 -0
- package/dist/appium/session-manager.js.map +1 -0
- package/dist/auth/credential-store.d.ts +31 -0
- package/dist/auth/credential-store.d.ts.map +1 -0
- package/dist/auth/credential-store.js +154 -0
- package/dist/auth/credential-store.js.map +1 -0
- package/dist/auth/pairing-auth.d.ts +26 -0
- package/dist/auth/pairing-auth.d.ts.map +1 -0
- package/dist/auth/pairing-auth.js +177 -0
- package/dist/auth/pairing-auth.js.map +1 -0
- package/dist/auth/token-manager.d.ts +20 -0
- package/dist/auth/token-manager.d.ts.map +1 -0
- package/dist/auth/token-manager.js +101 -0
- package/dist/auth/token-manager.js.map +1 -0
- package/dist/commands/connect.d.ts +9 -0
- package/dist/commands/connect.d.ts.map +1 -0
- package/dist/commands/connect.js +270 -0
- package/dist/commands/connect.js.map +1 -0
- package/dist/commands/devices.d.ts +2 -0
- package/dist/commands/devices.d.ts.map +1 -0
- package/dist/commands/devices.js +52 -0
- package/dist/commands/devices.js.map +1 -0
- package/dist/commands/doctor.d.ts +2 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +214 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/mirror.d.ts +2 -0
- package/dist/commands/mirror.d.ts.map +1 -0
- package/dist/commands/mirror.js +77 -0
- package/dist/commands/mirror.js.map +1 -0
- package/dist/commands/prompt.d.ts +12 -0
- package/dist/commands/prompt.d.ts.map +1 -0
- package/dist/commands/prompt.js +174 -0
- package/dist/commands/prompt.js.map +1 -0
- package/dist/commands/record.d.ts +7 -0
- package/dist/commands/record.d.ts.map +1 -0
- package/dist/commands/record.js +129 -0
- package/dist/commands/record.js.map +1 -0
- package/dist/commands/run.d.ts +16 -0
- package/dist/commands/run.d.ts.map +1 -0
- package/dist/commands/run.js +271 -0
- package/dist/commands/run.js.map +1 -0
- package/dist/commands/setup.d.ts +6 -0
- package/dist/commands/setup.d.ts.map +1 -0
- package/dist/commands/setup.js +50 -0
- package/dist/commands/setup.js.map +1 -0
- package/dist/commands/status.d.ts +2 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +32 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/healing/ai-adapter.d.ts +44 -0
- package/dist/healing/ai-adapter.d.ts.map +1 -0
- package/dist/healing/ai-adapter.js +87 -0
- package/dist/healing/ai-adapter.js.map +1 -0
- package/dist/healing/healer.d.ts +109 -0
- package/dist/healing/healer.d.ts.map +1 -0
- package/dist/healing/healer.js +475 -0
- package/dist/healing/healer.js.map +1 -0
- package/dist/healing/similarity.d.ts +63 -0
- package/dist/healing/similarity.d.ts.map +1 -0
- package/dist/healing/similarity.js +172 -0
- package/dist/healing/similarity.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +107 -0
- package/dist/index.js.map +1 -0
- package/dist/recording/capture-strategies/capture-strategy.interface.d.ts +6 -0
- package/dist/recording/capture-strategies/capture-strategy.interface.d.ts.map +1 -0
- package/dist/recording/capture-strategies/capture-strategy.interface.js +3 -0
- package/dist/recording/capture-strategies/capture-strategy.interface.js.map +1 -0
- package/dist/recording/capture-strategies/getevent.strategy.d.ts +27 -0
- package/dist/recording/capture-strategies/getevent.strategy.d.ts.map +1 -0
- package/dist/recording/capture-strategies/getevent.strategy.js +163 -0
- package/dist/recording/capture-strategies/getevent.strategy.js.map +1 -0
- package/dist/recording/capture-strategies/key-capture.strategy.d.ts +16 -0
- package/dist/recording/capture-strategies/key-capture.strategy.d.ts.map +1 -0
- package/dist/recording/capture-strategies/key-capture.strategy.js +67 -0
- package/dist/recording/capture-strategies/key-capture.strategy.js.map +1 -0
- package/dist/recording/capture-strategies/logcat-capture.strategy.d.ts +32 -0
- package/dist/recording/capture-strategies/logcat-capture.strategy.d.ts.map +1 -0
- package/dist/recording/capture-strategies/logcat-capture.strategy.js +176 -0
- package/dist/recording/capture-strategies/logcat-capture.strategy.js.map +1 -0
- package/dist/recording/confidence-scorer.d.ts +13 -0
- package/dist/recording/confidence-scorer.d.ts.map +1 -0
- package/dist/recording/confidence-scorer.js +34 -0
- package/dist/recording/confidence-scorer.js.map +1 -0
- package/dist/recording/event-capture.d.ts +40 -0
- package/dist/recording/event-capture.d.ts.map +1 -0
- package/dist/recording/event-capture.js +168 -0
- package/dist/recording/event-capture.js.map +1 -0
- package/dist/recording/recorder.d.ts +32 -0
- package/dist/recording/recorder.d.ts.map +1 -0
- package/dist/recording/recorder.js +107 -0
- package/dist/recording/recorder.js.map +1 -0
- package/dist/runner/adb-executor.d.ts +116 -0
- package/dist/runner/adb-executor.d.ts.map +1 -0
- package/dist/runner/adb-executor.js +323 -0
- package/dist/runner/adb-executor.js.map +1 -0
- package/dist/runner/appium-executor.d.ts +35 -0
- package/dist/runner/appium-executor.d.ts.map +1 -0
- package/dist/runner/appium-executor.js +191 -0
- package/dist/runner/appium-executor.js.map +1 -0
- package/dist/runner/parse-strategies/appium-default.strategy.d.ts +12 -0
- package/dist/runner/parse-strategies/appium-default.strategy.d.ts.map +1 -0
- package/dist/runner/parse-strategies/appium-default.strategy.js +342 -0
- package/dist/runner/parse-strategies/appium-default.strategy.js.map +1 -0
- package/dist/runner/parse-strategies/index.d.ts +7 -0
- package/dist/runner/parse-strategies/index.d.ts.map +1 -0
- package/dist/runner/parse-strategies/index.js +14 -0
- package/dist/runner/parse-strategies/index.js.map +1 -0
- package/dist/runner/parse-strategies/java.strategy.d.ts +8 -0
- package/dist/runner/parse-strategies/java.strategy.d.ts.map +1 -0
- package/dist/runner/parse-strategies/java.strategy.js +170 -0
- package/dist/runner/parse-strategies/java.strategy.js.map +1 -0
- package/dist/runner/parse-strategies/maestro.strategy.d.ts +8 -0
- package/dist/runner/parse-strategies/maestro.strategy.d.ts.map +1 -0
- package/dist/runner/parse-strategies/maestro.strategy.js +105 -0
- package/dist/runner/parse-strategies/maestro.strategy.js.map +1 -0
- package/dist/runner/parse-strategies/python.strategy.d.ts +8 -0
- package/dist/runner/parse-strategies/python.strategy.d.ts.map +1 -0
- package/dist/runner/parse-strategies/python.strategy.js +140 -0
- package/dist/runner/parse-strategies/python.strategy.js.map +1 -0
- package/dist/runner/parse-strategies/robot.strategy.d.ts +8 -0
- package/dist/runner/parse-strategies/robot.strategy.d.ts.map +1 -0
- package/dist/runner/parse-strategies/robot.strategy.js +81 -0
- package/dist/runner/parse-strategies/robot.strategy.js.map +1 -0
- package/dist/runner/parse-strategies/types.d.ts +17 -0
- package/dist/runner/parse-strategies/types.d.ts.map +1 -0
- package/dist/runner/parse-strategies/types.js +3 -0
- package/dist/runner/parse-strategies/types.js.map +1 -0
- package/dist/runner/parse-strategies/utils.d.ts +39 -0
- package/dist/runner/parse-strategies/utils.d.ts.map +1 -0
- package/dist/runner/parse-strategies/utils.js +217 -0
- package/dist/runner/parse-strategies/utils.js.map +1 -0
- package/dist/runner/script-parser.d.ts +43 -0
- package/dist/runner/script-parser.d.ts.map +1 -0
- package/dist/runner/script-parser.js +68 -0
- package/dist/runner/script-parser.js.map +1 -0
- package/dist/runner/selector-resolver.d.ts +87 -0
- package/dist/runner/selector-resolver.d.ts.map +1 -0
- package/dist/runner/selector-resolver.js +206 -0
- package/dist/runner/selector-resolver.js.map +1 -0
- package/dist/runner/step-executor.d.ts +112 -0
- package/dist/runner/step-executor.d.ts.map +1 -0
- package/dist/runner/step-executor.js +518 -0
- package/dist/runner/step-executor.js.map +1 -0
- package/dist/runner/test-runner.d.ts +89 -0
- package/dist/runner/test-runner.d.ts.map +1 -0
- package/dist/runner/test-runner.js +320 -0
- package/dist/runner/test-runner.js.map +1 -0
- package/dist/scrcpy/launcher.d.ts +16 -0
- package/dist/scrcpy/launcher.d.ts.map +1 -0
- package/dist/scrcpy/launcher.js +87 -0
- package/dist/scrcpy/launcher.js.map +1 -0
- package/dist/services/device-poller.d.ts +10 -0
- package/dist/services/device-poller.d.ts.map +1 -0
- package/dist/services/device-poller.js +44 -0
- package/dist/services/device-poller.js.map +1 -0
- package/dist/services/execution-manager.d.ts +18 -0
- package/dist/services/execution-manager.d.ts.map +1 -0
- package/dist/services/execution-manager.js +185 -0
- package/dist/services/execution-manager.js.map +1 -0
- package/dist/services/recording-manager.d.ts +13 -0
- package/dist/services/recording-manager.d.ts.map +1 -0
- package/dist/services/recording-manager.js +234 -0
- package/dist/services/recording-manager.js.map +1 -0
- package/dist/setup/adb-installer.d.ts +8 -0
- package/dist/setup/adb-installer.d.ts.map +1 -0
- package/dist/setup/adb-installer.js +115 -0
- package/dist/setup/adb-installer.js.map +1 -0
- package/dist/setup/appium-installer.d.ts +8 -0
- package/dist/setup/appium-installer.d.ts.map +1 -0
- package/dist/setup/appium-installer.js +113 -0
- package/dist/setup/appium-installer.js.map +1 -0
- package/dist/setup/base-installer.d.ts +33 -0
- package/dist/setup/base-installer.d.ts.map +1 -0
- package/dist/setup/base-installer.js +170 -0
- package/dist/setup/base-installer.js.map +1 -0
- package/dist/setup/build-tools-installer.d.ts +8 -0
- package/dist/setup/build-tools-installer.d.ts.map +1 -0
- package/dist/setup/build-tools-installer.js +109 -0
- package/dist/setup/build-tools-installer.js.map +1 -0
- package/dist/setup/java-installer.d.ts +8 -0
- package/dist/setup/java-installer.d.ts.map +1 -0
- package/dist/setup/java-installer.js +132 -0
- package/dist/setup/java-installer.js.map +1 -0
- package/dist/setup/paths.d.ts +12 -0
- package/dist/setup/paths.d.ts.map +1 -0
- package/dist/setup/paths.js +90 -0
- package/dist/setup/paths.js.map +1 -0
- package/dist/setup/scrcpy-installer.d.ts +8 -0
- package/dist/setup/scrcpy-installer.d.ts.map +1 -0
- package/dist/setup/scrcpy-installer.js +143 -0
- package/dist/setup/scrcpy-installer.js.map +1 -0
- package/dist/setup/setup-manager.d.ts +18 -0
- package/dist/setup/setup-manager.d.ts.map +1 -0
- package/dist/setup/setup-manager.js +113 -0
- package/dist/setup/setup-manager.js.map +1 -0
- package/dist/ws/socket.d.ts +38 -0
- package/dist/ws/socket.d.ts.map +1 -0
- package/dist/ws/socket.js +91 -0
- package/dist/ws/socket.js.map +1 -0
- package/package.json +53 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { RecordingEvent } from '@testdroid-ai/shared';
|
|
2
|
+
import { CaptureStrategy } from './capture-strategy.interface';
|
|
3
|
+
/**
|
|
4
|
+
* Captures user interactions via the TestDroid accessibility service APK.
|
|
5
|
+
*
|
|
6
|
+
* The APK (com.testdroid.capture) is an AccessibilityService that logs
|
|
7
|
+
* every user interaction to logcat with tag TESTDROID_CAPTURE.
|
|
8
|
+
*
|
|
9
|
+
* Format: EVENT|action|text|resourceId|className|bounds|packageName|inputText
|
|
10
|
+
*
|
|
11
|
+
* This is the PRIMARY capture strategy — it captures ALL interaction types:
|
|
12
|
+
* tap, long_press, type_text, swipe, navigate, back.
|
|
13
|
+
*/
|
|
14
|
+
export declare class LogcatCaptureStrategy implements CaptureStrategy {
|
|
15
|
+
private serial;
|
|
16
|
+
private process;
|
|
17
|
+
private onEventCb;
|
|
18
|
+
private typingTimer;
|
|
19
|
+
private typingEvent;
|
|
20
|
+
private lastEventKey;
|
|
21
|
+
private lastEventTime;
|
|
22
|
+
private currentPackage;
|
|
23
|
+
constructor(serial: string);
|
|
24
|
+
start(onEvent: (event: RecordingEvent) => void): Promise<void>;
|
|
25
|
+
stop(): void;
|
|
26
|
+
/** Current foreground package (from last event) */
|
|
27
|
+
getPackage(): string;
|
|
28
|
+
private parseLine;
|
|
29
|
+
private emit;
|
|
30
|
+
private decodeEntities;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=logcat-capture.strategy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logcat-capture.strategy.d.ts","sourceRoot":"","sources":["../../../src/recording/capture-strategies/logcat-capture.strategy.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAsB,MAAM,sBAAsB,CAAC;AAC1E,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAG/D;;;;;;;;;;GAUG;AACH,qBAAa,qBAAsB,YAAW,eAAe;IAe/C,OAAO,CAAC,MAAM;IAd1B,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,SAAS,CAAkD;IAGnE,OAAO,CAAC,WAAW,CAA8C;IACjE,OAAO,CAAC,WAAW,CAA+B;IAGlD,OAAO,CAAC,YAAY,CAAM;IAC1B,OAAO,CAAC,aAAa,CAAK;IAG1B,OAAO,CAAC,cAAc,CAAM;gBAER,MAAM,EAAE,MAAM;IAE5B,KAAK,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IA2BpE,IAAI,IAAI,IAAI;IAeZ,mDAAmD;IACnD,UAAU,IAAI,MAAM;IAEpB,OAAO,CAAC,SAAS;IA4FjB,OAAO,CAAC,IAAI;IAKZ,OAAO,CAAC,cAAc;CAUvB"}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LogcatCaptureStrategy = void 0;
|
|
4
|
+
const child_process_1 = require("child_process");
|
|
5
|
+
const confidence_scorer_1 = require("../confidence-scorer");
|
|
6
|
+
/**
|
|
7
|
+
* Captures user interactions via the TestDroid accessibility service APK.
|
|
8
|
+
*
|
|
9
|
+
* The APK (com.testdroid.capture) is an AccessibilityService that logs
|
|
10
|
+
* every user interaction to logcat with tag TESTDROID_CAPTURE.
|
|
11
|
+
*
|
|
12
|
+
* Format: EVENT|action|text|resourceId|className|bounds|packageName|inputText
|
|
13
|
+
*
|
|
14
|
+
* This is the PRIMARY capture strategy — it captures ALL interaction types:
|
|
15
|
+
* tap, long_press, type_text, swipe, navigate, back.
|
|
16
|
+
*/
|
|
17
|
+
class LogcatCaptureStrategy {
|
|
18
|
+
serial;
|
|
19
|
+
process = null;
|
|
20
|
+
onEventCb = null;
|
|
21
|
+
// Typing debounce: accumulate text, emit once when typing stops
|
|
22
|
+
typingTimer = null;
|
|
23
|
+
typingEvent = null;
|
|
24
|
+
// Dedup: skip duplicate events within 150ms (same action on same element)
|
|
25
|
+
lastEventKey = '';
|
|
26
|
+
lastEventTime = 0;
|
|
27
|
+
// Track current package for appPackage detection
|
|
28
|
+
currentPackage = '';
|
|
29
|
+
constructor(serial) {
|
|
30
|
+
this.serial = serial;
|
|
31
|
+
}
|
|
32
|
+
async start(onEvent) {
|
|
33
|
+
this.onEventCb = onEvent;
|
|
34
|
+
// Clear logcat buffer
|
|
35
|
+
try {
|
|
36
|
+
(0, child_process_1.execFileSync)('adb', ['-s', this.serial, 'logcat', '-c'], { stdio: 'pipe', timeout: 3000 });
|
|
37
|
+
}
|
|
38
|
+
catch { }
|
|
39
|
+
// Start logcat monitor
|
|
40
|
+
this.process = (0, child_process_1.spawn)('adb', [
|
|
41
|
+
'-s', this.serial, 'logcat', '-s', 'TESTDROID_CAPTURE:I',
|
|
42
|
+
], { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
43
|
+
let buffer = '';
|
|
44
|
+
this.process.stdout?.on('data', (data) => {
|
|
45
|
+
buffer += data.toString();
|
|
46
|
+
const lines = buffer.split('\n');
|
|
47
|
+
buffer = lines.pop() || '';
|
|
48
|
+
for (const line of lines) {
|
|
49
|
+
this.parseLine(line.trim());
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
this.process.stderr?.on('data', () => { });
|
|
53
|
+
this.process.on('error', () => { });
|
|
54
|
+
}
|
|
55
|
+
stop() {
|
|
56
|
+
// Flush pending typing
|
|
57
|
+
if (this.typingEvent) {
|
|
58
|
+
clearTimeout(this.typingTimer);
|
|
59
|
+
this.emit(this.typingEvent);
|
|
60
|
+
this.typingEvent = null;
|
|
61
|
+
this.typingTimer = null;
|
|
62
|
+
}
|
|
63
|
+
// Kill logcat process
|
|
64
|
+
if (this.process) {
|
|
65
|
+
try {
|
|
66
|
+
this.process.kill('SIGKILL');
|
|
67
|
+
}
|
|
68
|
+
catch { }
|
|
69
|
+
this.process = null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/** Current foreground package (from last event) */
|
|
73
|
+
getPackage() { return this.currentPackage; }
|
|
74
|
+
parseLine(line) {
|
|
75
|
+
const match = line.match(/TESTDROID_CAPTURE:\s*EVENT\|([^|]*)\|([^|]*)\|([^|]*)\|([^|]*)\|([^|]*)\|([^|]*)\|([^|]*)/);
|
|
76
|
+
if (!match)
|
|
77
|
+
return;
|
|
78
|
+
const [, action, text, resourceId, className, bounds, packageName, inputText] = match;
|
|
79
|
+
// Map action to RecordingEventType
|
|
80
|
+
const typeMap = {
|
|
81
|
+
tap: 'tap',
|
|
82
|
+
long_press: 'long_press',
|
|
83
|
+
type_text: 'type_text',
|
|
84
|
+
swipe: 'scroll',
|
|
85
|
+
navigate: 'window_changed',
|
|
86
|
+
notification: 'window_changed',
|
|
87
|
+
};
|
|
88
|
+
const eventType = typeMap[action];
|
|
89
|
+
if (!eventType)
|
|
90
|
+
return;
|
|
91
|
+
// Skip system UI (status bar, keyboard)
|
|
92
|
+
if (packageName === 'com.android.systemui')
|
|
93
|
+
return;
|
|
94
|
+
if (packageName?.startsWith('com.google.android.inputmethod'))
|
|
95
|
+
return;
|
|
96
|
+
if (packageName?.startsWith('com.android.inputmethod'))
|
|
97
|
+
return;
|
|
98
|
+
// Track package
|
|
99
|
+
if (packageName && packageName !== 'com.android.systemui') {
|
|
100
|
+
this.currentPackage = packageName;
|
|
101
|
+
}
|
|
102
|
+
// Parse bounds center
|
|
103
|
+
let x;
|
|
104
|
+
let y;
|
|
105
|
+
if (bounds) {
|
|
106
|
+
const parts = bounds.split(',').map(Number);
|
|
107
|
+
if (parts.length === 4 && !isNaN(parts[0])) {
|
|
108
|
+
x = Math.round((parts[0] + parts[2]) / 2);
|
|
109
|
+
y = Math.round((parts[1] + parts[3]) / 2);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
// Decode HTML entities in text
|
|
113
|
+
const cleanText = text ? this.decodeEntities(text) : undefined;
|
|
114
|
+
// Build event
|
|
115
|
+
const event = {
|
|
116
|
+
type: eventType,
|
|
117
|
+
timestamp: Date.now(),
|
|
118
|
+
x, y,
|
|
119
|
+
elementId: resourceId || undefined,
|
|
120
|
+
elementText: cleanText,
|
|
121
|
+
elementClass: className || undefined,
|
|
122
|
+
elementBounds: bounds || undefined,
|
|
123
|
+
};
|
|
124
|
+
// Handle type_text: debounce, emit final text
|
|
125
|
+
if (action === 'type_text') {
|
|
126
|
+
const typedText = inputText ? inputText.replace(/^\[/, '').replace(/\]$/, '') : cleanText;
|
|
127
|
+
event.text = typedText;
|
|
128
|
+
// Check for password field
|
|
129
|
+
if (typedText && /[\u2022\u25CF*]/.test(typedText)) {
|
|
130
|
+
event.text = '<PASSWORD>';
|
|
131
|
+
}
|
|
132
|
+
this.typingEvent = event;
|
|
133
|
+
if (this.typingTimer)
|
|
134
|
+
clearTimeout(this.typingTimer);
|
|
135
|
+
this.typingTimer = setTimeout(() => {
|
|
136
|
+
if (this.typingEvent) {
|
|
137
|
+
this.emit(this.typingEvent);
|
|
138
|
+
this.typingEvent = null;
|
|
139
|
+
}
|
|
140
|
+
}, 1500);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
// Non-typing event: flush pending typing first
|
|
144
|
+
if (this.typingEvent) {
|
|
145
|
+
clearTimeout(this.typingTimer);
|
|
146
|
+
this.emit(this.typingEvent);
|
|
147
|
+
this.typingEvent = null;
|
|
148
|
+
this.typingTimer = null;
|
|
149
|
+
}
|
|
150
|
+
// Dedup: skip if same action+element within 150ms
|
|
151
|
+
const eventKey = `${action}|${resourceId}|${cleanText}`;
|
|
152
|
+
const now = Date.now();
|
|
153
|
+
if (eventKey === this.lastEventKey && now - this.lastEventTime < 150)
|
|
154
|
+
return;
|
|
155
|
+
this.lastEventKey = eventKey;
|
|
156
|
+
this.lastEventTime = now;
|
|
157
|
+
this.emit(event);
|
|
158
|
+
}
|
|
159
|
+
emit(event) {
|
|
160
|
+
event.confidence = (0, confidence_scorer_1.scoreConfidence)(event);
|
|
161
|
+
if (this.onEventCb)
|
|
162
|
+
this.onEventCb(event);
|
|
163
|
+
}
|
|
164
|
+
decodeEntities(text) {
|
|
165
|
+
return text
|
|
166
|
+
.replace(/&#(\d+);/g, (_, code) => String.fromCodePoint(parseInt(code, 10)))
|
|
167
|
+
.replace(/&#x([0-9a-fA-F]+);/g, (_, hex) => String.fromCodePoint(parseInt(hex, 16)))
|
|
168
|
+
.replace(/&/g, '&')
|
|
169
|
+
.replace(/</g, '<')
|
|
170
|
+
.replace(/>/g, '>')
|
|
171
|
+
.replace(/"/g, '"')
|
|
172
|
+
.replace(/'/g, "'");
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
exports.LogcatCaptureStrategy = LogcatCaptureStrategy;
|
|
176
|
+
//# sourceMappingURL=logcat-capture.strategy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logcat-capture.strategy.js","sourceRoot":"","sources":["../../../src/recording/capture-strategies/logcat-capture.strategy.ts"],"names":[],"mappings":";;;AAAA,iDAAkE;AAGlE,4DAAuD;AAEvD;;;;;;;;;;GAUG;AACH,MAAa,qBAAqB;IAeZ;IAdZ,OAAO,GAAwB,IAAI,CAAC;IACpC,SAAS,GAA6C,IAAI,CAAC;IAEnE,gEAAgE;IACxD,WAAW,GAAyC,IAAI,CAAC;IACzD,WAAW,GAA0B,IAAI,CAAC;IAElD,0EAA0E;IAClE,YAAY,GAAG,EAAE,CAAC;IAClB,aAAa,GAAG,CAAC,CAAC;IAE1B,iDAAiD;IACzC,cAAc,GAAG,EAAE,CAAC;IAE5B,YAAoB,MAAc;QAAd,WAAM,GAAN,MAAM,CAAQ;IAAG,CAAC;IAEtC,KAAK,CAAC,KAAK,CAAC,OAAwC;QAClD,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC;QAEzB,sBAAsB;QACtB,IAAI,CAAC;YACH,IAAA,4BAAY,EAAC,KAAK,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7F,CAAC;QAAC,MAAM,CAAC,CAAA,CAAC;QAEV,uBAAuB;QACvB,IAAI,CAAC,OAAO,GAAG,IAAA,qBAAK,EAAC,KAAK,EAAE;YAC1B,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,qBAAqB;SACzD,EAAE,EAAE,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;QAE1C,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,IAAY,EAAE,EAAE;YAC/C,MAAM,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC1B,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACjC,MAAM,GAAG,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC;YAC3B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACzB,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;YAC9B,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QAC1C,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IACrC,CAAC;IAED,IAAI;QACF,uBAAuB;QACvB,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,YAAY,CAAC,IAAI,CAAC,WAAY,CAAC,CAAC;YAChC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YAC5B,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;YACxB,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QAC1B,CAAC;QACD,sBAAsB;QACtB,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,IAAI,CAAC;gBAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAA,CAAC;YAC9C,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACtB,CAAC;IACH,CAAC;IAED,mDAAmD;IACnD,UAAU,KAAa,OAAO,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC;IAE5C,SAAS,CAAC,IAAY;QAC5B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CACtB,2FAA2F,CAC5F,CAAC;QACF,IAAI,CAAC,KAAK;YAAE,OAAO;QAEnB,MAAM,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,CAAC,GAAG,KAAK,CAAC;QAEtF,mCAAmC;QACnC,MAAM,OAAO,GAAuC;YAClD,GAAG,EAAE,KAAK;YACV,UAAU,EAAE,YAAY;YACxB,SAAS,EAAE,WAAW;YACtB,KAAK,EAAE,QAAQ;YACf,QAAQ,EAAE,gBAAgB;YAC1B,YAAY,EAAE,gBAAgB;SAC/B,CAAC;QACF,MAAM,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;QAClC,IAAI,CAAC,SAAS;YAAE,OAAO;QAEvB,wCAAwC;QACxC,IAAI,WAAW,KAAK,sBAAsB;YAAE,OAAO;QACnD,IAAI,WAAW,EAAE,UAAU,CAAC,gCAAgC,CAAC;YAAE,OAAO;QACtE,IAAI,WAAW,EAAE,UAAU,CAAC,yBAAyB,CAAC;YAAE,OAAO;QAE/D,gBAAgB;QAChB,IAAI,WAAW,IAAI,WAAW,KAAK,sBAAsB,EAAE,CAAC;YAC1D,IAAI,CAAC,cAAc,GAAG,WAAW,CAAC;QACpC,CAAC;QAED,sBAAsB;QACtB,IAAI,CAAqB,CAAC;QAC1B,IAAI,CAAqB,CAAC;QAC1B,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YAC5C,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC3C,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;gBAC1C,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YAC5C,CAAC;QACH,CAAC;QAED,+BAA+B;QAC/B,MAAM,SAAS,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAE/D,cAAc;QACd,MAAM,KAAK,GAAmB;YAC5B,IAAI,EAAE,SAAS;YACf,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;YACrB,CAAC,EAAE,CAAC;YACJ,SAAS,EAAE,UAAU,IAAI,SAAS;YAClC,WAAW,EAAE,SAAS;YACtB,YAAY,EAAE,SAAS,IAAI,SAAS;YACpC,aAAa,EAAE,MAAM,IAAI,SAAS;SACnC,CAAC;QAEF,8CAA8C;QAC9C,IAAI,MAAM,KAAK,WAAW,EAAE,CAAC;YAC3B,MAAM,SAAS,GAAG,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YAC1F,KAAK,CAAC,IAAI,GAAG,SAAS,CAAC;YACvB,2BAA2B;YAC3B,IAAI,SAAS,IAAI,iBAAiB,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;gBACnD,KAAK,CAAC,IAAI,GAAG,YAAY,CAAC;YAC5B,CAAC;YACD,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;YACzB,IAAI,IAAI,CAAC,WAAW;gBAAE,YAAY,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YACrD,IAAI,CAAC,WAAW,GAAG,UAAU,CAAC,GAAG,EAAE;gBACjC,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;oBACrB,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;oBAC5B,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;gBAC1B,CAAC;YACH,CAAC,EAAE,IAAI,CAAC,CAAC;YACT,OAAO;QACT,CAAC;QAED,+CAA+C;QAC/C,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,YAAY,CAAC,IAAI,CAAC,WAAY,CAAC,CAAC;YAChC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YAC5B,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;YACxB,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QAC1B,CAAC;QAED,kDAAkD;QAClD,MAAM,QAAQ,GAAG,GAAG,MAAM,IAAI,UAAU,IAAI,SAAS,EAAE,CAAC;QACxD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,IAAI,QAAQ,KAAK,IAAI,CAAC,YAAY,IAAI,GAAG,GAAG,IAAI,CAAC,aAAa,GAAG,GAAG;YAAE,OAAO;QAC7E,IAAI,CAAC,YAAY,GAAG,QAAQ,CAAC;QAC7B,IAAI,CAAC,aAAa,GAAG,GAAG,CAAC;QAEzB,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACnB,CAAC;IAEO,IAAI,CAAC,KAAqB;QAChC,KAAK,CAAC,UAAU,GAAG,IAAA,mCAAe,EAAC,KAAK,CAAC,CAAC;QAC1C,IAAI,IAAI,CAAC,SAAS;YAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAC5C,CAAC;IAEO,cAAc,CAAC,IAAY;QACjC,OAAO,IAAI;aACR,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC;aAC3E,OAAO,CAAC,qBAAqB,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC;aACnF,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC;aACtB,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC;aACrB,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC;aACrB,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC;aACvB,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;IAC7B,CAAC;CACF;AAzKD,sDAyKC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { RecordingEvent } from '@testdroid-ai/shared';
|
|
2
|
+
/**
|
|
3
|
+
* Scores the confidence of a recording event's selectors.
|
|
4
|
+
* Higher score = more reliable for test generation and replay.
|
|
5
|
+
*
|
|
6
|
+
* 100: unique resourceId (most stable)
|
|
7
|
+
* 85: elementText exists and is non-empty
|
|
8
|
+
* 70: elementClass exists
|
|
9
|
+
* 50: only coordinates (fragile)
|
|
10
|
+
* 30: no actionable data
|
|
11
|
+
*/
|
|
12
|
+
export declare function scoreConfidence(event: RecordingEvent): number;
|
|
13
|
+
//# sourceMappingURL=confidence-scorer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"confidence-scorer.d.ts","sourceRoot":"","sources":["../../src/recording/confidence-scorer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,CAuB7D"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.scoreConfidence = scoreConfidence;
|
|
4
|
+
/**
|
|
5
|
+
* Scores the confidence of a recording event's selectors.
|
|
6
|
+
* Higher score = more reliable for test generation and replay.
|
|
7
|
+
*
|
|
8
|
+
* 100: unique resourceId (most stable)
|
|
9
|
+
* 85: elementText exists and is non-empty
|
|
10
|
+
* 70: elementClass exists
|
|
11
|
+
* 50: only coordinates (fragile)
|
|
12
|
+
* 30: no actionable data
|
|
13
|
+
*/
|
|
14
|
+
function scoreConfidence(event) {
|
|
15
|
+
// ResourceId is the most stable selector
|
|
16
|
+
if (event.elementId && event.elementId.includes(':id/')) {
|
|
17
|
+
return 100;
|
|
18
|
+
}
|
|
19
|
+
// Text is second best
|
|
20
|
+
if (event.elementText && event.elementText.length > 0) {
|
|
21
|
+
return 85;
|
|
22
|
+
}
|
|
23
|
+
// Class name gives some context
|
|
24
|
+
if (event.elementClass) {
|
|
25
|
+
return 70;
|
|
26
|
+
}
|
|
27
|
+
// Only coordinates -- fragile
|
|
28
|
+
if (event.x !== undefined && event.y !== undefined) {
|
|
29
|
+
return 50;
|
|
30
|
+
}
|
|
31
|
+
// Nothing useful
|
|
32
|
+
return 30;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=confidence-scorer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"confidence-scorer.js","sourceRoot":"","sources":["../../src/recording/confidence-scorer.ts"],"names":[],"mappings":";;AAYA,0CAuBC;AAjCD;;;;;;;;;GASG;AACH,SAAgB,eAAe,CAAC,KAAqB;IACnD,yCAAyC;IACzC,IAAI,KAAK,CAAC,SAAS,IAAI,KAAK,CAAC,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QACxD,OAAO,GAAG,CAAC;IACb,CAAC;IAED,sBAAsB;IACtB,IAAI,KAAK,CAAC,WAAW,IAAI,KAAK,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtD,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,gCAAgC;IAChC,IAAI,KAAK,CAAC,YAAY,EAAE,CAAC;QACvB,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,8BAA8B;IAC9B,IAAI,KAAK,CAAC,CAAC,KAAK,SAAS,IAAI,KAAK,CAAC,CAAC,KAAK,SAAS,EAAE,CAAC;QACnD,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,iBAAiB;IACjB,OAAO,EAAE,CAAC;AACZ,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { RecordingEvent } from '@testdroid-ai/shared';
|
|
2
|
+
export interface EventCaptureOptions {
|
|
3
|
+
useAppium?: boolean;
|
|
4
|
+
appiumUrl?: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Orchestrates capture of user interactions on Android devices.
|
|
8
|
+
*
|
|
9
|
+
* Strategy selection (in priority order):
|
|
10
|
+
* 1. LogcatCapture — accessibility service APK (captures ALL events)
|
|
11
|
+
* 2. GeteventStrategy — raw kernel input (fallback, coords only)
|
|
12
|
+
*
|
|
13
|
+
* KeyCaptureStrategy always runs alongside for back/home hardware keys.
|
|
14
|
+
*/
|
|
15
|
+
export declare class EventCapture {
|
|
16
|
+
private serial;
|
|
17
|
+
private options;
|
|
18
|
+
private strategy;
|
|
19
|
+
private keyCapture;
|
|
20
|
+
private screenSize;
|
|
21
|
+
private appiumActive;
|
|
22
|
+
private logcatStrategy;
|
|
23
|
+
constructor(serial: string, options?: EventCaptureOptions);
|
|
24
|
+
/** Whether Appium mode is active */
|
|
25
|
+
isAppiumActive(): boolean;
|
|
26
|
+
/** Get current foreground app package */
|
|
27
|
+
getAppPackage(): string;
|
|
28
|
+
getScreenSize(): Promise<{
|
|
29
|
+
width: number;
|
|
30
|
+
height: number;
|
|
31
|
+
}>;
|
|
32
|
+
start(onEvent: (event: RecordingEvent) => void): Promise<void>;
|
|
33
|
+
stop(): void;
|
|
34
|
+
/**
|
|
35
|
+
* Ensures the TestDroid accessibility service APK is installed and active.
|
|
36
|
+
* Returns true if service is ready for capture.
|
|
37
|
+
*/
|
|
38
|
+
private ensureAccessibilityService;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=event-capture.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-capture.d.ts","sourceRoot":"","sources":["../../src/recording/event-capture.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAMtD,MAAM,WAAW,mBAAmB;IAClC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;GAQG;AACH,qBAAa,YAAY;IAQrB,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,OAAO;IARjB,OAAO,CAAC,QAAQ,CAAgC;IAChD,OAAO,CAAC,UAAU,CAAmC;IACrD,OAAO,CAAC,UAAU,CAAiC;IACnD,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,cAAc,CAAa;gBAGzB,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,mBAAwB;IAG3C,oCAAoC;IACpC,cAAc,IAAI,OAAO;IAEzB,yCAAyC;IACzC,aAAa,IAAI,MAAM;IAOjB,aAAa,IAAI,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAa3D,KAAK,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IA8BpE,IAAI,IAAI,IAAI;IAMZ;;;OAGG;YACW,0BAA0B;CAsDzC"}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.EventCapture = void 0;
|
|
37
|
+
const child_process_1 = require("child_process");
|
|
38
|
+
const key_capture_strategy_1 = require("./capture-strategies/key-capture.strategy");
|
|
39
|
+
const confidence_scorer_1 = require("./confidence-scorer");
|
|
40
|
+
const path = __importStar(require("path"));
|
|
41
|
+
/**
|
|
42
|
+
* Orchestrates capture of user interactions on Android devices.
|
|
43
|
+
*
|
|
44
|
+
* Strategy selection (in priority order):
|
|
45
|
+
* 1. LogcatCapture — accessibility service APK (captures ALL events)
|
|
46
|
+
* 2. GeteventStrategy — raw kernel input (fallback, coords only)
|
|
47
|
+
*
|
|
48
|
+
* KeyCaptureStrategy always runs alongside for back/home hardware keys.
|
|
49
|
+
*/
|
|
50
|
+
class EventCapture {
|
|
51
|
+
serial;
|
|
52
|
+
options;
|
|
53
|
+
strategy = null;
|
|
54
|
+
keyCapture = null;
|
|
55
|
+
screenSize = { width: 1080, height: 2400 };
|
|
56
|
+
appiumActive = false;
|
|
57
|
+
logcatStrategy = null; // LogcatCaptureStrategy reference for getPackage()
|
|
58
|
+
constructor(serial, options = {}) {
|
|
59
|
+
this.serial = serial;
|
|
60
|
+
this.options = options;
|
|
61
|
+
}
|
|
62
|
+
/** Whether Appium mode is active */
|
|
63
|
+
isAppiumActive() { return this.appiumActive; }
|
|
64
|
+
/** Get current foreground app package */
|
|
65
|
+
getAppPackage() {
|
|
66
|
+
if (this.logcatStrategy?.getPackage) {
|
|
67
|
+
return this.logcatStrategy.getPackage();
|
|
68
|
+
}
|
|
69
|
+
return '';
|
|
70
|
+
}
|
|
71
|
+
async getScreenSize() {
|
|
72
|
+
try {
|
|
73
|
+
const output = (0, child_process_1.execFileSync)('adb', ['-s', this.serial, 'shell', 'wm', 'size'], {
|
|
74
|
+
encoding: 'utf-8', stdio: 'pipe', timeout: 5000,
|
|
75
|
+
});
|
|
76
|
+
const match = output.match(/(\d+)x(\d+)/);
|
|
77
|
+
if (match) {
|
|
78
|
+
this.screenSize = { width: parseInt(match[1], 10), height: parseInt(match[2], 10) };
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
catch { }
|
|
82
|
+
return this.screenSize;
|
|
83
|
+
}
|
|
84
|
+
async start(onEvent) {
|
|
85
|
+
const enrichedEmit = (event) => {
|
|
86
|
+
event.confidence = (0, confidence_scorer_1.scoreConfidence)(event);
|
|
87
|
+
onEvent(event);
|
|
88
|
+
};
|
|
89
|
+
// Try LogcatCapture (APK accessibility service) first
|
|
90
|
+
const apkReady = await this.ensureAccessibilityService();
|
|
91
|
+
if (apkReady) {
|
|
92
|
+
// PRIMARY: LogcatCapture — captures ALL user interactions
|
|
93
|
+
const { LogcatCaptureStrategy } = await Promise.resolve().then(() => __importStar(require('./capture-strategies/logcat-capture.strategy')));
|
|
94
|
+
this.logcatStrategy = new LogcatCaptureStrategy(this.serial);
|
|
95
|
+
this.strategy = this.logcatStrategy;
|
|
96
|
+
await this.strategy.start(enrichedEmit);
|
|
97
|
+
this.appiumActive = true;
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
// FALLBACK: GeteventStrategy — raw coords only, no element context
|
|
101
|
+
const { GeteventStrategy } = await Promise.resolve().then(() => __importStar(require('./capture-strategies/getevent.strategy')));
|
|
102
|
+
const getevent = new GeteventStrategy(this.serial);
|
|
103
|
+
getevent.setScreenSize(this.screenSize);
|
|
104
|
+
this.strategy = getevent;
|
|
105
|
+
await this.strategy.start(enrichedEmit);
|
|
106
|
+
}
|
|
107
|
+
// KeyCapture always runs (back/home hardware keys)
|
|
108
|
+
this.keyCapture = new key_capture_strategy_1.KeyCaptureStrategy(this.serial);
|
|
109
|
+
await this.keyCapture.start(enrichedEmit);
|
|
110
|
+
}
|
|
111
|
+
stop() {
|
|
112
|
+
if (this.strategy) {
|
|
113
|
+
this.strategy.stop();
|
|
114
|
+
this.strategy = null;
|
|
115
|
+
}
|
|
116
|
+
if (this.keyCapture) {
|
|
117
|
+
this.keyCapture.stop();
|
|
118
|
+
this.keyCapture = null;
|
|
119
|
+
}
|
|
120
|
+
this.logcatStrategy = null;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Ensures the TestDroid accessibility service APK is installed and active.
|
|
124
|
+
* Returns true if service is ready for capture.
|
|
125
|
+
*/
|
|
126
|
+
async ensureAccessibilityService() {
|
|
127
|
+
const SERVICE = 'com.testdroid.capture/com.testdroid.capture.CaptureService';
|
|
128
|
+
// Check if APK is installed
|
|
129
|
+
try {
|
|
130
|
+
const installed = (0, child_process_1.execFileSync)('adb', ['-s', this.serial, 'shell', 'pm', 'list', 'packages', 'com.testdroid.capture'], { encoding: 'utf-8', stdio: 'pipe', timeout: 5000 });
|
|
131
|
+
if (!installed.includes('com.testdroid.capture')) {
|
|
132
|
+
// Install the APK
|
|
133
|
+
const apkPath = path.resolve(__dirname, '../../android-service/testdroid-capture-aligned.apk');
|
|
134
|
+
try {
|
|
135
|
+
(0, child_process_1.execFileSync)('adb', ['-s', this.serial, 'install', '-r', '-g', apkPath], { stdio: 'pipe', timeout: 30000 });
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
// Re-enable the service (fixes stale state)
|
|
146
|
+
try {
|
|
147
|
+
(0, child_process_1.execFileSync)('adb', ['-s', this.serial, 'shell', 'settings', 'put', 'secure',
|
|
148
|
+
'enabled_accessibility_services', '""'], { stdio: 'pipe', timeout: 3000 });
|
|
149
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
150
|
+
(0, child_process_1.execFileSync)('adb', ['-s', this.serial, 'shell', 'settings', 'put', 'secure',
|
|
151
|
+
'enabled_accessibility_services', SERVICE], { stdio: 'pipe', timeout: 3000 });
|
|
152
|
+
await new Promise(r => setTimeout(r, 1500));
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
// Verify service is running
|
|
158
|
+
try {
|
|
159
|
+
const output = (0, child_process_1.execFileSync)('adb', ['-s', this.serial, 'shell', 'settings', 'get', 'secure', 'enabled_accessibility_services'], { encoding: 'utf-8', stdio: 'pipe', timeout: 3000 });
|
|
160
|
+
return output.includes('com.testdroid.capture');
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
exports.EventCapture = EventCapture;
|
|
168
|
+
//# sourceMappingURL=event-capture.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-capture.js","sourceRoot":"","sources":["../../src/recording/event-capture.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { RecordingEvent, Recording } from '@testdroid-ai/shared';
|
|
2
|
+
export interface RecorderOptions {
|
|
3
|
+
includeScreenshots?: boolean;
|
|
4
|
+
useAppium?: boolean;
|
|
5
|
+
appiumUrl?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare class Recorder {
|
|
8
|
+
private serial;
|
|
9
|
+
private options;
|
|
10
|
+
private events;
|
|
11
|
+
private capture;
|
|
12
|
+
private startTime;
|
|
13
|
+
private screenSize;
|
|
14
|
+
private onEventCallback?;
|
|
15
|
+
constructor(serial: string, options?: RecorderOptions);
|
|
16
|
+
/** Whether Appium/APK mode is active */
|
|
17
|
+
isAppiumActive(): boolean;
|
|
18
|
+
start(onEvent?: (event: RecordingEvent) => void): Promise<void>;
|
|
19
|
+
stop(): Recording & {
|
|
20
|
+
screenSize: {
|
|
21
|
+
width: number;
|
|
22
|
+
height: number;
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Detects appPackage from elementId prefix in events.
|
|
27
|
+
* Format: "com.example.app:id/button_name" → "com.example.app"
|
|
28
|
+
*/
|
|
29
|
+
private detectAppPackageFromEvents;
|
|
30
|
+
private getCurrentPackageFromDevice;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=recorder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recorder.d.ts","sourceRoot":"","sources":["../../src/recording/recorder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAIjE,MAAM,WAAW,eAAe;IAC9B,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,qBAAa,QAAQ;IAQjB,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,OAAO;IARjB,OAAO,CAAC,MAAM,CAAwB;IACtC,OAAO,CAAC,OAAO,CAAe;IAC9B,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,UAAU,CAAiC;IACnD,OAAO,CAAC,eAAe,CAAC,CAAkC;gBAGhD,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,eAAoB;IAQvC,wCAAwC;IACxC,cAAc,IAAI,OAAO;IAEnB,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAqBrE,IAAI,IAAI,SAAS,GAAG;QAAE,UAAU,EAAE;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE;IA0BrE;;;OAGG;IACH,OAAO,CAAC,0BAA0B;IAsBlC,OAAO,CAAC,2BAA2B;CAqBpC"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Recorder = void 0;
|
|
4
|
+
const event_capture_1 = require("./event-capture");
|
|
5
|
+
const child_process_1 = require("child_process");
|
|
6
|
+
class Recorder {
|
|
7
|
+
serial;
|
|
8
|
+
options;
|
|
9
|
+
events = [];
|
|
10
|
+
capture;
|
|
11
|
+
startTime = 0;
|
|
12
|
+
screenSize = { width: 1080, height: 2400 };
|
|
13
|
+
onEventCallback;
|
|
14
|
+
constructor(serial, options = {}) {
|
|
15
|
+
this.serial = serial;
|
|
16
|
+
this.options = options;
|
|
17
|
+
this.capture = new event_capture_1.EventCapture(serial, {
|
|
18
|
+
useAppium: options.useAppium,
|
|
19
|
+
appiumUrl: options.appiumUrl,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
/** Whether Appium/APK mode is active */
|
|
23
|
+
isAppiumActive() { return this.capture.isAppiumActive(); }
|
|
24
|
+
async start(onEvent) {
|
|
25
|
+
this.events = [];
|
|
26
|
+
this.startTime = Date.now();
|
|
27
|
+
this.onEventCallback = onEvent;
|
|
28
|
+
this.screenSize = await this.capture.getScreenSize();
|
|
29
|
+
await this.capture.start((event) => {
|
|
30
|
+
// Make timestamp relative to recording start
|
|
31
|
+
event.timestamp = event.timestamp - this.startTime;
|
|
32
|
+
// Store event
|
|
33
|
+
this.events.push(event);
|
|
34
|
+
// Notify callback
|
|
35
|
+
if (this.onEventCallback) {
|
|
36
|
+
this.onEventCallback(event);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
stop() {
|
|
41
|
+
this.capture.stop();
|
|
42
|
+
const duration = Date.now() - this.startTime;
|
|
43
|
+
const eventsCopy = this.events.map(e => ({ ...e }));
|
|
44
|
+
// Detect appPackage: prefer capture source, then events, then ADB
|
|
45
|
+
const launcherPkgs = ['com.google.android.apps.nexuslauncher', 'com.android.launcher', 'com.android.launcher3'];
|
|
46
|
+
const appFromCapture = this.capture.getAppPackage();
|
|
47
|
+
const appFromEvents = this.detectAppPackageFromEvents(eventsCopy);
|
|
48
|
+
const appPackage = appFromEvents
|
|
49
|
+
|| (appFromCapture && !launcherPkgs.includes(appFromCapture) ? appFromCapture : '')
|
|
50
|
+
|| this.getCurrentPackageFromDevice();
|
|
51
|
+
return {
|
|
52
|
+
id: `rec_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`,
|
|
53
|
+
projectId: '',
|
|
54
|
+
events: eventsCopy,
|
|
55
|
+
deviceSerial: this.serial,
|
|
56
|
+
appPackage,
|
|
57
|
+
screenSize: this.screenSize,
|
|
58
|
+
duration,
|
|
59
|
+
createdAt: new Date(),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Detects appPackage from elementId prefix in events.
|
|
64
|
+
* Format: "com.example.app:id/button_name" → "com.example.app"
|
|
65
|
+
*/
|
|
66
|
+
detectAppPackageFromEvents(events) {
|
|
67
|
+
const packageCounts = {};
|
|
68
|
+
const systemPackages = new Set([
|
|
69
|
+
'com.google.android.apps.nexuslauncher',
|
|
70
|
+
'com.android.launcher', 'com.android.launcher3',
|
|
71
|
+
'com.android.systemui',
|
|
72
|
+
'com.google.android.inputmethod', 'com.android.inputmethod',
|
|
73
|
+
]);
|
|
74
|
+
for (const event of events) {
|
|
75
|
+
if (!event.elementId)
|
|
76
|
+
continue;
|
|
77
|
+
const match = event.elementId.match(/^([a-zA-Z][a-zA-Z0-9_.]+):id\//);
|
|
78
|
+
if (!match)
|
|
79
|
+
continue;
|
|
80
|
+
const pkg = match[1];
|
|
81
|
+
if (systemPackages.has(pkg))
|
|
82
|
+
continue;
|
|
83
|
+
packageCounts[pkg] = (packageCounts[pkg] || 0) + 1;
|
|
84
|
+
}
|
|
85
|
+
const sorted = Object.entries(packageCounts).sort((a, b) => b[1] - a[1]);
|
|
86
|
+
return sorted.length > 0 ? sorted[0][0] : '';
|
|
87
|
+
}
|
|
88
|
+
getCurrentPackageFromDevice() {
|
|
89
|
+
try {
|
|
90
|
+
const raw = (0, child_process_1.execFileSync)('adb', ['-s', this.serial, 'shell', 'dumpsys', 'activity', 'activities'], { encoding: 'utf-8', stdio: 'pipe', timeout: 5000 });
|
|
91
|
+
const match = raw.match(/topResumedActivity=.*?([a-zA-Z][a-zA-Z0-9_.]+)\//);
|
|
92
|
+
if (match)
|
|
93
|
+
return match[1];
|
|
94
|
+
}
|
|
95
|
+
catch { }
|
|
96
|
+
try {
|
|
97
|
+
const raw = (0, child_process_1.execFileSync)('adb', ['-s', this.serial, 'shell', 'dumpsys', 'window'], { encoding: 'utf-8', stdio: 'pipe', timeout: 5000 });
|
|
98
|
+
const match = raw.match(/mCurrentFocus=.*?([a-zA-Z][a-zA-Z0-9_.]+)\//);
|
|
99
|
+
if (match)
|
|
100
|
+
return match[1];
|
|
101
|
+
}
|
|
102
|
+
catch { }
|
|
103
|
+
return '';
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
exports.Recorder = Recorder;
|
|
107
|
+
//# sourceMappingURL=recorder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recorder.js","sourceRoot":"","sources":["../../src/recording/recorder.ts"],"names":[],"mappings":";;;AACA,mDAA+C;AAC/C,iDAA6C;AAQ7C,MAAa,QAAQ;IAQT;IACA;IARF,MAAM,GAAqB,EAAE,CAAC;IAC9B,OAAO,CAAe;IACtB,SAAS,GAAG,CAAC,CAAC;IACd,UAAU,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAC3C,eAAe,CAAmC;IAE1D,YACU,MAAc,EACd,UAA2B,EAAE;QAD7B,WAAM,GAAN,MAAM,CAAQ;QACd,YAAO,GAAP,OAAO,CAAsB;QAErC,IAAI,CAAC,OAAO,GAAG,IAAI,4BAAY,CAAC,MAAM,EAAE;YACtC,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,SAAS,EAAE,OAAO,CAAC,SAAS;SAC7B,CAAC,CAAC;IACL,CAAC;IAED,wCAAwC;IACxC,cAAc,KAAc,OAAO,IAAI,CAAC,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC;IAEnE,KAAK,CAAC,KAAK,CAAC,OAAyC;QACnD,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC;QACjB,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC5B,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC;QAE/B,IAAI,CAAC,UAAU,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC;QAErD,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,KAAqB,EAAE,EAAE;YACjD,6CAA6C;YAC7C,KAAK,CAAC,SAAS,GAAG,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC;YAEnD,cAAc;YACd,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAExB,kBAAkB;YAClB,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;gBACzB,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;YAC9B,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAED,IAAI;QACF,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;QAEpB,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC;QAC7C,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QAEpD,kEAAkE;QAClE,MAAM,YAAY,GAAG,CAAC,uCAAuC,EAAE,sBAAsB,EAAE,uBAAuB,CAAC,CAAC;QAChH,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC;QACpD,MAAM,aAAa,GAAG,IAAI,CAAC,0BAA0B,CAAC,UAAU,CAAC,CAAC;QAClE,MAAM,UAAU,GAAG,aAAa;eAC3B,CAAC,cAAc,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,CAAC;eAChF,IAAI,CAAC,2BAA2B,EAAE,CAAC;QAExC,OAAO;YACL,EAAE,EAAE,OAAO,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE;YACrE,SAAS,EAAE,EAAE;YACb,MAAM,EAAE,UAAU;YAClB,YAAY,EAAE,IAAI,CAAC,MAAM;YACzB,UAAU;YACV,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,QAAQ;YACR,SAAS,EAAE,IAAI,IAAI,EAAE;SACtB,CAAC;IACJ,CAAC;IAED;;;OAGG;IACK,0BAA0B,CAAC,MAAwB;QACzD,MAAM,aAAa,GAA2B,EAAE,CAAC;QACjD,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC;YAC7B,uCAAuC;YACvC,sBAAsB,EAAE,uBAAuB;YAC/C,sBAAsB;YACtB,gCAAgC,EAAE,yBAAyB;SAC5D,CAAC,CAAC;QAEH,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,IAAI,CAAC,KAAK,CAAC,SAAS;gBAAE,SAAS;YAC/B,MAAM,KAAK,GAAG,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,gCAAgC,CAAC,CAAC;YACtE,IAAI,CAAC,KAAK;gBAAE,SAAS;YACrB,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YACrB,IAAI,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC;gBAAE,SAAS;YACtC,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;QACrD,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACzE,OAAO,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC/C,CAAC;IAEO,2BAA2B;QACjC,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAA,4BAAY,EACtB,KAAK,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,YAAY,CAAC,EACxE,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,CACpD,CAAC;YACF,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,kDAAkD,CAAC,CAAC;YAC5E,IAAI,KAAK;gBAAE,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC;QAC7B,CAAC;QAAC,MAAM,CAAC,CAAA,CAAC;QAEV,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAA,4BAAY,EACtB,KAAK,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,CAAC,EACxD,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,CACpD,CAAC;YACF,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,6CAA6C,CAAC,CAAC;YACvE,IAAI,KAAK;gBAAE,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC;QAC7B,CAAC;QAAC,MAAM,CAAC,CAAA,CAAC;QAEV,OAAO,EAAE,CAAC;IACZ,CAAC;CACF;AAlHD,4BAkHC"}
|