@aiscene/aiserver 1.7.1 → 1.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/dist/core/adb.d.ts +10 -0
  2. package/dist/core/adb.d.ts.map +1 -0
  3. package/dist/core/adb.js +25 -0
  4. package/dist/core/adb.js.map +1 -0
  5. package/dist/core/android-identifiers.d.ts +3 -0
  6. package/dist/core/android-identifiers.d.ts.map +1 -0
  7. package/dist/core/android-identifiers.js +21 -0
  8. package/dist/core/android-identifiers.js.map +1 -0
  9. package/dist/core/native-swipe.d.ts.map +1 -1
  10. package/dist/core/native-swipe.js +3 -6
  11. package/dist/core/native-swipe.js.map +1 -1
  12. package/dist/core/types.d.ts +13 -0
  13. package/dist/core/types.d.ts.map +1 -1
  14. package/dist/debug/report-group-name.d.ts +14 -0
  15. package/dist/debug/report-group-name.d.ts.map +1 -0
  16. package/dist/debug/report-group-name.js +30 -0
  17. package/dist/debug/report-group-name.js.map +1 -0
  18. package/dist/debug/types.d.ts +55 -31
  19. package/dist/debug/types.d.ts.map +1 -1
  20. package/dist/debug/websocket-server.d.ts +1 -0
  21. package/dist/debug/websocket-server.d.ts.map +1 -1
  22. package/dist/debug/websocket-server.js +142 -9
  23. package/dist/debug/websocket-server.js.map +1 -1
  24. package/dist/device/detector.d.ts.map +1 -1
  25. package/dist/device/detector.js +2 -1
  26. package/dist/device/detector.js.map +1 -1
  27. package/dist/device/heartbeat.d.ts.map +1 -1
  28. package/dist/device/heartbeat.js +2 -4
  29. package/dist/device/heartbeat.js.map +1 -1
  30. package/dist/executor/action-executor.d.ts.map +1 -1
  31. package/dist/executor/action-executor.js +22 -22
  32. package/dist/executor/action-executor.js.map +1 -1
  33. package/dist/executor/android-executor.d.ts.map +1 -1
  34. package/dist/executor/android-executor.js +22 -19
  35. package/dist/executor/android-executor.js.map +1 -1
  36. package/dist/executor/cli-executor.d.ts.map +1 -1
  37. package/dist/executor/cli-executor.js +6 -0
  38. package/dist/executor/cli-executor.js.map +1 -1
  39. package/dist/login/android-login-context.d.ts +29 -0
  40. package/dist/login/android-login-context.d.ts.map +1 -0
  41. package/dist/login/android-login-context.js +165 -0
  42. package/dist/login/android-login-context.js.map +1 -0
  43. package/dist/recorder/android-login-replay.d.ts +11 -0
  44. package/dist/recorder/android-login-replay.d.ts.map +1 -0
  45. package/dist/recorder/android-login-replay.js +70 -0
  46. package/dist/recorder/android-login-replay.js.map +1 -0
  47. package/dist/recorder/android-login-script.d.ts +50 -0
  48. package/dist/recorder/android-login-script.d.ts.map +1 -0
  49. package/dist/recorder/android-login-script.js +2 -0
  50. package/dist/recorder/android-login-script.js.map +1 -0
  51. package/dist/recorder/android-recording-manager.d.ts +23 -0
  52. package/dist/recorder/android-recording-manager.d.ts.map +1 -0
  53. package/dist/recorder/android-recording-manager.js +180 -0
  54. package/dist/recorder/android-recording-manager.js.map +1 -0
  55. package/dist/recorder/android-ui-hierarchy.d.ts +45 -0
  56. package/dist/recorder/android-ui-hierarchy.d.ts.map +1 -0
  57. package/dist/recorder/android-ui-hierarchy.js +183 -0
  58. package/dist/recorder/android-ui-hierarchy.js.map +1 -0
  59. package/dist/recorder/web-login-replay.d.ts +9 -0
  60. package/dist/recorder/web-login-replay.d.ts.map +1 -0
  61. package/dist/recorder/web-login-replay.js +39 -0
  62. package/dist/recorder/web-login-replay.js.map +1 -0
  63. package/dist/recorder/web-login-script.d.ts +36 -0
  64. package/dist/recorder/web-login-script.d.ts.map +1 -0
  65. package/dist/recorder/web-login-script.js +2 -0
  66. package/dist/recorder/web-login-script.js.map +1 -0
  67. package/dist/recorder/web-recording-manager.d.ts +41 -0
  68. package/dist/recorder/web-recording-manager.d.ts.map +1 -0
  69. package/dist/recorder/web-recording-manager.js +226 -0
  70. package/dist/recorder/web-recording-manager.js.map +1 -0
  71. package/dist/scrcpy/server.d.ts.map +1 -1
  72. package/dist/scrcpy/server.js +213 -11
  73. package/dist/scrcpy/server.js.map +1 -1
  74. package/dist/task/poller.d.ts.map +1 -1
  75. package/dist/task/poller.js +8 -1
  76. package/dist/task/poller.js.map +1 -1
  77. package/dist/task/scheduler.d.ts.map +1 -1
  78. package/dist/task/scheduler.js +49 -0
  79. package/dist/task/scheduler.js.map +1 -1
  80. package/dist/task/types.d.ts.map +1 -1
  81. package/dist/task/types.js +35 -1
  82. package/dist/task/types.js.map +1 -1
  83. package/package.json +1 -1
@@ -0,0 +1,183 @@
1
+ import { execFile } from 'node:child_process';
2
+ import { spawn } from 'node:child_process';
3
+ import { promisify } from 'node:util';
4
+ const execFileAsync = promisify(execFile);
5
+ function decodeXml(value) {
6
+ return value
7
+ .replace(/"/g, '"')
8
+ .replace(/'/g, "'")
9
+ .replace(/&lt;/g, '<')
10
+ .replace(/&gt;/g, '>')
11
+ .replace(/&amp;/g, '&');
12
+ }
13
+ function parseAttributes(source) {
14
+ const result = {};
15
+ const pattern = /([\w-]+)="([^"]*)"/g;
16
+ let match;
17
+ while ((match = pattern.exec(source))) {
18
+ result[match[1]] = decodeXml(match[2]);
19
+ }
20
+ return result;
21
+ }
22
+ function parseBounds(value) {
23
+ if (!value)
24
+ return null;
25
+ const match = value.match(/^\[(\d+),(\d+)]\[(\d+),(\d+)]$/);
26
+ if (!match)
27
+ return null;
28
+ return [Number(match[1]), Number(match[2]), Number(match[3]), Number(match[4])];
29
+ }
30
+ export function parseAndroidUiNodes(xml) {
31
+ const nodes = [];
32
+ const pattern = /<node\b([^>]*)\/?>/g;
33
+ let match;
34
+ while ((match = pattern.exec(xml))) {
35
+ const attrs = parseAttributes(match[1]);
36
+ const bounds = parseBounds(attrs.bounds);
37
+ if (!bounds)
38
+ continue;
39
+ const selector = {};
40
+ if (attrs['resource-id'])
41
+ selector.resourceId = attrs['resource-id'];
42
+ if (attrs['content-desc'])
43
+ selector.contentDescription = attrs['content-desc'];
44
+ if (attrs.text)
45
+ selector.text = attrs.text;
46
+ if (attrs.class)
47
+ selector.className = attrs.class;
48
+ nodes.push({
49
+ id: `node-${nodes.length}`,
50
+ selector,
51
+ focused: attrs.focused === 'true',
52
+ clickable: attrs.clickable === 'true',
53
+ checkable: attrs.checkable === 'true',
54
+ editable: attrs.class === 'android.widget.EditText' ||
55
+ attrs.focusable === 'true' ||
56
+ attrs['long-clickable'] === 'true',
57
+ password: attrs.password === 'true',
58
+ enabled: attrs.enabled !== 'false',
59
+ bounds,
60
+ });
61
+ }
62
+ return nodes;
63
+ }
64
+ async function runAdbBuffer(deviceId, args, timeout = 10000) {
65
+ return new Promise((resolve, reject) => {
66
+ const child = spawn('adb', ['-s', deviceId, ...args], {
67
+ stdio: ['ignore', 'pipe', 'pipe'],
68
+ });
69
+ const stdout = [];
70
+ const stderr = [];
71
+ const timer = setTimeout(() => {
72
+ child.kill('SIGKILL');
73
+ reject(new Error(`ADB command timed out after ${timeout}ms`));
74
+ }, timeout);
75
+ child.stdout.on('data', (chunk) => stdout.push(chunk));
76
+ child.stderr.on('data', (chunk) => stderr.push(chunk));
77
+ child.once('error', (error) => {
78
+ clearTimeout(timer);
79
+ reject(error);
80
+ });
81
+ child.once('close', (code) => {
82
+ clearTimeout(timer);
83
+ if (code === 0) {
84
+ resolve(Buffer.concat(stdout));
85
+ }
86
+ else {
87
+ reject(new Error(Buffer.concat(stderr).toString('utf8') || `ADB exited with ${code}`));
88
+ }
89
+ });
90
+ });
91
+ }
92
+ async function runAdb(deviceId, args, timeout = 10000) {
93
+ const { stdout } = await execFileAsync('adb', ['-s', deviceId, ...args], {
94
+ timeout,
95
+ maxBuffer: 10 * 1024 * 1024,
96
+ });
97
+ return stdout;
98
+ }
99
+ export async function resetAndLaunchAndroidApp(deviceId, packageName) {
100
+ await runAdb(deviceId, ['shell', 'pm', 'clear', packageName], 20000);
101
+ await runAdb(deviceId, ['shell', 'monkey', '-p', packageName, '-c', 'android.intent.category.LAUNCHER', '1'], 20000);
102
+ }
103
+ export async function captureAndroidUiHierarchy(deviceId) {
104
+ const remotePath = `/sdcard/aiserver-window-${Date.now()}.xml`;
105
+ try {
106
+ await runAdb(deviceId, ['shell', 'uiautomator', 'dump', remotePath], 15000);
107
+ return await runAdb(deviceId, ['shell', 'cat', remotePath], 10000);
108
+ }
109
+ finally {
110
+ runAdb(deviceId, ['shell', 'rm', '-f', remotePath], 5000).catch(() => { });
111
+ }
112
+ }
113
+ export async function getAndroidScreenSize(deviceId) {
114
+ const output = await runAdb(deviceId, ['shell', 'wm', 'size'], 5000);
115
+ const matches = [...output.matchAll(/(\d+)x(\d+)/g)];
116
+ const match = matches[matches.length - 1];
117
+ return match
118
+ ? { width: Number(match[1]), height: Number(match[2]) }
119
+ : { width: 1080, height: 1920 };
120
+ }
121
+ export async function captureAndroidScreen(deviceId) {
122
+ return runAdbBuffer(deviceId, ['exec-out', 'screencap', '-p'], 15000);
123
+ }
124
+ export async function captureAndroidScreenshotState(deviceId) {
125
+ const screenshot = await captureAndroidScreen(deviceId);
126
+ return {
127
+ screenshotBase64: screenshot.toString('base64'),
128
+ width: screenshot.length >= 24 ? screenshot.readUInt32BE(16) : 0,
129
+ height: screenshot.length >= 24 ? screenshot.readUInt32BE(20) : 0,
130
+ };
131
+ }
132
+ export async function captureAndroidElementState(deviceId) {
133
+ const xml = await captureAndroidUiHierarchy(deviceId);
134
+ return {
135
+ xml,
136
+ nodes: parseAndroidUiNodes(xml),
137
+ };
138
+ }
139
+ export async function captureAndroidScreenState(deviceId) {
140
+ const [screenshot, elements] = await Promise.all([
141
+ captureAndroidScreenshotState(deviceId),
142
+ captureAndroidElementState(deviceId),
143
+ ]);
144
+ return {
145
+ ...screenshot,
146
+ ...elements,
147
+ };
148
+ }
149
+ export function findSelectorAtPoint(xml, x, y) {
150
+ const candidates = parseAndroidUiNodes(xml)
151
+ .filter(({ bounds }) => x >= bounds[0] && x <= bounds[2] && y >= bounds[1] && y <= bounds[3])
152
+ .sort((a, b) => {
153
+ const areaA = (a.bounds[2] - a.bounds[0]) * (a.bounds[3] - a.bounds[1]);
154
+ const areaB = (b.bounds[2] - b.bounds[0]) * (b.bounds[3] - b.bounds[1]);
155
+ return areaA - areaB;
156
+ });
157
+ return candidates.find((node) => Object.keys(node.selector).length > 1)?.selector;
158
+ }
159
+ export function findFocusedSelector(xml) {
160
+ return parseAndroidUiNodes(xml).find((node) => node.focused && Object.keys(node.selector).length > 1)?.selector;
161
+ }
162
+ export function findPointBySelector(xml, selector) {
163
+ const node = parseAndroidUiNodes(xml).find(({ selector: candidate }) => {
164
+ if (selector.resourceId && candidate.resourceId !== selector.resourceId)
165
+ return false;
166
+ if (selector.contentDescription &&
167
+ candidate.contentDescription !== selector.contentDescription)
168
+ return false;
169
+ if (selector.text && candidate.text !== selector.text)
170
+ return false;
171
+ if (selector.className && candidate.className !== selector.className)
172
+ return false;
173
+ return true;
174
+ });
175
+ if (!node)
176
+ return undefined;
177
+ return {
178
+ x: Math.round((node.bounds[0] + node.bounds[2]) / 2),
179
+ y: Math.round((node.bounds[1] + node.bounds[3]) / 2),
180
+ };
181
+ }
182
+ export { runAdb };
183
+ //# sourceMappingURL=android-ui-hierarchy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"android-ui-hierarchy.js","sourceRoot":"","sources":["../../src/recorder/android-ui-hierarchy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { Page } from 'playwright';
2
+ import type { WebLoginScript, WebLoginStep } from './web-login-script.js';
3
+ export declare function replayWebLoginScript(options: {
4
+ page: Page;
5
+ script: WebLoginScript;
6
+ variables?: Record<string, string>;
7
+ onStep?: (index: number, step: WebLoginStep) => void;
8
+ }): Promise<void>;
9
+ //# sourceMappingURL=web-login-replay.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web-login-replay.d.ts","sourceRoot":"","sources":["../../src/recorder/web-login-replay.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAuC1E,wBAAsB,oBAAoB,CAAC,OAAO,EAAE;IAClD,IAAI,EAAE,IAAI,CAAC;IACX,MAAM,EAAE,cAAc,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,KAAK,IAAI,CAAC;CACtD,GAAG,OAAO,CAAC,IAAI,CAAC,CAOhB"}
@@ -0,0 +1,39 @@
1
+ function resolveValue(value, variables) {
2
+ return value.replace(/\{\{(\w+)}}/g, (_, key) => variables[key] ?? '');
3
+ }
4
+ async function executeStep(page, step, variables) {
5
+ if (step.type === 'goto') {
6
+ await page.goto(step.url, { waitUntil: 'domcontentloaded' });
7
+ return;
8
+ }
9
+ if (step.type === 'wait') {
10
+ await page.waitForTimeout(step.duration);
11
+ return;
12
+ }
13
+ const locator = page.locator(step.selector).first();
14
+ await locator.waitFor({ state: 'visible', timeout: step.timeout || 10000 });
15
+ if (step.type === 'click') {
16
+ await locator.click();
17
+ return;
18
+ }
19
+ if (step.type === 'check') {
20
+ await locator.check();
21
+ return;
22
+ }
23
+ const value = resolveValue(step.value, variables);
24
+ if (step.clearBeforeInput !== false) {
25
+ await locator.fill(value);
26
+ }
27
+ else {
28
+ await locator.pressSequentially(value);
29
+ }
30
+ }
31
+ export async function replayWebLoginScript(options) {
32
+ for (let index = 0; index < options.script.steps.length; index += 1) {
33
+ const step = options.script.steps[index];
34
+ options.onStep?.(index, step);
35
+ await executeStep(options.page, step, options.variables || {});
36
+ await options.page.waitForTimeout(200);
37
+ }
38
+ }
39
+ //# sourceMappingURL=web-login-replay.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web-login-replay.js","sourceRoot":"","sources":["../../src/recorder/web-login-replay.ts"],"names":[],"mappings":"AAGA,SAAS,YAAY,CAAC,KAAa,EAAE,SAAiC;IACpE,OAAO,KAAK,CAAC,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC,EAAE,GAAW,EAAE,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;AACjF,CAAC;AAED,KAAK,UAAU,WAAW,CACxB,IAAU,EACV,IAAkB,EAClB,SAAiC;IAEjC,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QACzB,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,kBAAkB,EAAE,CAAC,CAAC;QAC7D,OAAO;IACT,CAAC;IACD,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QACzB,MAAM,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACzC,OAAO;IACT,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,CAAC;IACpD,MAAM,OAAO,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,KAAK,EAAE,CAAC,CAAC;IAC5E,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QAC1B,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;QACtB,OAAO;IACT,CAAC;IACD,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QAC1B,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;QACtB,OAAO;IACT,CAAC;IAED,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;IAClD,IAAI,IAAI,CAAC,gBAAgB,KAAK,KAAK,EAAE,CAAC;QACpC,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC5B,CAAC;SAAM,CAAC;QACN,MAAM,OAAO,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC;IACzC,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,OAK1C;IACC,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QACpE,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACzC,OAAO,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QAC9B,MAAM,WAAW,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC;QAC/D,MAAM,OAAO,CAAC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;IACzC,CAAC;AACH,CAAC"}
@@ -0,0 +1,36 @@
1
+ export type WebLoginStep = {
2
+ id: string;
3
+ type: 'goto';
4
+ url: string;
5
+ } | {
6
+ id: string;
7
+ type: 'click';
8
+ selector: string;
9
+ timeout?: number;
10
+ } | {
11
+ id: string;
12
+ type: 'input';
13
+ selector: string;
14
+ value: string;
15
+ secret?: boolean;
16
+ clearBeforeInput?: boolean;
17
+ timeout?: number;
18
+ } | {
19
+ id: string;
20
+ type: 'check';
21
+ selector: string;
22
+ timeout?: number;
23
+ } | {
24
+ id: string;
25
+ type: 'wait';
26
+ duration: number;
27
+ };
28
+ export interface WebLoginScript {
29
+ schemaVersion: 1;
30
+ platform: 'web';
31
+ name: string;
32
+ targetUrl: string;
33
+ steps: WebLoginStep[];
34
+ createdAt: string;
35
+ }
36
+ //# sourceMappingURL=web-login-script.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web-login-script.d.ts","sourceRoot":"","sources":["../../src/recorder/web-login-script.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GACpB;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GACzC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GACjE;IACE,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,GACD;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GACjE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnD,MAAM,WAAW,cAAc;IAC7B,aAAa,EAAE,CAAC,CAAC;IACjB,QAAQ,EAAE,KAAK,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=web-login-script.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web-login-script.js","sourceRoot":"","sources":["../../src/recorder/web-login-script.ts"],"names":[],"mappings":""}
@@ -0,0 +1,41 @@
1
+ import type { WebLoginScript } from './web-login-script.js';
2
+ export declare class WebRecordingManager {
3
+ private sessions;
4
+ start(clientId: string, options: {
5
+ name?: string;
6
+ targetUrl: string;
7
+ }): Promise<WebLoginScript>;
8
+ snapshot(clientId: string): Promise<{
9
+ screenshotBase64: string;
10
+ width: number;
11
+ height: number;
12
+ nodes: Record<string, unknown>[];
13
+ }>;
14
+ select(clientId: string, x: number, y: number): Promise<{
15
+ step: {
16
+ id: string;
17
+ type: "click";
18
+ selector: string;
19
+ timeout?: number;
20
+ };
21
+ passwordField: boolean;
22
+ }>;
23
+ input(clientId: string, text: string, recordingValue: string, secret: boolean): Promise<{
24
+ id: string;
25
+ type: "input";
26
+ selector: string;
27
+ value: string;
28
+ secret?: boolean;
29
+ clearBeforeInput?: boolean;
30
+ timeout?: number;
31
+ }>;
32
+ stop(clientId: string): Promise<WebLoginScript>;
33
+ replay(options: {
34
+ script: WebLoginScript;
35
+ variables?: Record<string, string>;
36
+ }): Promise<void>;
37
+ cancel(clientId: string): Promise<void>;
38
+ private requireSession;
39
+ }
40
+ export declare const webRecordingManager: WebRecordingManager;
41
+ //# sourceMappingURL=web-recording-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web-recording-manager.d.ts","sourceRoot":"","sources":["../../src/recorder/web-recording-manager.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAgB,MAAM,uBAAuB,CAAC;AA+G1E,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAA0C;IAEpD,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE;IAmBrE,QAAQ,CAAC,QAAQ,EAAE,MAAM;;;;;;IAgCzB,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM;;;;;;;;;IA8B7C,KAAK,CACT,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,cAAc,EAAE,MAAM,EACtB,MAAM,EAAE,OAAO;;;;;;;;;IAmBX,IAAI,CAAC,QAAQ,EAAE,MAAM;IAOrB,MAAM,CAAC,OAAO,EAAE;QACpB,MAAM,EAAE,cAAc,CAAC;QACvB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KACpC;IAWK,MAAM,CAAC,QAAQ,EAAE,MAAM;IAM7B,OAAO,CAAC,cAAc;CAKvB;AAED,eAAO,MAAM,mBAAmB,qBAA4B,CAAC"}
@@ -0,0 +1,226 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { chromium } from 'playwright';
3
+ import { replayWebLoginScript } from './web-login-replay.js';
4
+ const selectorHelper = `
5
+ const cssEscape = globalThis.CSS && globalThis.CSS.escape
6
+ ? globalThis.CSS.escape
7
+ : function(value) { return value; };
8
+ const escapeValue = function(value) {
9
+ return value.replace(/\\\\/g, '\\\\\\\\').replace(/"/g, '\\\\"');
10
+ };
11
+ const selectorFor = function(element) {
12
+ if (element.id) return '#' + cssEscape(element.id);
13
+ for (const attr of ['data-testid', 'data-test', 'name', 'aria-label', 'placeholder']) {
14
+ const value = element.getAttribute(attr);
15
+ if (value) {
16
+ return element.tagName.toLowerCase() + '[' + attr + '="' + escapeValue(value) + '"]';
17
+ }
18
+ }
19
+ const text = (element.innerText || element.textContent || '').trim().replace(/\\s+/g, ' ');
20
+ if (text && text.length <= 40 && isInteractive(element)) {
21
+ return element.tagName.toLowerCase() + ':has-text("' + escapeValue(text) + '")';
22
+ }
23
+ const parts = [];
24
+ let current = element;
25
+ while (current && current !== document.body && parts.length < 5) {
26
+ const siblings = Array.from(
27
+ current.parentElement ? current.parentElement.children : [],
28
+ ).filter(function(item) { return item.tagName === current.tagName; });
29
+ parts.unshift(
30
+ current.tagName.toLowerCase() + ':nth-of-type(' + (siblings.indexOf(current) + 1) + ')',
31
+ );
32
+ current = current.parentElement;
33
+ }
34
+ return 'body > ' + parts.join(' > ');
35
+ };
36
+ const loginWords = /登录|登陆|账号|账户|密码|短信|验证码|手机|下一步|继续|同意|login|sign[ -]?in|password|sms|verify|mobile/i;
37
+ const interactiveTags = ['INPUT', 'TEXTAREA', 'BUTTON', 'A', 'SELECT', 'OPTION', 'LABEL'];
38
+ const isVisible = function(element) {
39
+ const rect = element.getBoundingClientRect();
40
+ const style = getComputedStyle(element);
41
+ return rect.width >= 4 && rect.height >= 4 &&
42
+ rect.bottom > 0 && rect.right > 0 &&
43
+ rect.top < innerHeight && rect.left < innerWidth &&
44
+ style.display !== 'none' && style.visibility !== 'hidden' &&
45
+ Number(style.opacity || 1) > 0;
46
+ };
47
+ const isEditable = function(element) {
48
+ return ['INPUT', 'TEXTAREA', 'SELECT'].includes(element.tagName) ||
49
+ element.isContentEditable;
50
+ };
51
+ const isStrongInteractive = function(element) {
52
+ if (!element || !isVisible(element)) return false;
53
+ const role = (element.getAttribute('role') || '').toLowerCase();
54
+ const style = getComputedStyle(element);
55
+ return interactiveTags.includes(element.tagName) ||
56
+ isEditable(element) ||
57
+ element.hasAttribute('onclick') ||
58
+ typeof element.onclick === 'function' ||
59
+ ['button', 'link', 'tab', 'checkbox', 'radio', 'switch', 'option'].includes(role) ||
60
+ element.tabIndex >= 0 ||
61
+ style.cursor === 'pointer';
62
+ };
63
+ const isInteractive = function(element) {
64
+ if (isStrongInteractive(element)) return true;
65
+ if (!element || !isVisible(element)) return false;
66
+ const classAndId = (element.className || '') + ' ' + (element.id || '');
67
+ const text = (
68
+ element.innerText || element.textContent ||
69
+ element.getAttribute('aria-label') || element.getAttribute('placeholder') || ''
70
+ ).trim().replace(/\\s+/g, ' ');
71
+ return loginWords.test(text) ||
72
+ /(login|signin|password|pwd|sms|verify|captcha|account|mobile|phone|tab|button|btn)/i.test(classAndId);
73
+ };
74
+ const interactiveAncestor = function(element) {
75
+ let current = element;
76
+ let fallback = null;
77
+ while (current && current !== document.body) {
78
+ if (isStrongInteractive(current)) return current;
79
+ if (!fallback && isInteractive(current)) fallback = current;
80
+ current = current.parentElement;
81
+ }
82
+ return fallback || element;
83
+ };
84
+ `;
85
+ function createScript(session) {
86
+ return {
87
+ schemaVersion: 1,
88
+ platform: 'web',
89
+ name: session.name,
90
+ targetUrl: session.targetUrl,
91
+ steps: session.steps,
92
+ createdAt: new Date().toISOString(),
93
+ };
94
+ }
95
+ export class WebRecordingManager {
96
+ sessions = new Map();
97
+ async start(clientId, options) {
98
+ await this.cancel(clientId);
99
+ const targetUrl = new URL(options.targetUrl).toString();
100
+ const browser = await chromium.launch({ headless: true });
101
+ const context = await browser.newContext({ viewport: { width: 1280, height: 800 } });
102
+ const page = await context.newPage();
103
+ await page.goto(targetUrl, { waitUntil: 'domcontentloaded' });
104
+ const session = {
105
+ browser,
106
+ context,
107
+ page,
108
+ name: options.name?.trim() || 'Web 登录',
109
+ targetUrl,
110
+ steps: [{ id: randomUUID(), type: 'goto', url: targetUrl }],
111
+ };
112
+ this.sessions.set(clientId, session);
113
+ return createScript(session);
114
+ }
115
+ async snapshot(clientId) {
116
+ const session = this.requireSession(clientId);
117
+ const screenshotBase64 = (await session.page.screenshot({ type: 'png' })).toString('base64');
118
+ const viewport = session.page.viewportSize() || { width: 1280, height: 800 };
119
+ const nodes = await session.page.evaluate(`(() => {
120
+ ${selectorHelper}
121
+ const seen = new Set();
122
+ return Array.from(document.querySelectorAll('*')).map(function(source, index) {
123
+ const element = interactiveAncestor(source);
124
+ if (!isInteractive(element) || seen.has(element)) return null;
125
+ seen.add(element);
126
+ const rect = element.getBoundingClientRect();
127
+ return {
128
+ id: 'web-' + index,
129
+ bounds: [rect.left, rect.top, rect.right, rect.bottom],
130
+ selector: selectorFor(element),
131
+ editable: isEditable(element),
132
+ password: element.type === 'password',
133
+ text: (
134
+ element.innerText || element.textContent ||
135
+ element.placeholder || element.name || element.getAttribute('aria-label') || ''
136
+ ).trim().replace(/\\s+/g, ' ').slice(0, 80),
137
+ };
138
+ }).filter(Boolean).sort(function(a, b) {
139
+ const areaA = (a.bounds[2] - a.bounds[0]) * (a.bounds[3] - a.bounds[1]);
140
+ const areaB = (b.bounds[2] - b.bounds[0]) * (b.bounds[3] - b.bounds[1]);
141
+ return areaA - areaB;
142
+ });
143
+ })()`);
144
+ return { screenshotBase64, width: viewport.width, height: viewport.height, nodes };
145
+ }
146
+ async select(clientId, x, y) {
147
+ const session = this.requireSession(clientId);
148
+ if (!Number.isFinite(x) || !Number.isFinite(y)) {
149
+ throw new Error('Invalid element coordinates');
150
+ }
151
+ const element = await session.page.evaluate(`(() => {
152
+ ${selectorHelper}
153
+ const source = document.elementFromPoint(${JSON.stringify(x)}, ${JSON.stringify(y)});
154
+ if (!source) return null;
155
+ const target = interactiveAncestor(source);
156
+ return {
157
+ selector: selectorFor(target),
158
+ editable: isEditable(target),
159
+ password: target.type === 'password',
160
+ };
161
+ })()`);
162
+ if (!element)
163
+ throw new Error('No element found at selected point');
164
+ const locator = session.page.locator(element.selector).first();
165
+ await locator.click();
166
+ session.focusedSelector = element.selector;
167
+ const step = {
168
+ id: randomUUID(),
169
+ type: 'click',
170
+ selector: element.selector,
171
+ timeout: 10000,
172
+ };
173
+ session.steps.push(step);
174
+ return { step, passwordField: element.editable ? element.password : false };
175
+ }
176
+ async input(clientId, text, recordingValue, secret) {
177
+ const session = this.requireSession(clientId);
178
+ if (!text)
179
+ throw new Error('Input text is empty');
180
+ if (!session.focusedSelector)
181
+ throw new Error('Please select an input element first');
182
+ await session.page.locator(session.focusedSelector).first().fill(text);
183
+ const step = {
184
+ id: randomUUID(),
185
+ type: 'input',
186
+ selector: session.focusedSelector,
187
+ value: recordingValue,
188
+ secret,
189
+ clearBeforeInput: true,
190
+ timeout: 10000,
191
+ };
192
+ session.steps.push(step);
193
+ return step;
194
+ }
195
+ async stop(clientId) {
196
+ const session = this.requireSession(clientId);
197
+ const script = createScript(session);
198
+ await this.cancel(clientId);
199
+ return script;
200
+ }
201
+ async replay(options) {
202
+ const browser = await chromium.launch({ headless: true });
203
+ try {
204
+ const context = await browser.newContext({ viewport: { width: 1280, height: 800 } });
205
+ const page = await context.newPage();
206
+ await replayWebLoginScript({ page, script: options.script, variables: options.variables });
207
+ }
208
+ finally {
209
+ await browser.close();
210
+ }
211
+ }
212
+ async cancel(clientId) {
213
+ const session = this.sessions.get(clientId);
214
+ this.sessions.delete(clientId);
215
+ if (session)
216
+ await session.browser.close().catch(() => undefined);
217
+ }
218
+ requireSession(clientId) {
219
+ const session = this.sessions.get(clientId);
220
+ if (!session)
221
+ throw new Error('No active Web recording session');
222
+ return session;
223
+ }
224
+ }
225
+ export const webRecordingManager = new WebRecordingManager();
226
+ //# sourceMappingURL=web-recording-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web-recording-manager.js","sourceRoot":"","sources":["../../src/recorder/web-recording-manager.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/scrcpy/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AAaH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAWxD,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAgGD,qBAAa,YAAY;IACvB,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,GAAG,CAAkB;IAC7B,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,SAAS,CAAa;IAC9B,OAAO,CAAC,eAAe,CAAuB;IAC9C,OAAO,CAAC,kBAAkB,CAA+B;IACzD,OAAO,CAAC,kBAAkB,CAAM;IAChC,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,OAAO,CAAS;IACxB,iCAAiC;IACjC,OAAO,CAAC,QAAQ,CAAiD;gBAErD,MAAM,EAAE,YAAY;YAmBlB,iBAAiB;YA6CjB,YAAY;YA2BZ,cAAc;YAuBd,MAAM;YAqCN,gBAAgB;YA2BhB,WAAW;IAwEzB,OAAO,CAAC,aAAa;IAMrB,OAAO,CAAC,oBAAoB;IA4B5B,OAAO,CAAC,sBAAsB;YAwBhB,iBAAiB;IAa/B,wDAAwD;YAC1C,cAAc;IAqB5B,OAAO,CAAC,oBAAoB;IAyd5B,OAAO,CAAC,qBAAqB;IAYvB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAoBtB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CA6B7B"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/scrcpy/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AAYH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAsBxD,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAiGD,qBAAa,YAAY;IACvB,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,GAAG,CAAkB;IAC7B,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,SAAS,CAAa;IAC9B,OAAO,CAAC,eAAe,CAAuB;IAC9C,OAAO,CAAC,kBAAkB,CAA+B;IACzD,OAAO,CAAC,kBAAkB,CAAM;IAChC,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,OAAO,CAAS;IACxB,iCAAiC;IACjC,OAAO,CAAC,QAAQ,CAAiD;gBAErD,MAAM,EAAE,YAAY;YAmBlB,iBAAiB;YA6CjB,YAAY;YA2BZ,cAAc;YAuBd,MAAM;YAqCN,gBAAgB;YA2BhB,WAAW;IAwEzB,OAAO,CAAC,aAAa;IAMrB,OAAO,CAAC,oBAAoB;IA4B5B,OAAO,CAAC,sBAAsB;YAyBhB,iBAAiB;IAa/B,wDAAwD;YAC1C,cAAc;IAqB5B,OAAO,CAAC,oBAAoB;IAyqB5B,OAAO,CAAC,qBAAqB;IAYvB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAoBtB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CA6B7B"}