@aiscene/shared 1.6.0

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 (212) hide show
  1. package/README.md +9 -0
  2. package/dist/es/baseDB.mjs +109 -0
  3. package/dist/es/build/copy-static.mjs +31 -0
  4. package/dist/es/build/rspack-config.mjs +4 -0
  5. package/dist/es/cli/cli-runner.mjs +140 -0
  6. package/dist/es/cli/index.mjs +2 -0
  7. package/dist/es/common.mjs +37 -0
  8. package/dist/es/constants/example-code.mjs +223 -0
  9. package/dist/es/constants/index.mjs +23 -0
  10. package/dist/es/env/basic.mjs +6 -0
  11. package/dist/es/env/constants.mjs +70 -0
  12. package/dist/es/env/global-config-manager.mjs +94 -0
  13. package/dist/es/env/helper.mjs +43 -0
  14. package/dist/es/env/index.mjs +5 -0
  15. package/dist/es/env/init-debug.mjs +18 -0
  16. package/dist/es/env/model-config-manager.mjs +79 -0
  17. package/dist/es/env/parse-model-config.mjs +132 -0
  18. package/dist/es/env/types.mjs +211 -0
  19. package/dist/es/env/utils.mjs +26 -0
  20. package/dist/es/extractor/constants.mjs +2 -0
  21. package/dist/es/extractor/debug.mjs +6 -0
  22. package/dist/es/extractor/dom-util.mjs +92 -0
  23. package/dist/es/extractor/index.mjs +5 -0
  24. package/dist/es/extractor/locator.mjs +250 -0
  25. package/dist/es/extractor/tree.mjs +78 -0
  26. package/dist/es/extractor/util.mjs +245 -0
  27. package/dist/es/extractor/web-extractor.mjs +303 -0
  28. package/dist/es/img/box-select.mjs +824 -0
  29. package/dist/es/img/canvas-fallback.mjs +238 -0
  30. package/dist/es/img/get-photon.mjs +45 -0
  31. package/dist/es/img/get-sharp.mjs +11 -0
  32. package/dist/es/img/index.mjs +4 -0
  33. package/dist/es/img/info.mjs +29 -0
  34. package/dist/es/img/transform.mjs +275 -0
  35. package/dist/es/index.mjs +4 -0
  36. package/dist/es/logger.mjs +70 -0
  37. package/dist/es/mcp/base-server.mjs +281 -0
  38. package/dist/es/mcp/base-tools.mjs +91 -0
  39. package/dist/es/mcp/chrome-path.mjs +35 -0
  40. package/dist/es/mcp/index.mjs +7 -0
  41. package/dist/es/mcp/inject-report-html-plugin.mjs +53 -0
  42. package/dist/es/mcp/launcher-helper.mjs +52 -0
  43. package/dist/es/mcp/tool-generator.mjs +297 -0
  44. package/dist/es/mcp/types.mjs +3 -0
  45. package/dist/es/node/fs.mjs +44 -0
  46. package/dist/es/node/index.mjs +2 -0
  47. package/dist/es/node/port.mjs +24 -0
  48. package/dist/es/polyfills/async-hooks.mjs +2 -0
  49. package/dist/es/polyfills/index.mjs +1 -0
  50. package/dist/es/types/index.mjs +3 -0
  51. package/dist/es/us-keyboard-layout.mjs +1414 -0
  52. package/dist/es/us-keyboard-layout.mjs.LICENSE.txt +5 -0
  53. package/dist/es/utils.mjs +77 -0
  54. package/dist/es/zod-schema-utils.mjs +54 -0
  55. package/dist/lib/baseDB.js +149 -0
  56. package/dist/lib/build/copy-static.js +79 -0
  57. package/dist/lib/build/rspack-config.js +38 -0
  58. package/dist/lib/cli/cli-runner.js +196 -0
  59. package/dist/lib/cli/index.js +48 -0
  60. package/dist/lib/common.js +93 -0
  61. package/dist/lib/constants/example-code.js +260 -0
  62. package/dist/lib/constants/index.js +96 -0
  63. package/dist/lib/env/basic.js +40 -0
  64. package/dist/lib/env/constants.js +113 -0
  65. package/dist/lib/env/global-config-manager.js +128 -0
  66. package/dist/lib/env/helper.js +80 -0
  67. package/dist/lib/env/index.js +90 -0
  68. package/dist/lib/env/init-debug.js +52 -0
  69. package/dist/lib/env/model-config-manager.js +113 -0
  70. package/dist/lib/env/parse-model-config.js +178 -0
  71. package/dist/lib/env/types.js +518 -0
  72. package/dist/lib/env/utils.js +72 -0
  73. package/dist/lib/extractor/constants.js +42 -0
  74. package/dist/lib/extractor/debug.js +12 -0
  75. package/dist/lib/extractor/dom-util.js +153 -0
  76. package/dist/lib/extractor/index.js +81 -0
  77. package/dist/lib/extractor/locator.js +296 -0
  78. package/dist/lib/extractor/tree.js +124 -0
  79. package/dist/lib/extractor/util.js +336 -0
  80. package/dist/lib/extractor/web-extractor.js +349 -0
  81. package/dist/lib/img/box-select.js +875 -0
  82. package/dist/lib/img/canvas-fallback.js +305 -0
  83. package/dist/lib/img/get-photon.js +82 -0
  84. package/dist/lib/img/get-sharp.js +45 -0
  85. package/dist/lib/img/index.js +92 -0
  86. package/dist/lib/img/info.js +83 -0
  87. package/dist/lib/img/transform.js +364 -0
  88. package/dist/lib/index.js +47 -0
  89. package/dist/lib/logger.js +123 -0
  90. package/dist/lib/mcp/base-server.js +331 -0
  91. package/dist/lib/mcp/base-tools.js +125 -0
  92. package/dist/lib/mcp/chrome-path.js +72 -0
  93. package/dist/lib/mcp/index.js +100 -0
  94. package/dist/lib/mcp/inject-report-html-plugin.js +98 -0
  95. package/dist/lib/mcp/launcher-helper.js +86 -0
  96. package/dist/lib/mcp/tool-generator.js +334 -0
  97. package/dist/lib/mcp/types.js +40 -0
  98. package/dist/lib/node/fs.js +97 -0
  99. package/dist/lib/node/index.js +65 -0
  100. package/dist/lib/node/port.js +61 -0
  101. package/dist/lib/polyfills/async-hooks.js +36 -0
  102. package/dist/lib/polyfills/index.js +58 -0
  103. package/dist/lib/types/index.js +37 -0
  104. package/dist/lib/us-keyboard-layout.js +1457 -0
  105. package/dist/lib/us-keyboard-layout.js.LICENSE.txt +5 -0
  106. package/dist/lib/utils.js +156 -0
  107. package/dist/lib/zod-schema-utils.js +97 -0
  108. package/dist/types/baseDB.d.ts +25 -0
  109. package/dist/types/build/copy-static.d.ts +31 -0
  110. package/dist/types/build/rspack-config.d.ts +8 -0
  111. package/dist/types/cli/cli-runner.d.ts +14 -0
  112. package/dist/types/cli/index.d.ts +2 -0
  113. package/dist/types/common.d.ts +12 -0
  114. package/dist/types/constants/example-code.d.ts +2 -0
  115. package/dist/types/constants/index.d.ts +21 -0
  116. package/dist/types/env/basic.d.ts +6 -0
  117. package/dist/types/env/constants.d.ts +40 -0
  118. package/dist/types/env/global-config-manager.d.ts +32 -0
  119. package/dist/types/env/helper.d.ts +4 -0
  120. package/dist/types/env/index.d.ts +4 -0
  121. package/dist/types/env/init-debug.d.ts +1 -0
  122. package/dist/types/env/model-config-manager.d.ts +25 -0
  123. package/dist/types/env/parse-model-config.d.ts +31 -0
  124. package/dist/types/env/types.d.ts +309 -0
  125. package/dist/types/env/utils.d.ts +38 -0
  126. package/dist/types/extractor/constants.d.ts +1 -0
  127. package/dist/types/extractor/debug.d.ts +1 -0
  128. package/dist/types/extractor/dom-util.d.ts +56 -0
  129. package/dist/types/extractor/index.d.ts +32 -0
  130. package/dist/types/extractor/locator.d.ts +9 -0
  131. package/dist/types/extractor/tree.d.ts +6 -0
  132. package/dist/types/extractor/util.d.ts +47 -0
  133. package/dist/types/extractor/web-extractor.d.ts +19 -0
  134. package/dist/types/img/box-select.d.ts +26 -0
  135. package/dist/types/img/canvas-fallback.d.ts +105 -0
  136. package/dist/types/img/get-photon.d.ts +19 -0
  137. package/dist/types/img/get-sharp.d.ts +3 -0
  138. package/dist/types/img/index.d.ts +3 -0
  139. package/dist/types/img/info.d.ts +29 -0
  140. package/dist/types/img/transform.d.ts +98 -0
  141. package/dist/types/index.d.ts +4 -0
  142. package/dist/types/logger.d.ts +6 -0
  143. package/dist/types/mcp/base-server.d.ts +93 -0
  144. package/dist/types/mcp/base-tools.d.ts +79 -0
  145. package/dist/types/mcp/chrome-path.d.ts +2 -0
  146. package/dist/types/mcp/index.d.ts +7 -0
  147. package/dist/types/mcp/inject-report-html-plugin.d.ts +18 -0
  148. package/dist/types/mcp/launcher-helper.d.ts +94 -0
  149. package/dist/types/mcp/tool-generator.d.ts +10 -0
  150. package/dist/types/mcp/types.d.ts +103 -0
  151. package/dist/types/node/fs.d.ts +15 -0
  152. package/dist/types/node/index.d.ts +2 -0
  153. package/dist/types/node/port.d.ts +8 -0
  154. package/dist/types/polyfills/async-hooks.d.ts +6 -0
  155. package/dist/types/polyfills/index.d.ts +4 -0
  156. package/dist/types/types/index.d.ts +34 -0
  157. package/dist/types/us-keyboard-layout.d.ts +32 -0
  158. package/dist/types/utils.d.ts +37 -0
  159. package/dist/types/zod-schema-utils.d.ts +23 -0
  160. package/package.json +121 -0
  161. package/src/baseDB.ts +158 -0
  162. package/src/build/copy-static.ts +68 -0
  163. package/src/build/rspack-config.ts +12 -0
  164. package/src/cli/cli-runner.ts +224 -0
  165. package/src/cli/index.ts +8 -0
  166. package/src/common.ts +67 -0
  167. package/src/constants/example-code.ts +223 -0
  168. package/src/constants/index.ts +29 -0
  169. package/src/env/basic.ts +12 -0
  170. package/src/env/constants.ts +234 -0
  171. package/src/env/global-config-manager.ts +191 -0
  172. package/src/env/helper.ts +58 -0
  173. package/src/env/index.ts +4 -0
  174. package/src/env/init-debug.ts +34 -0
  175. package/src/env/model-config-manager.ts +149 -0
  176. package/src/env/parse-model-config.ts +294 -0
  177. package/src/env/types.ts +533 -0
  178. package/src/env/utils.ts +89 -0
  179. package/src/extractor/constants.ts +5 -0
  180. package/src/extractor/debug.ts +10 -0
  181. package/src/extractor/dom-util.ts +226 -0
  182. package/src/extractor/index.ts +48 -0
  183. package/src/extractor/locator.ts +469 -0
  184. package/src/extractor/tree.ts +179 -0
  185. package/src/extractor/util.ts +482 -0
  186. package/src/extractor/web-extractor.ts +481 -0
  187. package/src/img/box-select.ts +588 -0
  188. package/src/img/canvas-fallback.ts +393 -0
  189. package/src/img/get-photon.ts +108 -0
  190. package/src/img/get-sharp.ts +18 -0
  191. package/src/img/index.ts +25 -0
  192. package/src/img/info.ts +75 -0
  193. package/src/img/transform.ts +553 -0
  194. package/src/index.ts +8 -0
  195. package/src/logger.ts +107 -0
  196. package/src/mcp/base-server.ts +502 -0
  197. package/src/mcp/base-tools.ts +185 -0
  198. package/src/mcp/chrome-path.ts +48 -0
  199. package/src/mcp/index.ts +7 -0
  200. package/src/mcp/inject-report-html-plugin.ts +119 -0
  201. package/src/mcp/launcher-helper.ts +200 -0
  202. package/src/mcp/tool-generator.ts +429 -0
  203. package/src/mcp/types.ts +112 -0
  204. package/src/node/fs.ts +84 -0
  205. package/src/node/index.ts +2 -0
  206. package/src/node/port.ts +37 -0
  207. package/src/polyfills/async-hooks.ts +6 -0
  208. package/src/polyfills/index.ts +4 -0
  209. package/src/types/index.ts +52 -0
  210. package/src/us-keyboard-layout.ts +723 -0
  211. package/src/utils.ts +166 -0
  212. package/src/zod-schema-utils.ts +133 -0
@@ -0,0 +1,224 @@
1
+ import { existsSync, writeFileSync } from 'node:fs';
2
+ import { tmpdir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ import dotenv from 'dotenv';
5
+ import { getDebug } from '../logger';
6
+ import type { BaseMidsceneTools } from '../mcp/base-tools';
7
+ import type {
8
+ ToolDefinition,
9
+ ToolResult,
10
+ ToolResultContent,
11
+ } from '../mcp/types';
12
+
13
+ const debug = getDebug('cli-runner');
14
+
15
+ interface CLICommand {
16
+ name: string;
17
+ def: ToolDefinition;
18
+ }
19
+
20
+ export interface CLIRunnerOptions {
21
+ stripPrefix?: string;
22
+ argv?: string[];
23
+ version?: string;
24
+ }
25
+
26
+ export class CLIError extends Error {
27
+ constructor(
28
+ message: string,
29
+ public exitCode = 1,
30
+ ) {
31
+ super(message);
32
+ }
33
+ }
34
+
35
+ export function parseValue(raw: string): unknown {
36
+ // JSON objects/arrays
37
+ if (raw.startsWith('{') || raw.startsWith('[')) {
38
+ try {
39
+ return JSON.parse(raw);
40
+ } catch {
41
+ // Not valid JSON, treat as string below
42
+ }
43
+ }
44
+
45
+ // Numbers
46
+ if (/^-?\d+(\.\d+)?$/.test(raw)) {
47
+ return Number(raw);
48
+ }
49
+
50
+ return raw;
51
+ }
52
+
53
+ export function parseCliArgs(args: string[]): Record<string, unknown> {
54
+ const result: Record<string, unknown> = {};
55
+
56
+ for (let i = 0; i < args.length; i++) {
57
+ const arg = args[i];
58
+ if (!arg.startsWith('--')) continue;
59
+
60
+ const body = arg.slice(2);
61
+ const eqIdx = body.indexOf('=');
62
+
63
+ if (eqIdx >= 0) {
64
+ // --key=value
65
+ result[body.slice(0, eqIdx)] = parseValue(body.slice(eqIdx + 1));
66
+ } else if (args[i + 1] && !args[i + 1].startsWith('--')) {
67
+ // --key value
68
+ i++;
69
+ result[body] = parseValue(args[i]);
70
+ } else {
71
+ // --flag (boolean)
72
+ result[body] = true;
73
+ }
74
+ }
75
+
76
+ return result;
77
+ }
78
+
79
+ function outputContentItem(item: ToolResultContent, isError: boolean): void {
80
+ switch (item.type) {
81
+ case 'text':
82
+ if (isError) {
83
+ console.error(item.text);
84
+ } else {
85
+ console.log(item.text);
86
+ }
87
+ break;
88
+
89
+ case 'image': {
90
+ const filename = `screenshot-${Date.now()}.png`;
91
+ const filepath = join(tmpdir(), filename);
92
+ writeFileSync(filepath, Buffer.from(item.data, 'base64'));
93
+ console.log(`Screenshot saved: ${filepath}`);
94
+ break;
95
+ }
96
+
97
+ default:
98
+ console.log(`[${item.type} content not displayed in CLI]`);
99
+ }
100
+ }
101
+
102
+ function outputResult(result: ToolResult): void {
103
+ for (const item of result.content) {
104
+ outputContentItem(item, result.isError ?? false);
105
+ }
106
+ }
107
+
108
+ export function removePrefix(name: string, prefix?: string): string {
109
+ if (prefix && name.startsWith(prefix)) {
110
+ return name.slice(prefix.length);
111
+ }
112
+ return name;
113
+ }
114
+
115
+ function printCommandHelp(scriptName: string, cmd: CLICommand): void {
116
+ const { def } = cmd;
117
+ console.log(`\nUsage: ${scriptName} ${cmd.name} [options]\n`);
118
+ console.log(def.description);
119
+
120
+ const schemaEntries = Object.entries(def.schema);
121
+ if (schemaEntries.length > 0) {
122
+ console.log('\nOptions:');
123
+ for (const [key, zodType] of schemaEntries) {
124
+ const desc = zodType.description ?? '';
125
+ console.log(` --${key.padEnd(20)} ${desc}`);
126
+ }
127
+ }
128
+ }
129
+
130
+ function printVersion(scriptName: string, version: string): void {
131
+ console.log(`${scriptName} v${version}`);
132
+ }
133
+
134
+ function printHelp(
135
+ scriptName: string,
136
+ commands: CLICommand[],
137
+ version?: string,
138
+ ): void {
139
+ if (version) {
140
+ printVersion(scriptName, version);
141
+ console.log('');
142
+ }
143
+ console.log(`\nUsage: ${scriptName} <command> [options]\n`);
144
+ console.log('Commands:');
145
+ for (const { name, def } of commands) {
146
+ console.log(` ${name.padEnd(30)} ${def.description}`);
147
+ }
148
+ console.log(` ${'version'.padEnd(30)} Show CLI version`);
149
+ console.log(`\nRun "${scriptName} <command> --help" for more info.`);
150
+ }
151
+
152
+ export async function runToolsCLI(
153
+ tools: BaseMidsceneTools,
154
+ scriptName: string,
155
+ options?: CLIRunnerOptions,
156
+ ): Promise<void> {
157
+ const rawArgs = options?.argv ?? process.argv.slice(2);
158
+ debug('CLI invoked: %s %s', scriptName, rawArgs.join(' '));
159
+
160
+ // Load .env from cwd before any tool initialization
161
+ const envFile = join(process.cwd(), '.env');
162
+ if (existsSync(envFile)) {
163
+ dotenv.config({ path: envFile });
164
+ }
165
+
166
+ await tools.initTools();
167
+
168
+ const commands: CLICommand[] = tools.getToolDefinitions().map((def) => ({
169
+ name: removePrefix(def.name, options?.stripPrefix).toLowerCase(),
170
+ def,
171
+ }));
172
+ const cliVersion = options?.version;
173
+
174
+ const [commandName, ...restArgs] = rawArgs;
175
+
176
+ if (!commandName || commandName === '--help' || commandName === '-h') {
177
+ debug('showing help (no command or --help flag)');
178
+ printHelp(scriptName, commands, cliVersion);
179
+ return;
180
+ }
181
+
182
+ if (
183
+ commandName === '--version' ||
184
+ commandName === '-v' ||
185
+ commandName.toLowerCase() === 'version'
186
+ ) {
187
+ if (!cliVersion) {
188
+ throw new CLIError('Failed to determine CLI version');
189
+ }
190
+ printVersion(scriptName, cliVersion);
191
+ return;
192
+ }
193
+
194
+ const match = commands.find(
195
+ (c) => c.name.toLowerCase() === commandName.toLowerCase(),
196
+ );
197
+ if (!match) {
198
+ debug('unknown command: %s', commandName);
199
+ console.error(`Unknown command: ${commandName}`);
200
+ printHelp(scriptName, commands, cliVersion);
201
+ throw new CLIError(`Unknown command: ${commandName}`);
202
+ }
203
+
204
+ const parsedArgs = parseCliArgs(restArgs);
205
+ debug('command: %s, args: %s', match.name, JSON.stringify(parsedArgs));
206
+
207
+ if (parsedArgs.help === true) {
208
+ debug('showing command help for: %s', match.name);
209
+ printCommandHelp(scriptName, match);
210
+ return;
211
+ }
212
+
213
+ const result = await match.def.handler(parsedArgs);
214
+ debug(
215
+ 'command %s completed, isError: %s',
216
+ match.name,
217
+ result.isError ?? false,
218
+ );
219
+ outputResult(result);
220
+ await tools.destroy();
221
+ if (result.isError) {
222
+ throw new CLIError('Command failed', 1);
223
+ }
224
+ }
@@ -0,0 +1,8 @@
1
+ export {
2
+ runToolsCLI,
3
+ CLIError,
4
+ parseValue,
5
+ parseCliArgs,
6
+ removePrefix,
7
+ } from './cli-runner';
8
+ export type { CLIRunnerOptions } from './cli-runner';
package/src/common.ts ADDED
@@ -0,0 +1,67 @@
1
+ import { existsSync, mkdirSync } from 'node:fs';
2
+ import { tmpdir } from 'node:os';
3
+ import path from 'node:path';
4
+ // do not import getBasicEnvValue and MIDSCENE_RUN_DIR directly from ./env,
5
+ // because it will cause circular dependency
6
+ import { getBasicEnvValue } from './env/basic';
7
+ import { MIDSCENE_RUN_DIR } from './env/types';
8
+ import { ifInNode } from './utils';
9
+
10
+ export const defaultRunDirName = 'midscene_run';
11
+ // Define locally for now to avoid import issues
12
+
13
+ export const getMidsceneRunDir = () => {
14
+ if (!ifInNode) {
15
+ return '';
16
+ }
17
+
18
+ return getBasicEnvValue(MIDSCENE_RUN_DIR) || defaultRunDirName;
19
+ };
20
+
21
+ export const getMidsceneRunBaseDir = () => {
22
+ if (!ifInNode) {
23
+ return '';
24
+ }
25
+
26
+ let basePath = path.resolve(process.cwd(), getMidsceneRunDir());
27
+
28
+ // Create a base directory
29
+ if (!existsSync(basePath)) {
30
+ try {
31
+ mkdirSync(basePath, { recursive: true });
32
+ } catch (error) {
33
+ // console.error(`Failed to create ${runDirName} directory: ${error}`);
34
+ basePath = path.join(tmpdir(), defaultRunDirName);
35
+ mkdirSync(basePath, { recursive: true });
36
+ }
37
+ }
38
+
39
+ return basePath;
40
+ };
41
+
42
+ /**
43
+ * Get the path to the midscene_run directory or a subdirectory within it.
44
+ * Creates the directory if it doesn't exist.
45
+ *
46
+ * @param subdir - Optional subdirectory name (e.g., 'log', 'report')
47
+ * @returns The absolute path to the requested directory
48
+ */
49
+ export const getMidsceneRunSubDir = (
50
+ subdir: 'dump' | 'cache' | 'report' | 'tmp' | 'log' | 'output',
51
+ ): string => {
52
+ if (!ifInNode) {
53
+ return '';
54
+ }
55
+
56
+ // Create a log directory
57
+ const basePath = getMidsceneRunBaseDir();
58
+ const logPath = path.join(basePath, subdir);
59
+ if (!existsSync(logPath)) {
60
+ mkdirSync(logPath, { recursive: true });
61
+ }
62
+
63
+ return logPath;
64
+ };
65
+
66
+ export const ERROR_CODE_NOT_IMPLEMENTED_AS_DESIGNED =
67
+ 'NOT_IMPLEMENTED_AS_DESIGNED';
@@ -0,0 +1,223 @@
1
+ export const PLAYWRIGHT_EXAMPLE_CODE = `
2
+ // Reference the following code to generate Midscene test cases
3
+ // The following is test code for Midscene AI, for reference
4
+ // The following is Playwright syntax, you can use Playwright to assist in test generation
5
+ IMPORTANT: Follow these exact type signatures for AI functions:
6
+
7
+ // Type signatures for AI functions:
8
+ aiAct(prompt: string, options?: { cacheable?: boolean, deepThink?: 'unset' | true | false }): Promise<void>
9
+ aiInput(text: string, locate: string, options?: { deepLocate?: boolean, xpath?: string, cacheable?: boolean }): Promise<void>
10
+ aiTap(locate: string, options?: { deepLocate?: boolean, xpath?: string, cacheable?: boolean }): Promise<void>
11
+ aiHover(locate: string, options?: { deepLocate?: boolean, xpath?: string, cacheable?: boolean }): Promise<void>
12
+ aiDoubleClick(locate: string, options?: { deepLocate?: boolean, xpath?: string, cacheable?: boolean }): Promise<void>
13
+ aiKeyboardPress(key: string, locate?: string, options?: { deepLocate?: boolean, xpath?: string, cacheable?: boolean }): Promise<void>
14
+ aiScroll(locate: string | undefined, options: {
15
+ direction?: 'up' | 'down' | 'left' | 'right',
16
+ scrollType?: 'singleAction' | 'scrollToBottom' | 'scrollToTop' | 'scrollToRight' | 'scrollToLeft',
17
+ distance?: number | null,
18
+ deepLocate?: boolean,
19
+ xpath?: string,
20
+ cacheable?: boolean
21
+ }): Promise<void>
22
+ aiAssert(assertion: string, options?: { errorMessage?: string }): Promise<void>
23
+ aiWaitFor(prompt: string, options?: { timeout?: number }): Promise<void>
24
+ aiQuery<T>(queryObject: Record<string, string>): Promise<T> // Extracts data from page based on descriptions
25
+
26
+ // examples:
27
+ // Reference the following code to generate Midscene test cases
28
+ // The following is test code for Midscene AI, for reference
29
+ // The following is Playwright syntax, you can use Playwright to assist in test generation
30
+ import { test as base } from '@playwright/test';
31
+ import type { PlayWrightAiFixtureType } from '@midscene/web/playwright';
32
+ import { PlaywrightAiFixture } from '@midscene/web/playwright';
33
+
34
+ const test = base.extend<PlayWrightAiFixtureType>(PlaywrightAiFixture({
35
+ waitForNetworkIdleTimeout: 2000, // optional, the timeout for waiting for network idle between each action, default is 2000ms
36
+ }));
37
+
38
+
39
+ test.beforeEach(async ({ page }) => {
40
+ await page.goto('https://www.xxx.com/');
41
+ await page.setViewportSize({ width: 1920, height: 1080 });
42
+ });
43
+
44
+ test('ai shop', async ({
45
+ aiAct,
46
+ aiInput,
47
+ aiAssert,
48
+ aiQuery,
49
+ aiKeyboardPress,
50
+ aiHover,
51
+ aiTap,
52
+ aiWaitFor,
53
+ agentForPage,
54
+ page,
55
+ }) => {
56
+ // login
57
+ await aiAssert('The page shows the login interface');
58
+ await aiInput('user_name', 'in user name input');
59
+ await aiInput('password', 'in password input');
60
+ await aiKeyboardPress('Enter', 'Login Button');
61
+
62
+ // check the login success
63
+ await aiWaitFor('The page shows that the loading is complete');
64
+ await aiAssert('The current page shows the product detail page');
65
+
66
+ // check the product info
67
+ const dataA = await aiQuery({
68
+ userInfo: 'User information in the format {name: string}',
69
+ theFirstProductInfo: 'The first product info in the format {name: string, price: number}',
70
+ });
71
+ expect(dataA.theFirstProductInfo.name).toBe('xxx');
72
+ expect(dataA.theFirstProductInfo.price).toBe(100);
73
+
74
+
75
+ // add to cart
76
+ await aiTap('click add to cart button');
77
+
78
+ await aiTap('click right top cart icon');
79
+ await aiAssert('The cart icon shows the number 1');
80
+ });
81
+ `;
82
+
83
+ export const YAML_EXAMPLE_CODE = `
84
+ 1. Format:
85
+
86
+ web:
87
+ url: "starting_url"
88
+ viewportWidth: 1280
89
+ viewportHeight: 960
90
+
91
+ tasks:
92
+ - name: "descriptive task name"
93
+ flow:
94
+ - aiTap: "element description"
95
+ - aiInput: 'text value'
96
+ locate: 'input field description'
97
+ - aiScroll:
98
+ direction: down/up
99
+ scrollType: scrollToBottom/scrollToTop/singleAction
100
+ - aiAssert: "expected state"
101
+ - sleep: milliseconds
102
+
103
+ 2. Action Types:
104
+ - aiTap: for clicks (natural language targeting)
105
+ - aiInput: for text input with 'locate' field
106
+ - aiScroll: with direction and scrollType
107
+ - aiAssert: for validations
108
+ - sleep: for delays (milliseconds)
109
+
110
+ 3. Best Practices:
111
+ - Group related actions into logical tasks
112
+ - Use natural language descriptions
113
+ - Add deepLocate: true for complex interactions
114
+ - Keep task names concise but descriptive
115
+
116
+ 4. CRITICAL - YAML Indentation Rules:
117
+ - For actions with additional parameters (aiScroll, aiInput, aiKeyboardPress), the parameters must be SIBLING keys, NOT nested children
118
+ - Parameters like direction, scrollType, locate must align with the action key, not indented further
119
+ - CORRECT indentation example:
120
+ - aiScroll:
121
+ direction: down
122
+ scrollType: singleAction
123
+ - WRONG indentation (DO NOT do this):
124
+ - aiScroll:
125
+ direction: down
126
+ scrollType: singleAction
127
+
128
+
129
+
130
+ YAML type
131
+ tasks:
132
+ - name: <name>
133
+ continueOnError: <boolean> # Optional, whether to continue to the next task on error, defaults to false.
134
+ flow:
135
+ # Auto Planning (.ai)
136
+ # ----------------
137
+
138
+ # Perform an interaction. \`ai\` is a shorthand for \`aiAct\`.
139
+ - ai: <prompt>
140
+ cacheable: <boolean> # Optional, whether to cache the result of this API call when the [caching feature](./caching.mdx) is enabled. Defaults to True.
141
+
142
+ # This usage is the same as \`ai\`.
143
+ - aiAct: <prompt>
144
+ cacheable: <boolean> # Optional, whether to cache the result of this API call when the [caching feature](./caching.mdx) is enabled. Defaults to True.
145
+
146
+ # Instant Action (.aiTap, .aiDoubleClick, .aiHover, .aiInput, .aiKeyboardPress, .aiScroll)
147
+ # ----------------
148
+
149
+ # Tap an element described by a prompt.
150
+ - aiTap: <prompt>
151
+ deepLocate: <boolean> # Optional, whether to use deepLocate to precisely locate the element. Defaults to False.
152
+ xpath: <xpath> # Optional, the xpath of the target element for the operation. If provided, Midscene will prioritize this xpath to find the element before using the cache and the AI model. Defaults to empty.
153
+ cacheable: <boolean> # Optional, whether to cache the result of this API call when the [caching feature](./caching.mdx) is enabled. Defaults to True.
154
+
155
+ # Double click an element described by a prompt.
156
+ - aiDoubleClick: <prompt>
157
+ deepLocate: <boolean> # Optional, whether to use deepLocate to precisely locate the element. Defaults to False.
158
+ xpath: <xpath> # Optional, the xpath of the target element for the operation. If provided, Midscene will prioritize this xpath to find the element before using the cache and the AI model. Defaults to empty.
159
+ cacheable: <boolean> # Optional, whether to cache the result of this API call when the [caching feature](./caching.mdx) is enabled. Defaults to True.
160
+
161
+ # Hover over an element described by a prompt.
162
+ - aiHover: <prompt>
163
+ deepLocate: <boolean> # Optional, whether to use deepLocate to precisely locate the element. Defaults to False.
164
+ xpath: <xpath> # Optional, the xpath of the target element for the operation. If provided, Midscene will prioritize this xpath to find the element before using the cache and the AI model. Defaults to empty.
165
+ cacheable: <boolean> # Optional, whether to cache the result of this API call when the [caching feature](./caching.mdx) is enabled. Defaults to True.
166
+
167
+ # Input text into an element described by a prompt.
168
+ - aiInput: <final text content of the input>
169
+ locate: <prompt>
170
+ deepLocate: <boolean> # Optional, whether to use deepLocate to precisely locate the element. Defaults to False.
171
+ xpath: <xpath> # Optional, the xpath of the target element for the operation. If provided, Midscene will prioritize this xpath to find the element before using the cache and the AI model. Defaults to empty.
172
+ cacheable: <boolean> # Optional, whether to cache the result of this API call when the [caching feature](./caching.mdx) is enabled. Defaults to True.
173
+
174
+ # Press a key (e.g., Enter, Tab, Escape) on an element described by a prompt.
175
+ - aiKeyboardPress: <key>
176
+ locate: <prompt>
177
+ deepLocate: <boolean> # Optional, whether to use deepLocate to precisely locate the element. Defaults to False.
178
+ xpath: <xpath> # Optional, the xpath of the target element for the operation. If provided, Midscene will prioritize this xpath to find the element before using the cache and the AI model. Defaults to empty.
179
+ cacheable: <boolean> # Optional, whether to cache the result of this API call when the [caching feature](./caching.mdx) is enabled. Defaults to True.
180
+
181
+ # Scroll globally or on an element described by a prompt.
182
+ - aiScroll:
183
+ direction: 'up' # or 'down' | 'left' | 'right'
184
+ scrollType: 'singleAction' # or 'scrollToTop' | 'scrollToBottom' | 'scrollToLeft' | 'scrollToRight'
185
+ distance: <number> # Optional, the scroll distance in pixels.
186
+ locate: <prompt> # Optional, the element to scroll on.
187
+ deepLocate: <boolean> # Optional, whether to use deepLocate to precisely locate the element. Defaults to False.
188
+ xpath: <xpath> # Optional, the xpath of the target element for the operation. If provided, Midscene will prioritize this xpath to find the element before using the cache and the AI model. Defaults to empty.
189
+ cacheable: <boolean> # Optional, whether to cache the result of this API call when the [caching feature](./caching.mdx) is enabled. Defaults to True.
190
+
191
+ # Record the current screenshot with a description in the report file.
192
+ - recordToReport: <title> # Optional, the title of the screenshot. If not provided, the title will be 'untitled'.
193
+ content: <content> # Optional, the description of the screenshot.
194
+
195
+ # Data Extraction
196
+ # ----------------
197
+
198
+ # Perform a query that returns a JSON object.
199
+ - aiQuery: <prompt> # Remember to describe the format of the result in the prompt.
200
+ name: <name> # The key for the query result in the JSON output.
201
+
202
+ # More APIs
203
+ # ----------------
204
+
205
+ # Wait for a condition to be met, with a timeout (in ms, optional, defaults to 30000).
206
+ - aiWaitFor: <prompt>
207
+ timeout: <ms>
208
+
209
+ # Perform an assertion.
210
+ - aiAssert: <prompt>
211
+ errorMessage: <error-message> # Optional, the error message to print if the assertion fails.
212
+
213
+ # Wait for a specified amount of time.
214
+ - sleep: <ms>
215
+
216
+ # Execute a piece of JavaScript code in the web page context.
217
+ - javascript: <javascript>
218
+ name: <name> # Optional, assign a name to the return value, which will be used as a key in the JSON output.
219
+
220
+ - name: <name>
221
+ flow:
222
+ # ...
223
+ `;
@@ -0,0 +1,29 @@
1
+ export const TEXT_SIZE_THRESHOLD = 9;
2
+
3
+ export const TEXT_MAX_SIZE = 40;
4
+
5
+ export const CONTAINER_MINI_HEIGHT = 3;
6
+ export const CONTAINER_MINI_WIDTH = 3;
7
+
8
+ export enum NodeType {
9
+ CONTAINER = 'CONTAINER Node',
10
+ FORM_ITEM = 'FORM_ITEM Node',
11
+ BUTTON = 'BUTTON Node',
12
+ A = 'Anchor Node',
13
+ IMG = 'IMG Node',
14
+ TEXT = 'TEXT Node',
15
+ POSITION = 'POSITION Node',
16
+ }
17
+
18
+ export const PLAYGROUND_SERVER_PORT = 5800;
19
+ export const SCRCPY_SERVER_PORT = 5700;
20
+
21
+ // WebDriver constants
22
+ export const WEBDRIVER_ELEMENT_ID_KEY = 'element-6066-11e4-a52e-4f735466cecf';
23
+ export const DEFAULT_WDA_PORT = 8100;
24
+
25
+ export const DEFAULT_WAIT_FOR_NAVIGATION_TIMEOUT = 5000;
26
+ export const DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT = 2000;
27
+ export const DEFAULT_WAIT_FOR_NETWORK_IDLE_CONCURRENCY = 2;
28
+
29
+ export { PLAYWRIGHT_EXAMPLE_CODE, YAML_EXAMPLE_CODE } from './example-code';
@@ -0,0 +1,12 @@
1
+ import { BASIC_ENV_KEYS } from './types';
2
+
3
+ /**
4
+ * get basic env value from process.env
5
+ * use a single file to avoid circular dependency
6
+ */
7
+ export const getBasicEnvValue = (key: (typeof BASIC_ENV_KEYS)[number]) => {
8
+ if (!BASIC_ENV_KEYS.includes(key)) {
9
+ throw new Error(`getBasicEnvValue with key ${key} is not supported.`);
10
+ }
11
+ return process.env[key];
12
+ };