@midscene/cli 1.12.10-beta-20260920072249.0 → 1.13.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.
package/dist/lib/index.js CHANGED
@@ -3014,7 +3014,6 @@ var __webpack_modules__ = {
3014
3014
  });
3015
3015
  var external_node_fs_ = __webpack_require__("node:fs");
3016
3016
  var external_node_path_ = __webpack_require__("node:path");
3017
- var external_node_path_default = /*#__PURE__*/ __webpack_require__.n(external_node_path_);
3018
3017
  const external_node_crypto_namespaceObject = require("node:crypto");
3019
3018
  var core_ = __webpack_require__("@midscene/core");
3020
3019
  const common_namespaceObject = require("@midscene/shared/common");
@@ -3479,282 +3478,7 @@ var __webpack_modules__ = {
3479
3478
  }
3480
3479
  const external_puppeteer_namespaceObject = require("puppeteer");
3481
3480
  var external_puppeteer_default = /*#__PURE__*/ __webpack_require__.n(external_puppeteer_namespaceObject);
3482
- const external_http_server_namespaceObject = require("http-server");
3483
- const external_node_assert_namespaceObject = require("node:assert");
3484
- var external_node_assert_default = /*#__PURE__*/ __webpack_require__.n(external_node_assert_namespaceObject);
3485
- const agent_namespaceObject = require("@midscene/core/agent");
3486
- const utils_namespaceObject = require("@midscene/core/utils");
3487
- const bridge_mode_namespaceObject = require("@midscene/web/bridge-mode");
3488
- const debug = (0, logger_.getDebug)('create-yaml-player');
3489
- const cleanupWarning = (0, logger_.getDebug)('create-yaml-player', {
3490
- console: true
3491
- });
3492
- async function cleanupFailedPlayerSetup(freeFn) {
3493
- for (const cleanup of [
3494
- ...freeFn
3495
- ].reverse())try {
3496
- await cleanup.fn();
3497
- } catch (error) {
3498
- cleanupWarning(`failed to run ${cleanup.name} after YAML player setup failed`, error);
3499
- }
3500
- }
3501
- const launchServer = async (dir)=>new Promise((resolve)=>{
3502
- const server = (0, external_http_server_namespaceObject.createServer)({
3503
- root: dir
3504
- });
3505
- server.listen(0, '127.0.0.1', ()=>{
3506
- resolve(server);
3507
- });
3508
- });
3509
- function resolveReportFileName(yamlReportFileName, cliTestId, yamlTestId, fileName) {
3510
- if (void 0 !== yamlReportFileName) return yamlReportFileName;
3511
- const baseName = cliTestId ?? yamlTestId ?? fileName;
3512
- return (0, agent_namespaceObject.getReportFileName)(baseName);
3513
- }
3514
- function buildAgentOptions(yamlAgent, reportFileName, fileName) {
3515
- return {
3516
- ...yamlAgent || {},
3517
- cache: (0, utils_namespaceObject.processCacheConfig)(yamlAgent?.cache, fileName),
3518
- reportFileName
3519
- };
3520
- }
3521
- async function create_yaml_player_createYamlPlayer(file, script, options) {
3522
- const yamlScript = script || (0, yaml_.parseYamlScript)((0, external_node_fs_.readFileSync)(file, 'utf-8'), file);
3523
- const clonedYamlScript = structuredClone(yamlScript);
3524
- const fileName = (0, external_node_path_.basename)(file, (0, external_node_path_.extname)(file));
3525
- const preference = {
3526
- headed: options?.headed,
3527
- keepWindow: options?.keepWindow,
3528
- reportFileName: resolveReportFileName(clonedYamlScript.agent?.reportFileName, options?.testId, clonedYamlScript.agent?.testId, fileName)
3529
- };
3530
- const player = new yaml_.ScriptPlayer(clonedYamlScript, async ()=>{
3531
- const freeFn = [];
3532
- try {
3533
- const resolvedWebTarget = (0, yaml_.resolveWebTarget)(clonedYamlScript);
3534
- const webTarget = resolvedWebTarget?.target;
3535
- const targetCount = [
3536
- void 0 !== resolvedWebTarget,
3537
- void 0 !== clonedYamlScript.android,
3538
- void 0 !== clonedYamlScript.ios,
3539
- void 0 !== clonedYamlScript.harmony,
3540
- void 0 !== clonedYamlScript.computer,
3541
- void 0 !== clonedYamlScript.interface
3542
- ].filter(Boolean).length;
3543
- if (targetCount > 1) {
3544
- const specifiedTargets = [
3545
- resolvedWebTarget?.source ?? null,
3546
- void 0 !== clonedYamlScript.android ? 'android' : null,
3547
- void 0 !== clonedYamlScript.ios ? 'ios' : null,
3548
- void 0 !== clonedYamlScript.harmony ? 'harmony' : null,
3549
- void 0 !== clonedYamlScript.computer ? 'computer' : null,
3550
- void 0 !== clonedYamlScript.interface ? 'interface' : null
3551
- ].filter(Boolean);
3552
- throw new Error(`Only one target type can be specified, but found multiple: ${specifiedTargets.join(', ')}. Please specify only one of: page, browser, web, android, ios, harmony, computer, or interface.`);
3553
- }
3554
- if (void 0 !== webTarget) {
3555
- if (resolvedWebTarget?.source === 'target') console.warn("target is deprecated, please use page or browser instead. See https://midscenejs.com/automate-with-scripts-in-yaml for more information. Sorry for the inconvenience.");
3556
- let localServer;
3557
- let urlToVisit;
3558
- if (webTarget.serve) {
3559
- external_node_assert_default()('string' == typeof webTarget.url, 'url is required in serve mode');
3560
- localServer = await launchServer(webTarget.serve);
3561
- const serverAddress = localServer.server.address();
3562
- freeFn.push({
3563
- name: 'local_server',
3564
- fn: async ()=>{
3565
- if (!localServer) return;
3566
- await new Promise((resolve, reject)=>{
3567
- localServer?.server.close((error)=>{
3568
- if (error) reject(error);
3569
- else resolve();
3570
- });
3571
- });
3572
- }
3573
- });
3574
- urlToVisit = webTarget.url.startsWith('/') ? `http://${serverAddress?.address}:${serverAddress?.port}${webTarget.url}` : `http://${serverAddress?.address}:${serverAddress?.port}/${webTarget.url}`;
3575
- webTarget.url = urlToVisit;
3576
- }
3577
- if (webTarget.cdpEndpoint && webTarget.bridgeMode) throw new Error('cdpEndpoint and bridgeMode are mutually exclusive. Please specify only one.');
3578
- if ('browser' === webTarget.mode && webTarget.bridgeMode) throw new Error('[midscene] browser mode does not support bridgeMode. Use page: or web.mode: page for bridge mode.');
3579
- if (webTarget.cdpEndpoint) {
3580
- const ownsCdpBrowser = !options?.browser;
3581
- const cdpBrowser = options?.browser ?? await external_puppeteer_default().connect({
3582
- browserWSEndpoint: webTarget.cdpEndpoint,
3583
- defaultViewport: null,
3584
- downloadBehavior: (0, puppeteer_agent_launcher_namespaceObject.buildDownloadBehavior)(webTarget.downloadPath)
3585
- });
3586
- if (webTarget.chromeArgs) console.warn('chromeArgs are not supported in CDP mode (browser is already running). They will be ignored.');
3587
- let launchedAgent;
3588
- try {
3589
- launchedAgent = await (0, puppeteer_agent_launcher_namespaceObject.puppeteerAgentForTarget)(webTarget, {
3590
- ...preference,
3591
- ...buildAgentOptions(clonedYamlScript.agent, preference.reportFileName, fileName)
3592
- }, cdpBrowser, options?.page, options?.browserContext);
3593
- } catch (error) {
3594
- if (ownsCdpBrowser) try {
3595
- cdpBrowser.disconnect();
3596
- } catch (cleanupError) {
3597
- cleanupWarning('failed to disconnect the CDP browser after agent setup failed', cleanupError);
3598
- }
3599
- throw error;
3600
- }
3601
- const { agent, freeFn: newFreeFn } = launchedAgent;
3602
- const cleanFreeFn = newFreeFn.filter((f)=>'puppeteer_browser' !== f.name);
3603
- freeFn.push(...cleanFreeFn);
3604
- if (ownsCdpBrowser) freeFn.push({
3605
- name: 'cdp_browser_disconnect',
3606
- fn: ()=>cdpBrowser.disconnect()
3607
- });
3608
- return {
3609
- agent,
3610
- freeFn
3611
- };
3612
- }
3613
- if (!webTarget.bridgeMode) {
3614
- const { agent, freeFn: newFreeFn } = await (0, puppeteer_agent_launcher_namespaceObject.puppeteerAgentForTarget)(webTarget, {
3615
- ...preference,
3616
- ...buildAgentOptions(clonedYamlScript.agent, preference.reportFileName, fileName)
3617
- }, options?.browser, options?.page, options?.browserContext);
3618
- freeFn.push(...newFreeFn);
3619
- return {
3620
- agent,
3621
- freeFn
3622
- };
3623
- }
3624
- external_node_assert_default()('newTabWithUrl' === webTarget.bridgeMode || 'currentTab' === webTarget.bridgeMode, `bridgeMode config value must be either "newTabWithUrl" or "currentTab", but got ${webTarget.bridgeMode}`);
3625
- const bridgeUnsupportedKeys = [
3626
- 'userAgent',
3627
- 'viewportWidth',
3628
- 'viewportHeight',
3629
- 'deviceScaleFactor',
3630
- 'waitForNetworkIdle',
3631
- 'cookie',
3632
- 'extraHTTPHeaders',
3633
- 'downloadPath',
3634
- 'chromeArgs'
3635
- ];
3636
- const ignoredKeys = bridgeUnsupportedKeys.filter((key)=>null != webTarget[key]);
3637
- if (ignoredKeys.length > 0) console.warn(`puppeteer options (${ignoredKeys.join(', ')}) are not supported in bridge mode. They will be ignored.`);
3638
- const agent = new bridge_mode_namespaceObject.AgentOverChromeBridge({
3639
- closeNewTabsAfterDisconnect: webTarget.closeNewTabsAfterDisconnect,
3640
- closeConflictServer: true,
3641
- ...buildAgentOptions(clonedYamlScript.agent, preference.reportFileName, fileName)
3642
- });
3643
- if ('newTabWithUrl' === webTarget.bridgeMode) await agent.connectNewTabWithUrl(webTarget.url);
3644
- else {
3645
- if (webTarget.url) console.warn('url will be ignored in bridge mode with "currentTab"');
3646
- await agent.connectCurrentTab();
3647
- }
3648
- freeFn.push({
3649
- name: 'destroy_agent_over_chrome_bridge',
3650
- fn: ()=>agent.destroy()
3651
- });
3652
- return {
3653
- agent,
3654
- freeFn
3655
- };
3656
- }
3657
- if (void 0 !== clonedYamlScript.android) {
3658
- const androidTarget = clonedYamlScript.android;
3659
- const { agentFromAdbDevice } = await import("@midscene/android");
3660
- const agent = await agentFromAdbDevice(androidTarget?.deviceId, {
3661
- ...androidTarget,
3662
- ...buildAgentOptions(clonedYamlScript.agent, preference.reportFileName, fileName)
3663
- });
3664
- if (androidTarget?.launch) await agent.launch(androidTarget.launch);
3665
- freeFn.push({
3666
- name: 'destroy_android_agent',
3667
- fn: ()=>agent.destroy()
3668
- });
3669
- return {
3670
- agent,
3671
- freeFn
3672
- };
3673
- }
3674
- if (void 0 !== clonedYamlScript.ios) {
3675
- const iosTarget = clonedYamlScript.ios;
3676
- const { agentFromWebDriverAgent } = await import("@midscene/ios");
3677
- const agent = await agentFromWebDriverAgent({
3678
- ...iosTarget,
3679
- ...buildAgentOptions(clonedYamlScript.agent, preference.reportFileName, fileName)
3680
- });
3681
- if (iosTarget?.launch) await agent.launch(iosTarget.launch);
3682
- freeFn.push({
3683
- name: 'destroy_ios_agent',
3684
- fn: ()=>agent.destroy()
3685
- });
3686
- return {
3687
- agent,
3688
- freeFn
3689
- };
3690
- }
3691
- if (void 0 !== clonedYamlScript.harmony) {
3692
- const harmonyTarget = clonedYamlScript.harmony;
3693
- const { agentFromHdcDevice } = await import("@midscene/harmony");
3694
- const agent = await agentFromHdcDevice(harmonyTarget?.deviceId, {
3695
- ...harmonyTarget,
3696
- ...buildAgentOptions(clonedYamlScript.agent, preference.reportFileName, fileName)
3697
- });
3698
- if (harmonyTarget?.launch) await agent.launch(harmonyTarget.launch);
3699
- freeFn.push({
3700
- name: 'destroy_harmony_agent',
3701
- fn: ()=>agent.destroy()
3702
- });
3703
- return {
3704
- agent,
3705
- freeFn
3706
- };
3707
- }
3708
- if (void 0 !== clonedYamlScript.computer) {
3709
- const computerTarget = clonedYamlScript.computer;
3710
- const { agentForComputer } = await import("@midscene/computer");
3711
- const agent = await agentForComputer({
3712
- ...computerTarget,
3713
- ...buildAgentOptions(clonedYamlScript.agent, preference.reportFileName, fileName)
3714
- });
3715
- freeFn.push({
3716
- name: 'destroy_computer_agent',
3717
- fn: ()=>agent.destroy()
3718
- });
3719
- return {
3720
- agent,
3721
- freeFn
3722
- };
3723
- }
3724
- if (void 0 !== clonedYamlScript.interface) {
3725
- const interfaceTarget = clonedYamlScript.interface;
3726
- const moduleSpecifier = interfaceTarget.module;
3727
- let finalModuleSpecifier;
3728
- if (moduleSpecifier.startsWith('./') || moduleSpecifier.startsWith('../') || external_node_path_default().isAbsolute(moduleSpecifier)) {
3729
- const resolvedPath = (0, external_node_path_.join)(process.cwd(), moduleSpecifier);
3730
- finalModuleSpecifier = resolvedPath;
3731
- } else finalModuleSpecifier = moduleSpecifier;
3732
- debug('importing module config', interfaceTarget.module, 'with export config', interfaceTarget.export, 'final module specifier', finalModuleSpecifier);
3733
- const importedModule = await import(finalModuleSpecifier);
3734
- const DeviceClass = interfaceTarget.export ? importedModule[interfaceTarget.export] : importedModule.default || importedModule;
3735
- debug('DeviceClass', DeviceClass, 'with param', interfaceTarget.param);
3736
- const device = new DeviceClass(interfaceTarget.param || {});
3737
- debug('creating agent from device', device);
3738
- const agent = (0, agent_namespaceObject.createAgent)(device, buildAgentOptions(clonedYamlScript.agent, preference.reportFileName, fileName));
3739
- freeFn.push({
3740
- name: 'destroy_general_interface_agent',
3741
- fn: ()=>{
3742
- agent.destroy();
3743
- }
3744
- });
3745
- return {
3746
- agent,
3747
- freeFn
3748
- };
3749
- }
3750
- throw new Error('No valid interface configuration found in the yaml script, should be either "web", "android", "ios", "harmony", "computer", or "interface"');
3751
- } catch (error) {
3752
- await cleanupFailedPlayerSetup(freeFn);
3753
- throw error;
3754
- }
3755
- }, void 0, file);
3756
- return player;
3757
- }
3481
+ const yaml_runtime_namespaceObject = require("@midscene/test/internal/yaml-runtime");
3758
3482
  var chalk_source = __webpack_require__("../../node_modules/.pnpm/chalk@4.1.2/node_modules/chalk/source/index.js");
3759
3483
  var source_default = /*#__PURE__*/ __webpack_require__.n(chalk_source);
3760
3484
  function resolveIsTTY(env = process.env, stdoutIsTTY = process.stdout.isTTY) {
@@ -4401,7 +4125,7 @@ var __webpack_modules__ = {
4401
4125
  const createFilePlayerContext = async (context)=>{
4402
4126
  const fileContext = {
4403
4127
  file: context.file,
4404
- player: await create_yaml_player_createYamlPlayer(context.file, context.executionConfig, context.options)
4128
+ player: await (0, yaml_runtime_namespaceObject.createYamlPlayer)(context.file, context.executionConfig, context.options)
4405
4129
  };
4406
4130
  fileContextsByCaseId.set(context.caseId, fileContext);
4407
4131
  return fileContext;
@@ -4446,7 +4170,7 @@ var __webpack_modules__ = {
4446
4170
  if (attempt > 1) {
4447
4171
  if (context.executionConfig.agent?.reportFileName) attempts[attempts.length - 1] = execution_summary_preserveYamlAttemptReport(attempts[attempts.length - 1]);
4448
4172
  await beforeRetry?.();
4449
- allFileContext.player = await create_yaml_player_createYamlPlayer(context.file, context.executionConfig, context.options);
4173
+ allFileContext.player = await (0, yaml_runtime_namespaceObject.createYamlPlayer)(context.file, context.executionConfig, context.options);
4450
4174
  }
4451
4175
  if (onProgress) reportProgressSnapshot(allFileContext, attempt, totalAttempts);
4452
4176
  else if (!isTTY) {
@@ -5069,7 +4793,9 @@ defineYamlBatchTest(test, testOptions);
5069
4793
  "use strict";
5070
4794
  var core_ = __webpack_require__("@midscene/core");
5071
4795
  const cli_namespaceObject = require("@midscene/shared/cli");
5072
- var package_namespaceObject = JSON.parse('{"rE":"1.12.10-beta-20260920072249.0"}');
4796
+ var package_namespaceObject = {
4797
+ rE: "1.13.0"
4798
+ };
5073
4799
  var external_node_fs_ = __webpack_require__("node:fs");
5074
4800
  var external_node_fs_namespaceObject = /*#__PURE__*/ __webpack_require__.t(external_node_fs_, 2);
5075
4801
  var external_node_path_ = __webpack_require__("node:path");
@@ -10685,7 +10411,7 @@ defineYamlBatchTest(test, testOptions);
10685
10411
  const parseProcessArgs = async ()=>{
10686
10412
  const args = yargs(hideBin(process.argv)).parserConfiguration({
10687
10413
  'dot-notation': true
10688
- }).usage(`Midscene.js helps you automate browser actions, assertions, and data extraction by AI.
10414
+ }).usage(`Midscene legacy YAML runner executes tasks/flow scripts with AI.
10689
10415
  Homepage: https://midscenejs.com
10690
10416
  Github: https://github.com/web-infra-dev/midscene
10691
10417
 
@@ -10704,7 +10430,7 @@ Usage:
10704
10430
  },
10705
10431
  config: {
10706
10432
  type: 'string',
10707
- description: 'Path to a configuration file. Options in this file are used as defaults.'
10433
+ description: 'Path to a legacy YAML batch configuration file. Options in this file are used as defaults.'
10708
10434
  },
10709
10435
  summary: {
10710
10436
  type: 'string',
@@ -10746,7 +10472,7 @@ Usage:
10746
10472
  type: 'boolean',
10747
10473
  description: 'Whether to dismiss the HarmonyOS keyboard after text input'
10748
10474
  }
10749
- }).version('version', 'Show version number', "1.12.10-beta-20260920072249.0").help().epilogue(`For complete list of configuration options, please visit:
10475
+ }).version('version', 'Show version number', "1.13.0").help().epilogue(`For complete list of configuration options, please visit:
10750
10476
  • Web options: https://midscenejs.com/automate-with-scripts-in-yaml#the-web-part
10751
10477
  • Android options: https://midscenejs.com/automate-with-scripts-in-yaml#the-android-part
10752
10478
  • HarmonyOS options: https://midscenejs.com/automate-with-scripts-in-yaml#the-harmony-part
@@ -10757,7 +10483,9 @@ Examples:
10757
10483
  $0 script.yaml --android.device-id emulator-5554 --android.ime-strategy yadb-for-non-ascii
10758
10484
  $0 script.yaml --android.device-id emulator-5554 --android.screenshot-strategy always-yadb
10759
10485
  $0 script.yaml --harmony.device-id 127.0.0.1:5555
10760
- $0 script.yaml --ios.wda-port 8100 --ios.auto-dismiss-keyboard`).wrap(yargs().terminalWidth());
10486
+ $0 script.yaml --ios.wda-port 8100 --ios.auto-dismiss-keyboard
10487
+
10488
+ Native cases/steps Test projects use the midscene-test command.`).wrap(yargs().terminalWidth());
10761
10489
  const argv = await args.argv;
10762
10490
  debug('argv', argv);
10763
10491
  const transformedArgv = {
@@ -12868,6 +12596,7 @@ Examples:
12868
12596
  throw new Error('Function yaml.' + from + " is removed in js-yaml 4. Use yaml." + to + ' instead, which is now safe by default.');
12869
12597
  };
12870
12598
  }
12599
+ var JSON_SCHEMA = json;
12871
12600
  var load = loader.load;
12872
12601
  loader.loadAll;
12873
12602
  dumper.dump;
@@ -13170,6 +12899,42 @@ Examples:
13170
12899
  io.stderr(`Unknown midscene model command: ${action}\n\n${MODEL_COMMAND_USAGE}`);
13171
12900
  return 1;
13172
12901
  }
12902
+ const test_runner_namespaceObject = require("@midscene/core/internal/test-runner");
12903
+ const probeWorkflowFormat = (file)=>{
12904
+ try {
12905
+ const content = (0, external_node_fs_.readFileSync)(file, 'utf8').replace(/\$\{[^}\r\n]*\}/g, 'MIDSCENE_ENV');
12906
+ return (0, test_runner_namespaceObject.classifyWorkflowDocumentFormat)(load(content, {
12907
+ schema: JSON_SCHEMA
12908
+ }));
12909
+ } catch {
12910
+ return 'unknown';
12911
+ }
12912
+ };
12913
+ const assertLegacyBatchConfigPath = (configPath)=>{
12914
+ if (![
12915
+ '.yaml',
12916
+ '.yml'
12917
+ ].includes((0, external_node_path_.extname)(configPath).toLowerCase())) throw new Error(`midscene --config only accepts a legacy YAML batch config: ${configPath}. Run a native Test project config with \`midscene-test --config ${configPath}\`.`);
12918
+ };
12919
+ const assertLegacyWorkflowSelection = (files)=>{
12920
+ const nativeFiles = [];
12921
+ const mixedFiles = [];
12922
+ for (const file of new Set(files.map((item)=>(0, external_node_path_.resolve)(item)))){
12923
+ const format = probeWorkflowFormat(file);
12924
+ if ('native' === format) nativeFiles.push(file);
12925
+ else if ('mixed' === format) mixedFiles.push(file);
12926
+ }
12927
+ if (0 === nativeFiles.length && 0 === mixedFiles.length) return;
12928
+ const details = [
12929
+ ...nativeFiles.map((file)=>`- native cases/steps: ${file}`),
12930
+ ...mixedFiles.map((file)=>`- mixed tasks/flow and cases/steps: ${file}`)
12931
+ ];
12932
+ throw new Error([
12933
+ 'midscene only runs legacy tasks/flow YAML.',
12934
+ ...details,
12935
+ 'Run native files with `midscene-test`, and keep legacy and native formats in separate files and command invocations.'
12936
+ ].join('\n'));
12937
+ };
13173
12938
  Promise.resolve((async ()=>{
13174
12939
  const rawArgs = process.argv.slice(2);
13175
12940
  const [firstArg] = rawArgs;
@@ -13215,6 +12980,7 @@ Examples:
13215
12980
  };
13216
12981
  let config;
13217
12982
  if (configFile) {
12983
+ assertLegacyBatchConfigPath(configFile);
13218
12984
  config = await createConfig(configFile, configOptions);
13219
12985
  console.log(` Config file: ${configFile}`);
13220
12986
  } else if (cmdFiles && cmdFiles.length > 0) {
@@ -13233,6 +12999,12 @@ Examples:
13233
12999
  console.error('Could not create a valid configuration.');
13234
13000
  process.exit(1);
13235
13001
  }
13002
+ assertLegacyWorkflowSelection([
13003
+ ...config.setup ? [
13004
+ config.setup
13005
+ ] : [],
13006
+ ...config.files
13007
+ ]);
13236
13008
  loadDotenvConfig({
13237
13009
  dotenvDebug: config.dotenvDebug,
13238
13010
  dotenvOverride: config.dotenvOverride,