@midscene/cli 0.8.9 → 0.8.10-beta-20241225120902.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/es/index.js CHANGED
@@ -9934,7 +9934,7 @@ function renamed(from, to) {
9934
9934
  throw new Error("Function yaml." + from + " is removed in js-yaml 4. Use yaml." + to + " instead, which is now safe by default.");
9935
9935
  };
9936
9936
  }
9937
- var isNothing_1, isObject_1, toArray_1, repeat_1, isNegativeZero_1, extend_1, common, exception, snippet, TYPE_CONSTRUCTOR_OPTIONS, YAML_NODE_KINDS, type, schema, str, seq, map, failsafe, _null, bool, int, YAML_FLOAT_PATTERN, SCIENTIFIC_WITHOUT_DOT, float, json, core, YAML_DATE_REGEXP, YAML_TIMESTAMP_REGEXP, timestamp, merge, BASE64_MAP, binary, _hasOwnProperty$3, _toString$2, omap, _toString$1, pairs, _hasOwnProperty$2, set, _default, _hasOwnProperty$1, CONTEXT_FLOW_IN, CONTEXT_FLOW_OUT, CONTEXT_BLOCK_IN, CONTEXT_BLOCK_OUT, CHOMPING_CLIP, CHOMPING_STRIP, CHOMPING_KEEP, PATTERN_NON_PRINTABLE, PATTERN_NON_ASCII_LINE_BREAKS, PATTERN_FLOW_INDICATORS, PATTERN_TAG_HANDLE, PATTERN_TAG_URI, simpleEscapeCheck, simpleEscapeMap, i, directiveHandlers, loadAll_1, load_1, loader, _toString2, _hasOwnProperty, CHAR_BOM, CHAR_TAB, CHAR_LINE_FEED, CHAR_CARRIAGE_RETURN, CHAR_SPACE, CHAR_EXCLAMATION, CHAR_DOUBLE_QUOTE, CHAR_SHARP, CHAR_PERCENT, CHAR_AMPERSAND, CHAR_SINGLE_QUOTE, CHAR_ASTERISK, CHAR_COMMA, CHAR_MINUS, CHAR_COLON, CHAR_EQUALS, CHAR_GREATER_THAN, CHAR_QUESTION, CHAR_COMMERCIAL_AT, CHAR_LEFT_SQUARE_BRACKET, CHAR_RIGHT_SQUARE_BRACKET, CHAR_GRAVE_ACCENT, CHAR_LEFT_CURLY_BRACKET, CHAR_VERTICAL_LINE, CHAR_RIGHT_CURLY_BRACKET, ESCAPE_SEQUENCES, DEPRECATED_BOOLEANS_SYNTAX, DEPRECATED_BASE60_SYNTAX, QUOTING_TYPE_SINGLE, QUOTING_TYPE_DOUBLE, STYLE_PLAIN, STYLE_SINGLE, STYLE_LITERAL, STYLE_FOLDED, STYLE_DOUBLE, dump_1, dumper, Type, Schema, FAILSAFE_SCHEMA, JSON_SCHEMA, CORE_SCHEMA, DEFAULT_SCHEMA, load, loadAll, dump, YAMLException, types2, safeLoad, safeLoadAll, safeDump, jsYaml, js_yaml_default;
9937
+ var isNothing_1, isObject_1, toArray_1, repeat_1, isNegativeZero_1, extend_1, common, exception, snippet, TYPE_CONSTRUCTOR_OPTIONS, YAML_NODE_KINDS, type, schema, str, seq, map, failsafe, _null, bool, int, YAML_FLOAT_PATTERN, SCIENTIFIC_WITHOUT_DOT, float, json, core, YAML_DATE_REGEXP, YAML_TIMESTAMP_REGEXP, timestamp, merge, BASE64_MAP, binary, _hasOwnProperty$3, _toString$2, omap, _toString$1, pairs, _hasOwnProperty$2, set, _default, _hasOwnProperty$1, CONTEXT_FLOW_IN, CONTEXT_FLOW_OUT, CONTEXT_BLOCK_IN, CONTEXT_BLOCK_OUT, CHOMPING_CLIP, CHOMPING_STRIP, CHOMPING_KEEP, PATTERN_NON_PRINTABLE, PATTERN_NON_ASCII_LINE_BREAKS, PATTERN_FLOW_INDICATORS, PATTERN_TAG_HANDLE, PATTERN_TAG_URI, simpleEscapeCheck, simpleEscapeMap, i, directiveHandlers, loadAll_1, load_1, loader, _toString2, _hasOwnProperty, CHAR_BOM, CHAR_TAB, CHAR_LINE_FEED, CHAR_CARRIAGE_RETURN, CHAR_SPACE, CHAR_EXCLAMATION, CHAR_DOUBLE_QUOTE, CHAR_SHARP, CHAR_PERCENT, CHAR_AMPERSAND, CHAR_SINGLE_QUOTE, CHAR_ASTERISK, CHAR_COMMA, CHAR_MINUS, CHAR_COLON, CHAR_EQUALS, CHAR_GREATER_THAN, CHAR_QUESTION, CHAR_COMMERCIAL_AT, CHAR_LEFT_SQUARE_BRACKET, CHAR_RIGHT_SQUARE_BRACKET, CHAR_GRAVE_ACCENT, CHAR_LEFT_CURLY_BRACKET, CHAR_VERTICAL_LINE, CHAR_RIGHT_CURLY_BRACKET, ESCAPE_SEQUENCES, DEPRECATED_BOOLEANS_SYNTAX, DEPRECATED_BASE60_SYNTAX, QUOTING_TYPE_SINGLE, QUOTING_TYPE_DOUBLE, STYLE_PLAIN, STYLE_SINGLE, STYLE_LITERAL, STYLE_FOLDED, STYLE_DOUBLE, dump_1, dumper, load, loadAll, dump, safeLoad, safeLoadAll, safeDump;
9938
9938
  var init_js_yaml = __esm({
9939
9939
  "../../node_modules/.pnpm/js-yaml@4.1.0/node_modules/js-yaml/dist/js-yaml.mjs"() {
9940
9940
  "use strict";
@@ -10336,51 +10336,12 @@ var init_js_yaml = __esm({
10336
10336
  dumper = {
10337
10337
  dump: dump_1
10338
10338
  };
10339
- Type = type;
10340
- Schema = schema;
10341
- FAILSAFE_SCHEMA = failsafe;
10342
- JSON_SCHEMA = json;
10343
- CORE_SCHEMA = core;
10344
- DEFAULT_SCHEMA = _default;
10345
10339
  load = loader.load;
10346
10340
  loadAll = loader.loadAll;
10347
10341
  dump = dumper.dump;
10348
- YAMLException = exception;
10349
- types2 = {
10350
- binary,
10351
- float,
10352
- map,
10353
- null: _null,
10354
- pairs,
10355
- set,
10356
- timestamp,
10357
- bool,
10358
- int,
10359
- merge,
10360
- omap,
10361
- seq,
10362
- str
10363
- };
10364
10342
  safeLoad = renamed("safeLoad", "load");
10365
10343
  safeLoadAll = renamed("safeLoadAll", "loadAll");
10366
10344
  safeDump = renamed("safeDump", "dump");
10367
- jsYaml = {
10368
- Type,
10369
- Schema,
10370
- FAILSAFE_SCHEMA,
10371
- JSON_SCHEMA,
10372
- CORE_SCHEMA,
10373
- DEFAULT_SCHEMA,
10374
- load,
10375
- loadAll,
10376
- dump,
10377
- YAMLException,
10378
- types: types2,
10379
- safeLoad,
10380
- safeLoadAll,
10381
- safeDump
10382
- };
10383
- js_yaml_default = jsYaml;
10384
10345
  }
10385
10346
  });
10386
10347
 
@@ -13189,19 +13150,19 @@ var require_build3 = __commonJS({
13189
13150
  var require_sync = __commonJS({
13190
13151
  "../../node_modules/.pnpm/escalade@3.2.0/node_modules/escalade/sync/index.js"(exports, module) {
13191
13152
  "use strict";
13192
- var { dirname: dirname3, resolve } = __require("path");
13153
+ var { dirname: dirname2, resolve } = __require("path");
13193
13154
  var { readdirSync: readdirSync2, statSync: statSync2 } = __require("fs");
13194
13155
  module.exports = function(start, callback) {
13195
13156
  let dir = resolve(".", start);
13196
13157
  let tmp, stats = statSync2(dir);
13197
13158
  if (!stats.isDirectory()) {
13198
- dir = dirname3(dir);
13159
+ dir = dirname2(dir);
13199
13160
  }
13200
13161
  while (true) {
13201
13162
  tmp = callback(dir, readdirSync2(dir));
13202
13163
  if (tmp)
13203
13164
  return resolve(dir, tmp);
13204
- dir = dirname3(tmp = dir);
13165
+ dir = dirname2(tmp = dir);
13205
13166
  if (tmp === dir)
13206
13167
  break;
13207
13168
  }
@@ -13238,9 +13199,9 @@ var require_require_directory = __commonJS({
13238
13199
  "../../node_modules/.pnpm/require-directory@2.1.1/node_modules/require-directory/index.js"(exports, module) {
13239
13200
  "use strict";
13240
13201
  var fs = __require("fs");
13241
- var join3 = __require("path").join;
13202
+ var join2 = __require("path").join;
13242
13203
  var resolve = __require("path").resolve;
13243
- var dirname3 = __require("path").dirname;
13204
+ var dirname2 = __require("path").dirname;
13244
13205
  var defaultOptions = {
13245
13206
  extensions: ["js", "json", "coffee"],
13246
13207
  recurse: true,
@@ -13273,9 +13234,9 @@ var require_require_directory = __commonJS({
13273
13234
  options[prop] = defaultOptions[prop];
13274
13235
  }
13275
13236
  }
13276
- path2 = !path2 ? dirname3(m.filename) : resolve(dirname3(m.filename), path2);
13237
+ path2 = !path2 ? dirname2(m.filename) : resolve(dirname2(m.filename), path2);
13277
13238
  fs.readdirSync(path2).forEach(function(filename) {
13278
- var joined = join3(path2, filename), files, key, obj;
13239
+ var joined = join2(path2, filename), files, key, obj;
13279
13240
  if (fs.statSync(joined).isDirectory() && options.recurse) {
13280
13241
  files = requireDirectory(m, joined, options);
13281
13242
  if (Object.keys(files).length) {
@@ -15050,7 +15011,7 @@ var require_package2 = __commonJS({
15050
15011
  module.exports = {
15051
15012
  name: "@midscene/cli",
15052
15013
  description: "An AI-powered automation SDK can control the page, perform assertions, and extract data in JSON format using natural language. See https://midscenejs.com/ for details.",
15053
- version: "0.8.9",
15014
+ version: "0.8.10-beta-20241225120902.0",
15054
15015
  repository: "https://github.com/web-infra-dev/midscene",
15055
15016
  homepage: "https://midscenejs.com/",
15056
15017
  "jsnext:source": "./src/index.ts",
@@ -15072,8 +15033,8 @@ var require_package2 = __commonJS({
15072
15033
  dependencies: {
15073
15034
  "@midscene/core": "workspace:*",
15074
15035
  "@midscene/web": "workspace:*",
15075
- "http-server": "14.1.1",
15076
- puppeteer: "23.0.2"
15036
+ puppeteer: "23.0.2",
15037
+ "http-server": "14.1.1"
15077
15038
  },
15078
15039
  devDependencies: {
15079
15040
  "@modern-js/module-tools": "2.60.6",
@@ -15093,7 +15054,7 @@ var require_package2 = __commonJS({
15093
15054
  yargs: "17.7.2"
15094
15055
  },
15095
15056
  engines: {
15096
- node: ">=16.0.0"
15057
+ node: ">=18.0.0"
15097
15058
  },
15098
15059
  publishConfig: {
15099
15060
  access: "public",
@@ -15627,7 +15588,8 @@ var require_source = __commonJS({
15627
15588
 
15628
15589
  // src/printer.ts
15629
15590
  import { basename, dirname, relative } from "path";
15630
- function textForStatus(status) {
15591
+ import { flowItemBrief } from "@midscene/web";
15592
+ function indicatorForStatus(status) {
15631
15593
  if (status === "init") {
15632
15594
  return import_chalk.default.gray("◌");
15633
15595
  }
@@ -15646,7 +15608,7 @@ function paddingLines(lines) {
15646
15608
  return `${indent}${line}`;
15647
15609
  });
15648
15610
  }
15649
- var import_chalk, isTTY, indent, spinnerInterval, spinnerFrames, currentSpinningFrame, flowItemBrief, contextInfo, singleTaskInfo, contextTaskListSummary;
15611
+ var import_chalk, isTTY, indent, spinnerInterval, spinnerFrames, currentSpinningFrame, contextInfo, singleTaskInfo, contextTaskListSummary;
15650
15612
  var init_printer = __esm({
15651
15613
  "src/printer.ts"() {
15652
15614
  "use strict";
@@ -15658,46 +15620,12 @@ var init_printer = __esm({
15658
15620
  currentSpinningFrame = () => {
15659
15621
  return spinnerFrames[Math.floor(Date.now() / spinnerInterval) % spinnerFrames.length];
15660
15622
  };
15661
- flowItemBrief = (flowItem) => {
15662
- if (!flowItem) {
15663
- return "";
15664
- }
15665
- const sliceText = (text) => {
15666
- const lengthLimit = 60;
15667
- if (text && text.length > lengthLimit) {
15668
- return `${text.slice(0, lengthLimit)}...`;
15669
- }
15670
- return text || "";
15671
- };
15672
- if (flowItem.aiAction || flowItem.ai) {
15673
- return `aiAction: ${sliceText(
15674
- flowItem.aiActionProgressTip || flowItem.aiAction || flowItem.ai
15675
- )}`;
15676
- }
15677
- if (flowItem.aiAssert) {
15678
- return `aiAssert: ${sliceText(
15679
- flowItem.aiAssert
15680
- )}`;
15681
- }
15682
- if (flowItem.aiQuery) {
15683
- return `aiQuery: ${sliceText(flowItem.aiQuery)}`;
15684
- }
15685
- if (flowItem.aiWaitFor) {
15686
- return `aiWaitFor: ${sliceText(
15687
- flowItem.aiWaitFor
15688
- )}`;
15689
- }
15690
- if (flowItem.sleep) {
15691
- return `sleep: ${flowItem.sleep}`;
15692
- }
15693
- return "";
15694
- };
15695
15623
  contextInfo = (context) => {
15696
15624
  const filePath = context.file;
15697
15625
  const fileName = basename(filePath);
15698
15626
  const fileDir = dirname(filePath);
15699
15627
  const fileNameToPrint = `${import_chalk.default.gray(`${fileDir}/`)}${fileName}`;
15700
- const fileStatusText = textForStatus(context.player.status);
15628
+ const fileStatusText = indicatorForStatus(context.player.status);
15701
15629
  const contextActionText = typeof context.player.currentTaskIndex === "undefined" && context.player.status === "running" ? import_chalk.default.gray("(navigating)") : "";
15702
15630
  const outputFile = context.player.output;
15703
15631
  const outputText = outputFile && Object.keys(context.player.result || {}).length > 0 ? `
@@ -15738,7 +15666,7 @@ ${indent}${import_chalk.default.gray(`report: ./${reportFileToShow}`)}` : "";
15738
15666
  const errorText = task.status === "error" ? `
15739
15667
  ${indent}${import_chalk.default.gray("error:")}
15740
15668
  ${indent}${indent}${(_a4 = task.error) == null ? void 0 : _a4.message}` : "";
15741
- const statusText = textForStatus(task.status);
15669
+ const statusText = indicatorForStatus(task.status);
15742
15670
  const mergedLine = `${statusText} ${task.name} ${stepText}${errorText}`;
15743
15671
  return {
15744
15672
  nameText: task.name,
@@ -15748,12 +15676,12 @@ ${indent}${indent}${(_a4 = task.error) == null ? void 0 : _a4.message}` : "";
15748
15676
  mergedLine
15749
15677
  };
15750
15678
  };
15751
- contextTaskListSummary = (taskStatus, context) => {
15679
+ contextTaskListSummary = (taskStatusArray, context) => {
15752
15680
  const prefixLines = [];
15753
15681
  const currentLine = [];
15754
15682
  const suffixText = [];
15755
15683
  const { mergedText: fileInfo } = contextInfo(context);
15756
- for (const task of taskStatus) {
15684
+ for (const task of taskStatusArray) {
15757
15685
  const { mergedLine } = singleTaskInfo(task);
15758
15686
  if (context.player.status === "init") {
15759
15687
  suffixText.push(mergedLine);
@@ -16319,47 +16247,58 @@ var init_tty_renderer = __esm({
16319
16247
  }
16320
16248
  });
16321
16249
 
16322
- // src/yaml-player.ts
16250
+ // src/yaml-runner.ts
16251
+ import { readFileSync } from "fs";
16252
+ import { basename as basename2, extname } from "path";
16253
+ import { ScriptPlayer, parseYamlScript } from "@midscene/web";
16323
16254
  import { createServer } from "http-server";
16324
- import puppeteer from "puppeteer";
16325
- import assert2 from "assert";
16326
- import { existsSync as existsSync2, mkdirSync, readFileSync, writeFileSync } from "fs";
16327
- import { basename as basename2, dirname as dirname2, extname, join as join2 } from "path";
16328
- import { PuppeteerAgent } from "@midscene/web/puppeteer";
16329
- import { paramStr, typeStr } from "@midscene/web/ui-utils";
16330
- function loadYamlScript(content, filePath) {
16331
- const obj = js_yaml_default.load(content);
16332
- const pathTip = filePath ? `, failed to load ${filePath}` : "";
16333
- assert2(obj.target, `property "target" is required in yaml script${pathTip}`);
16334
- assert2(
16335
- typeof obj.target === "object",
16336
- `property "target" must be an object${pathTip}`
16337
- );
16338
- assert2(
16339
- typeof obj.target.url === "string",
16340
- `property "target.url" must be provided in yaml script: ${pathTip}`
16341
- );
16342
- assert2(obj.tasks, `property "tasks" is required in yaml script${pathTip}`);
16343
- assert2(
16344
- Array.isArray(obj.tasks),
16345
- `property "tasks" must be an array${pathTip}`
16346
- );
16347
- return obj;
16348
- }
16255
+ import { assert as assert2 } from "console";
16256
+ import { puppeteerAgentForTarget } from "@midscene/web/puppeteer";
16349
16257
  function playYamlFiles(files, options) {
16350
16258
  return __async(this, null, function* () {
16351
16259
  const fileContextList = [];
16352
16260
  for (const file of files) {
16353
- const script = loadYamlScript(readFileSync(file, "utf-8"), file);
16261
+ const script = parseYamlScript(readFileSync(file, "utf-8"), file);
16354
16262
  const fileName = basename2(file, extname(file));
16355
- const player = new ScriptPlayer(script, __spreadProps(__spreadValues({}, options), {
16356
- testId: fileName,
16357
- onTaskStatusChange: (taskStatus) => {
16263
+ const preference = {
16264
+ headed: options == null ? void 0 : options.headed,
16265
+ keepWindow: options == null ? void 0 : options.keepWindow,
16266
+ testId: fileName
16267
+ };
16268
+ const player = new ScriptPlayer(
16269
+ script,
16270
+ (target) => __async(this, null, function* () {
16271
+ const freeFn = [];
16272
+ let localServer;
16273
+ let urlToVisit;
16274
+ assert2(typeof target.url === "string", "url is required");
16275
+ if (target.serve) {
16276
+ localServer = yield launchServer(target.serve);
16277
+ const serverAddress = localServer.server.address();
16278
+ freeFn.push({
16279
+ name: "local_server",
16280
+ fn: () => localServer == null ? void 0 : localServer.server.close()
16281
+ });
16282
+ if (target.url.startsWith("/")) {
16283
+ urlToVisit = `http://${serverAddress == null ? void 0 : serverAddress.address}:${serverAddress == null ? void 0 : serverAddress.port}${target.url}`;
16284
+ } else {
16285
+ urlToVisit = `http://${serverAddress == null ? void 0 : serverAddress.address}:${serverAddress == null ? void 0 : serverAddress.port}/${target.url}`;
16286
+ }
16287
+ target.url = urlToVisit;
16288
+ }
16289
+ const { agent, freeFn: newFreeFn } = yield puppeteerAgentForTarget(
16290
+ target,
16291
+ preference
16292
+ );
16293
+ freeFn.push(...newFreeFn);
16294
+ return { agent, freeFn };
16295
+ }),
16296
+ (taskStatus) => {
16358
16297
  if (!isTTY) {
16359
16298
  const { nameText } = singleTaskInfo(taskStatus);
16360
16299
  }
16361
16300
  }
16362
- }));
16301
+ );
16363
16302
  fileContextList.push({ file, player });
16364
16303
  }
16365
16304
  if (isTTY) {
@@ -16367,7 +16306,7 @@ function playYamlFiles(files, options) {
16367
16306
  const summary = [""];
16368
16307
  for (const context of fileContextList) {
16369
16308
  summary.push(
16370
- contextTaskListSummary(context.player.taskStatus, context)
16309
+ contextTaskListSummary(context.player.taskStatusList, context)
16371
16310
  );
16372
16311
  }
16373
16312
  summary.push("");
@@ -16389,25 +16328,21 @@ function playYamlFiles(files, options) {
16389
16328
  const { mergedText } = contextInfo(context);
16390
16329
  console.log(mergedText);
16391
16330
  yield context.player.run();
16392
- console.log(contextTaskListSummary(context.player.taskStatus, context));
16331
+ console.log(
16332
+ contextTaskListSummary(context.player.taskStatusList, context)
16333
+ );
16393
16334
  }
16394
16335
  }
16395
16336
  const ifFail = fileContextList.some((task) => task.player.status === "error");
16396
16337
  return !ifFail;
16397
16338
  });
16398
16339
  }
16399
- var defaultUA, defaultViewportWidth, defaultViewportHeight, defaultViewportScale, defaultWaitForNetworkIdleTimeout, launchServer, ttyRenderer, ScriptPlayer;
16400
- var init_yaml_player = __esm({
16401
- "src/yaml-player.ts"() {
16340
+ var launchServer, ttyRenderer;
16341
+ var init_yaml_runner = __esm({
16342
+ "src/yaml-runner.ts"() {
16402
16343
  "use strict";
16403
- init_js_yaml();
16404
16344
  init_printer();
16405
16345
  init_tty_renderer();
16406
- defaultUA = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36";
16407
- defaultViewportWidth = 1280;
16408
- defaultViewportHeight = 960;
16409
- defaultViewportScale = process.platform === "darwin" ? 2 : 1;
16410
- defaultWaitForNetworkIdleTimeout = 10 * 1e3;
16411
16346
  launchServer = (dir) => __async(void 0, null, function* () {
16412
16347
  return new Promise((resolve, reject) => {
16413
16348
  const server = createServer({
@@ -16418,291 +16353,6 @@ var init_yaml_player = __esm({
16418
16353
  });
16419
16354
  });
16420
16355
  });
16421
- ScriptPlayer = class {
16422
- constructor(script, options) {
16423
- this.script = script;
16424
- this.options = options;
16425
- __publicField(this, "currentTaskIndex");
16426
- __publicField(this, "taskStatus", []);
16427
- __publicField(this, "status", "init");
16428
- __publicField(this, "reportFile");
16429
- __publicField(this, "result");
16430
- __publicField(this, "unnamedResultIndex", 0);
16431
- __publicField(this, "output");
16432
- __publicField(this, "errorInSetup");
16433
- this.result = {};
16434
- this.output = script.target.output;
16435
- this.taskStatus = (script.tasks || []).map((task, taskIndex) => {
16436
- var _a4;
16437
- return __spreadProps(__spreadValues({}, task), {
16438
- index: taskIndex,
16439
- status: "init",
16440
- totalSteps: ((_a4 = task.flow) == null ? void 0 : _a4.length) || 0
16441
- });
16442
- });
16443
- }
16444
- setPlayerStatus(status, error) {
16445
- this.status = status;
16446
- this.errorInSetup = error;
16447
- }
16448
- setTaskStatus(index, statusValue, error) {
16449
- var _a4;
16450
- this.taskStatus[index].status = statusValue;
16451
- if (error) {
16452
- this.taskStatus[index].error = error;
16453
- }
16454
- if ((_a4 = this.options) == null ? void 0 : _a4.onTaskStatusChange) {
16455
- this.options.onTaskStatusChange(this.taskStatus[index]);
16456
- }
16457
- }
16458
- setTaskIndex(taskIndex) {
16459
- this.currentTaskIndex = taskIndex;
16460
- }
16461
- flushResult() {
16462
- if (Object.keys(this.result).length && this.output) {
16463
- const output = join2(process.cwd(), this.output);
16464
- const outputDir = dirname2(output);
16465
- if (!existsSync2(outputDir)) {
16466
- mkdirSync(outputDir, { recursive: true });
16467
- }
16468
- writeFileSync(output, JSON.stringify(this.result, void 0, 2));
16469
- }
16470
- }
16471
- playTask(_0, _1) {
16472
- return __async(this, arguments, function* (taskStatus, {
16473
- agent,
16474
- browser,
16475
- page
16476
- }) {
16477
- const { flow } = taskStatus;
16478
- assert2(flow, "missing flow in task");
16479
- for (const flowItemIndex in flow) {
16480
- taskStatus.currentStep = Number.parseInt(flowItemIndex, 10);
16481
- const flowItem = flow[flowItemIndex];
16482
- if (flowItem.aiAction || flowItem.ai) {
16483
- const actionTask = flowItem;
16484
- const prompt = actionTask.aiAction || actionTask.ai;
16485
- assert2(prompt, "missing prompt for ai (aiAction)");
16486
- assert2(
16487
- typeof prompt === "string",
16488
- "prompt for aiAction must be a string"
16489
- );
16490
- yield agent.aiAction(prompt, {
16491
- onTaskStart(task) {
16492
- const tip = `${typeStr(task)} - ${paramStr(task)}`;
16493
- flowItem.aiActionProgressTip = tip;
16494
- }
16495
- });
16496
- } else if (flowItem.aiAssert) {
16497
- const assertTask = flowItem;
16498
- const prompt = assertTask.aiAssert;
16499
- assert2(prompt, "missing prompt for aiAssert");
16500
- assert2(
16501
- typeof prompt === "string",
16502
- "prompt for aiAssert must be a string"
16503
- );
16504
- yield agent.aiAssert(prompt);
16505
- } else if (flowItem.aiQuery) {
16506
- const queryTask = flowItem;
16507
- const prompt = queryTask.aiQuery;
16508
- assert2(prompt, "missing prompt for aiQuery");
16509
- assert2(
16510
- typeof prompt === "string",
16511
- "prompt for aiQuery must be a string"
16512
- );
16513
- const queryResult = yield agent.aiQuery(prompt);
16514
- const resultKey = queryTask.name || this.unnamedResultIndex++;
16515
- if (this.result[resultKey]) {
16516
- console.warn(
16517
- `result key ${resultKey} already exists, will overwrite`
16518
- );
16519
- }
16520
- this.result[resultKey] = queryResult;
16521
- this.flushResult();
16522
- } else if (flowItem.aiWaitFor) {
16523
- const waitForTask = flowItem;
16524
- const prompt = waitForTask.aiWaitFor;
16525
- assert2(prompt, "missing prompt for aiWaitFor");
16526
- assert2(
16527
- typeof prompt === "string",
16528
- "prompt for aiWaitFor must be a string"
16529
- );
16530
- const timeout = waitForTask.timeout;
16531
- yield agent.aiWaitFor(prompt, { timeoutMs: timeout });
16532
- } else if (flowItem.sleep) {
16533
- const sleepTask = flowItem;
16534
- const ms = sleepTask.sleep;
16535
- assert2(
16536
- ms && ms > 0,
16537
- `ms for sleep must be greater than 0, but got ${ms}`
16538
- );
16539
- yield new Promise((resolve) => setTimeout(resolve, ms));
16540
- } else {
16541
- throw new Error(`unknown flowItem: ${JSON.stringify(flowItem)}`);
16542
- }
16543
- }
16544
- this.reportFile = agent.reportFile;
16545
- });
16546
- }
16547
- run() {
16548
- return __async(this, null, function* () {
16549
- var _a4, _b2, _c2, _d2, _e2, _f2;
16550
- const { target, tasks } = this.script;
16551
- this.setPlayerStatus("running");
16552
- const ua = target.userAgent || defaultUA;
16553
- let width = defaultViewportWidth;
16554
- if (target.viewportWidth) {
16555
- assert2(
16556
- typeof target.viewportWidth === "number",
16557
- "viewportWidth must be a number"
16558
- );
16559
- width = Number.parseInt(target.viewportWidth, 10);
16560
- assert2(
16561
- width > 0,
16562
- `viewportWidth must be greater than 0, but got ${width}`
16563
- );
16564
- }
16565
- let height = defaultViewportHeight;
16566
- if (target.viewportHeight) {
16567
- assert2(
16568
- typeof target.viewportHeight === "number",
16569
- "viewportHeight must be a number"
16570
- );
16571
- height = Number.parseInt(target.viewportHeight, 10);
16572
- assert2(
16573
- height > 0,
16574
- `viewportHeight must be greater than 0, but got ${height}`
16575
- );
16576
- }
16577
- let dpr = defaultViewportScale;
16578
- if (target.viewportScale) {
16579
- assert2(
16580
- typeof target.viewportScale === "number",
16581
- "viewportScale must be a number"
16582
- );
16583
- dpr = Number.parseInt(target.viewportScale, 10);
16584
- assert2(dpr > 0, `viewportScale must be greater than 0, but got ${dpr}`);
16585
- }
16586
- const viewportConfig = {
16587
- width,
16588
- height,
16589
- deviceScaleFactor: dpr
16590
- };
16591
- const freeFn = [];
16592
- let localServer;
16593
- let urlToVisit;
16594
- assert2(typeof target.url === "string", "url is required");
16595
- if (target.serve) {
16596
- localServer = yield launchServer(target.serve);
16597
- const serverAddress = localServer.server.address();
16598
- freeFn.push({
16599
- name: "local_server",
16600
- fn: () => localServer == null ? void 0 : localServer.server.close()
16601
- });
16602
- if (target.url.startsWith("/")) {
16603
- urlToVisit = `http://${serverAddress == null ? void 0 : serverAddress.address}:${serverAddress == null ? void 0 : serverAddress.port}${target.url}`;
16604
- } else {
16605
- urlToVisit = `http://${serverAddress == null ? void 0 : serverAddress.address}:${serverAddress == null ? void 0 : serverAddress.port}/${target.url}`;
16606
- }
16607
- } else {
16608
- urlToVisit = target.url;
16609
- }
16610
- const headed = ((_a4 = this.options) == null ? void 0 : _a4.headed) || ((_b2 = this.options) == null ? void 0 : _b2.keepWindow);
16611
- if (headed && process.env.CI === "1") {
16612
- console.warn(
16613
- "you are probably running headed mode in CI, this will usually fail."
16614
- );
16615
- }
16616
- const browser = yield puppeteer.launch({
16617
- headless: !headed,
16618
- args: [
16619
- "--no-sandbox",
16620
- "--disable-setuid-sandbox",
16621
- "--disable-features=PasswordLeakDetection",
16622
- "--disable-save-password-bubble"
16623
- ]
16624
- });
16625
- freeFn.push({
16626
- name: "puppeteer_browser",
16627
- fn: () => {
16628
- var _a5;
16629
- if (!((_a5 = this.options) == null ? void 0 : _a5.keepWindow)) {
16630
- browser.close();
16631
- }
16632
- }
16633
- });
16634
- const pages = yield browser.pages();
16635
- const page = pages[0];
16636
- yield page.setUserAgent(ua);
16637
- yield page.setViewport(viewportConfig);
16638
- if (target.cookie) {
16639
- const cookieFileContent = readFileSync(target.cookie, "utf-8");
16640
- yield page.setCookie(...JSON.parse(cookieFileContent));
16641
- }
16642
- yield page.goto(urlToVisit);
16643
- const waitForNetworkIdleTimeout = typeof ((_c2 = target.waitForNetworkIdle) == null ? void 0 : _c2.timeout) === "number" ? target.waitForNetworkIdle.timeout : defaultWaitForNetworkIdleTimeout;
16644
- try {
16645
- if (waitForNetworkIdleTimeout > 0) {
16646
- yield page.waitForNetworkIdle({
16647
- timeout: waitForNetworkIdleTimeout
16648
- });
16649
- }
16650
- } catch (e) {
16651
- if (typeof ((_d2 = target.waitForNetworkIdle) == null ? void 0 : _d2.continueOnNetworkIdleError) === "boolean" && !((_e2 = target.waitForNetworkIdle) == null ? void 0 : _e2.continueOnNetworkIdleError)) {
16652
- const newError = new Error(`failed to wait for network idle: ${e}`, {
16653
- cause: e
16654
- });
16655
- this.setPlayerStatus("error", newError);
16656
- return;
16657
- }
16658
- const newMessage = `failed to wait for network idle after ${waitForNetworkIdleTimeout}ms, but the script will continue.`;
16659
- console.warn(newMessage);
16660
- }
16661
- const agent = new PuppeteerAgent(page, {
16662
- autoPrintReportMsg: false,
16663
- testId: (_f2 = this.options) == null ? void 0 : _f2.testId
16664
- });
16665
- freeFn.push({
16666
- name: "midscene_puppeteer_agent",
16667
- fn: () => agent.destroy()
16668
- });
16669
- let taskIndex = 0;
16670
- this.setPlayerStatus("running");
16671
- let errorFlag = false;
16672
- while (taskIndex < tasks.length) {
16673
- const task = tasks[taskIndex];
16674
- this.setTaskStatus(taskIndex, "running");
16675
- try {
16676
- this.setTaskIndex(taskIndex);
16677
- yield this.playTask(this.taskStatus[taskIndex], {
16678
- agent,
16679
- browser,
16680
- page
16681
- });
16682
- } catch (e) {
16683
- this.setTaskStatus(taskIndex, "error", e);
16684
- this.setPlayerStatus("error");
16685
- errorFlag = true;
16686
- this.reportFile = agent.reportFile;
16687
- taskIndex++;
16688
- continue;
16689
- }
16690
- this.reportFile = agent.reportFile;
16691
- this.setTaskStatus(taskIndex, "done");
16692
- taskIndex++;
16693
- }
16694
- if (!errorFlag) {
16695
- this.setPlayerStatus("done");
16696
- }
16697
- freeFn.forEach((fn) => {
16698
- try {
16699
- fn.fn();
16700
- } catch (e) {
16701
- }
16702
- });
16703
- });
16704
- }
16705
- };
16706
16356
  }
16707
16357
  });
16708
16358
 
@@ -16711,7 +16361,7 @@ var require_src = __commonJS({
16711
16361
  "src/index.ts"(exports) {
16712
16362
  init_config();
16713
16363
  init_cli_utils();
16714
- init_yaml_player();
16364
+ init_yaml_runner();
16715
16365
  Promise.resolve(
16716
16366
  (() => __async(exports, null, function* () {
16717
16367
  const { options, path: path2 } = yield parseProcessArgs();
package/dist/lib/index.js CHANGED
@@ -3711,19 +3711,19 @@ var require_build3 = __commonJS({
3711
3711
  var require_sync = __commonJS({
3712
3712
  "../../node_modules/.pnpm/escalade@3.2.0/node_modules/escalade/sync/index.js"(exports2, module2) {
3713
3713
  "use strict";
3714
- var { dirname: dirname3, resolve } = require("path");
3714
+ var { dirname: dirname2, resolve } = require("path");
3715
3715
  var { readdirSync: readdirSync2, statSync: statSync2 } = require("fs");
3716
3716
  module2.exports = function(start, callback) {
3717
3717
  let dir = resolve(".", start);
3718
3718
  let tmp, stats = statSync2(dir);
3719
3719
  if (!stats.isDirectory()) {
3720
- dir = dirname3(dir);
3720
+ dir = dirname2(dir);
3721
3721
  }
3722
3722
  while (true) {
3723
3723
  tmp = callback(dir, readdirSync2(dir));
3724
3724
  if (tmp)
3725
3725
  return resolve(dir, tmp);
3726
- dir = dirname3(tmp = dir);
3726
+ dir = dirname2(tmp = dir);
3727
3727
  if (tmp === dir)
3728
3728
  break;
3729
3729
  }
@@ -3760,9 +3760,9 @@ var require_require_directory = __commonJS({
3760
3760
  "../../node_modules/.pnpm/require-directory@2.1.1/node_modules/require-directory/index.js"(exports2, module2) {
3761
3761
  "use strict";
3762
3762
  var fs = require("fs");
3763
- var join3 = require("path").join;
3763
+ var join2 = require("path").join;
3764
3764
  var resolve = require("path").resolve;
3765
- var dirname3 = require("path").dirname;
3765
+ var dirname2 = require("path").dirname;
3766
3766
  var defaultOptions = {
3767
3767
  extensions: ["js", "json", "coffee"],
3768
3768
  recurse: true,
@@ -3795,9 +3795,9 @@ var require_require_directory = __commonJS({
3795
3795
  options[prop] = defaultOptions[prop];
3796
3796
  }
3797
3797
  }
3798
- path2 = !path2 ? dirname3(m.filename) : resolve(dirname3(m.filename), path2);
3798
+ path2 = !path2 ? dirname2(m.filename) : resolve(dirname2(m.filename), path2);
3799
3799
  fs.readdirSync(path2).forEach(function(filename) {
3800
- var joined = join3(path2, filename), files, key, obj;
3800
+ var joined = join2(path2, filename), files, key, obj;
3801
3801
  if (fs.statSync(joined).isDirectory() && options.recurse) {
3802
3802
  files = requireDirectory(m, joined, options);
3803
3803
  if (Object.keys(files).length) {
@@ -5556,7 +5556,7 @@ var require_package2 = __commonJS({
5556
5556
  module2.exports = {
5557
5557
  name: "@midscene/cli",
5558
5558
  description: "An AI-powered automation SDK can control the page, perform assertions, and extract data in JSON format using natural language. See https://midscenejs.com/ for details.",
5559
- version: "0.8.9",
5559
+ version: "0.8.10-beta-20241225120902.0",
5560
5560
  repository: "https://github.com/web-infra-dev/midscene",
5561
5561
  homepage: "https://midscenejs.com/",
5562
5562
  "jsnext:source": "./src/index.ts",
@@ -5578,8 +5578,8 @@ var require_package2 = __commonJS({
5578
5578
  dependencies: {
5579
5579
  "@midscene/core": "workspace:*",
5580
5580
  "@midscene/web": "workspace:*",
5581
- "http-server": "14.1.1",
5582
- puppeteer: "23.0.2"
5581
+ puppeteer: "23.0.2",
5582
+ "http-server": "14.1.1"
5583
5583
  },
5584
5584
  devDependencies: {
5585
5585
  "@modern-js/module-tools": "2.60.6",
@@ -5599,7 +5599,7 @@ var require_package2 = __commonJS({
5599
5599
  yargs: "17.7.2"
5600
5600
  },
5601
5601
  engines: {
5602
- node: ">=16.0.0"
5602
+ node: ">=18.0.0"
5603
5603
  },
5604
5604
  publishConfig: {
5605
5605
  access: "public",
@@ -15379,51 +15379,12 @@ function renamed(from, to) {
15379
15379
  throw new Error("Function yaml." + from + " is removed in js-yaml 4. Use yaml." + to + " instead, which is now safe by default.");
15380
15380
  };
15381
15381
  }
15382
- var Type = type;
15383
- var Schema = schema;
15384
- var FAILSAFE_SCHEMA = failsafe;
15385
- var JSON_SCHEMA = json;
15386
- var CORE_SCHEMA = core;
15387
- var DEFAULT_SCHEMA = _default;
15388
15382
  var load = loader.load;
15389
15383
  var loadAll = loader.loadAll;
15390
15384
  var dump = dumper.dump;
15391
- var YAMLException = exception;
15392
- var types2 = {
15393
- binary,
15394
- float,
15395
- map,
15396
- null: _null,
15397
- pairs,
15398
- set,
15399
- timestamp,
15400
- bool,
15401
- int,
15402
- merge,
15403
- omap,
15404
- seq,
15405
- str
15406
- };
15407
15385
  var safeLoad = renamed("safeLoad", "load");
15408
15386
  var safeLoadAll = renamed("safeLoadAll", "loadAll");
15409
15387
  var safeDump = renamed("safeDump", "dump");
15410
- var jsYaml = {
15411
- Type,
15412
- Schema,
15413
- FAILSAFE_SCHEMA,
15414
- JSON_SCHEMA,
15415
- CORE_SCHEMA,
15416
- DEFAULT_SCHEMA,
15417
- load,
15418
- loadAll,
15419
- dump,
15420
- YAMLException,
15421
- types: types2,
15422
- safeLoad,
15423
- safeLoadAll,
15424
- safeDump
15425
- };
15426
- var js_yaml_default = jsYaml;
15427
15388
 
15428
15389
  // ../../node_modules/.pnpm/yargs@17.7.2/node_modules/yargs/yargs.mjs
15429
15390
  var import_build = __toESM(require_build4(), 1);
@@ -15475,17 +15436,15 @@ function matchYamlFiles(fileGlob) {
15475
15436
  });
15476
15437
  }
15477
15438
 
15478
- // src/yaml-player.ts
15479
- var import_http_server = require("http-server");
15480
- var import_puppeteer = __toESM(require("puppeteer"));
15481
- var import_node_assert2 = __toESM(require("assert"));
15439
+ // src/yaml-runner.ts
15482
15440
  var import_node_fs3 = require("fs");
15483
15441
  var import_node_path4 = require("path");
15484
- var import_puppeteer2 = require("@midscene/web/puppeteer");
15485
- var import_ui_utils = require("@midscene/web/ui-utils");
15442
+ var import_web2 = require("@midscene/web");
15443
+ var import_http_server = require("http-server");
15486
15444
 
15487
15445
  // src/printer.ts
15488
15446
  var import_node_path3 = require("path");
15447
+ var import_web = require("@midscene/web");
15489
15448
  var import_chalk = __toESM(require_source());
15490
15449
  var isTTY = process.env.MIDSCENE_CLI_LOG_ON_NON_TTY ? false : process.stdout.isTTY;
15491
15450
  var indent = " ";
@@ -15494,41 +15453,7 @@ var spinnerFrames = ["◰", "◳", "◲", "◱"];
15494
15453
  var currentSpinningFrame = () => {
15495
15454
  return spinnerFrames[Math.floor(Date.now() / spinnerInterval) % spinnerFrames.length];
15496
15455
  };
15497
- var flowItemBrief = (flowItem) => {
15498
- if (!flowItem) {
15499
- return "";
15500
- }
15501
- const sliceText = (text) => {
15502
- const lengthLimit = 60;
15503
- if (text && text.length > lengthLimit) {
15504
- return `${text.slice(0, lengthLimit)}...`;
15505
- }
15506
- return text || "";
15507
- };
15508
- if (flowItem.aiAction || flowItem.ai) {
15509
- return `aiAction: ${sliceText(
15510
- flowItem.aiActionProgressTip || flowItem.aiAction || flowItem.ai
15511
- )}`;
15512
- }
15513
- if (flowItem.aiAssert) {
15514
- return `aiAssert: ${sliceText(
15515
- flowItem.aiAssert
15516
- )}`;
15517
- }
15518
- if (flowItem.aiQuery) {
15519
- return `aiQuery: ${sliceText(flowItem.aiQuery)}`;
15520
- }
15521
- if (flowItem.aiWaitFor) {
15522
- return `aiWaitFor: ${sliceText(
15523
- flowItem.aiWaitFor
15524
- )}`;
15525
- }
15526
- if (flowItem.sleep) {
15527
- return `sleep: ${flowItem.sleep}`;
15528
- }
15529
- return "";
15530
- };
15531
- function textForStatus(status) {
15456
+ function indicatorForStatus(status) {
15532
15457
  if (status === "init") {
15533
15458
  return import_chalk.default.gray("◌");
15534
15459
  }
@@ -15547,7 +15472,7 @@ var contextInfo = (context) => {
15547
15472
  const fileName = (0, import_node_path3.basename)(filePath);
15548
15473
  const fileDir = (0, import_node_path3.dirname)(filePath);
15549
15474
  const fileNameToPrint = `${import_chalk.default.gray(`${fileDir}/`)}${fileName}`;
15550
- const fileStatusText = textForStatus(context.player.status);
15475
+ const fileStatusText = indicatorForStatus(context.player.status);
15551
15476
  const contextActionText = typeof context.player.currentTaskIndex === "undefined" && context.player.status === "running" ? import_chalk.default.gray("(navigating)") : "";
15552
15477
  const outputFile = context.player.output;
15553
15478
  const outputText = outputFile && Object.keys(context.player.result || {}).length > 0 ? `
@@ -15576,7 +15501,7 @@ var singleTaskInfo = (task) => {
15576
15501
  stepText = import_chalk.default.gray("(navigating)");
15577
15502
  } else if (typeof task.currentStep === "number") {
15578
15503
  const currentFlowItem = task.flow[task.currentStep];
15579
- const taskBrief = currentFlowItem && flowItemBrief(currentFlowItem);
15504
+ const taskBrief = currentFlowItem && (0, import_web.flowItemBrief)(currentFlowItem);
15580
15505
  const actionText = taskBrief ? `, ${taskBrief}` : "";
15581
15506
  stepText = import_chalk.default.gray(
15582
15507
  `(step ${task.currentStep + 1}/${task.totalSteps}${actionText})`.trim()
@@ -15588,7 +15513,7 @@ var singleTaskInfo = (task) => {
15588
15513
  const errorText = task.status === "error" ? `
15589
15514
  ${indent}${import_chalk.default.gray("error:")}
15590
15515
  ${indent}${indent}${(_a4 = task.error) == null ? void 0 : _a4.message}` : "";
15591
- const statusText = textForStatus(task.status);
15516
+ const statusText = indicatorForStatus(task.status);
15592
15517
  const mergedLine = `${statusText} ${task.name} ${stepText}${errorText}`;
15593
15518
  return {
15594
15519
  nameText: task.name,
@@ -15603,12 +15528,12 @@ function paddingLines(lines) {
15603
15528
  return `${indent}${line}`;
15604
15529
  });
15605
15530
  }
15606
- var contextTaskListSummary = (taskStatus, context) => {
15531
+ var contextTaskListSummary = (taskStatusArray, context) => {
15607
15532
  const prefixLines = [];
15608
15533
  const currentLine = [];
15609
15534
  const suffixText = [];
15610
15535
  const { mergedText: fileInfo } = contextInfo(context);
15611
- for (const task of taskStatus) {
15536
+ for (const task of taskStatusArray) {
15612
15537
  const { mergedLine } = singleTaskInfo(task);
15613
15538
  if (context.player.status === "init") {
15614
15539
  suffixText.push(mergedLine);
@@ -16130,31 +16055,9 @@ function getRenderedRowCount(contents, stream2) {
16130
16055
  return count;
16131
16056
  }
16132
16057
 
16133
- // src/yaml-player.ts
16134
- var defaultUA = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36";
16135
- var defaultViewportWidth = 1280;
16136
- var defaultViewportHeight = 960;
16137
- var defaultViewportScale = process.platform === "darwin" ? 2 : 1;
16138
- var defaultWaitForNetworkIdleTimeout = 10 * 1e3;
16139
- function loadYamlScript(content, filePath) {
16140
- const obj = js_yaml_default.load(content);
16141
- const pathTip = filePath ? `, failed to load ${filePath}` : "";
16142
- (0, import_node_assert2.default)(obj.target, `property "target" is required in yaml script${pathTip}`);
16143
- (0, import_node_assert2.default)(
16144
- typeof obj.target === "object",
16145
- `property "target" must be an object${pathTip}`
16146
- );
16147
- (0, import_node_assert2.default)(
16148
- typeof obj.target.url === "string",
16149
- `property "target.url" must be provided in yaml script: ${pathTip}`
16150
- );
16151
- (0, import_node_assert2.default)(obj.tasks, `property "tasks" is required in yaml script${pathTip}`);
16152
- (0, import_node_assert2.default)(
16153
- Array.isArray(obj.tasks),
16154
- `property "tasks" must be an array${pathTip}`
16155
- );
16156
- return obj;
16157
- }
16058
+ // src/yaml-runner.ts
16059
+ var import_node_console = require("console");
16060
+ var import_puppeteer = require("@midscene/web/puppeteer");
16158
16061
  var launchServer = (dir) => __async(void 0, null, function* () {
16159
16062
  return new Promise((resolve, reject) => {
16160
16063
  const server = (0, import_http_server.createServer)({
@@ -16170,16 +16073,47 @@ function playYamlFiles(files, options) {
16170
16073
  return __async(this, null, function* () {
16171
16074
  const fileContextList = [];
16172
16075
  for (const file of files) {
16173
- const script = loadYamlScript((0, import_node_fs3.readFileSync)(file, "utf-8"), file);
16076
+ const script = (0, import_web2.parseYamlScript)((0, import_node_fs3.readFileSync)(file, "utf-8"), file);
16174
16077
  const fileName = (0, import_node_path4.basename)(file, (0, import_node_path4.extname)(file));
16175
- const player = new ScriptPlayer(script, __spreadProps(__spreadValues({}, options), {
16176
- testId: fileName,
16177
- onTaskStatusChange: (taskStatus) => {
16078
+ const preference = {
16079
+ headed: options == null ? void 0 : options.headed,
16080
+ keepWindow: options == null ? void 0 : options.keepWindow,
16081
+ testId: fileName
16082
+ };
16083
+ const player = new import_web2.ScriptPlayer(
16084
+ script,
16085
+ (target) => __async(this, null, function* () {
16086
+ const freeFn = [];
16087
+ let localServer;
16088
+ let urlToVisit;
16089
+ (0, import_node_console.assert)(typeof target.url === "string", "url is required");
16090
+ if (target.serve) {
16091
+ localServer = yield launchServer(target.serve);
16092
+ const serverAddress = localServer.server.address();
16093
+ freeFn.push({
16094
+ name: "local_server",
16095
+ fn: () => localServer == null ? void 0 : localServer.server.close()
16096
+ });
16097
+ if (target.url.startsWith("/")) {
16098
+ urlToVisit = `http://${serverAddress == null ? void 0 : serverAddress.address}:${serverAddress == null ? void 0 : serverAddress.port}${target.url}`;
16099
+ } else {
16100
+ urlToVisit = `http://${serverAddress == null ? void 0 : serverAddress.address}:${serverAddress == null ? void 0 : serverAddress.port}/${target.url}`;
16101
+ }
16102
+ target.url = urlToVisit;
16103
+ }
16104
+ const { agent, freeFn: newFreeFn } = yield (0, import_puppeteer.puppeteerAgentForTarget)(
16105
+ target,
16106
+ preference
16107
+ );
16108
+ freeFn.push(...newFreeFn);
16109
+ return { agent, freeFn };
16110
+ }),
16111
+ (taskStatus) => {
16178
16112
  if (!isTTY) {
16179
16113
  const { nameText } = singleTaskInfo(taskStatus);
16180
16114
  }
16181
16115
  }
16182
- }));
16116
+ );
16183
16117
  fileContextList.push({ file, player });
16184
16118
  }
16185
16119
  if (isTTY) {
@@ -16187,7 +16121,7 @@ function playYamlFiles(files, options) {
16187
16121
  const summary = [""];
16188
16122
  for (const context of fileContextList) {
16189
16123
  summary.push(
16190
- contextTaskListSummary(context.player.taskStatus, context)
16124
+ contextTaskListSummary(context.player.taskStatusList, context)
16191
16125
  );
16192
16126
  }
16193
16127
  summary.push("");
@@ -16209,298 +16143,15 @@ function playYamlFiles(files, options) {
16209
16143
  const { mergedText } = contextInfo(context);
16210
16144
  console.log(mergedText);
16211
16145
  yield context.player.run();
16212
- console.log(contextTaskListSummary(context.player.taskStatus, context));
16146
+ console.log(
16147
+ contextTaskListSummary(context.player.taskStatusList, context)
16148
+ );
16213
16149
  }
16214
16150
  }
16215
16151
  const ifFail = fileContextList.some((task) => task.player.status === "error");
16216
16152
  return !ifFail;
16217
16153
  });
16218
16154
  }
16219
- var ScriptPlayer = class {
16220
- constructor(script, options) {
16221
- this.script = script;
16222
- this.options = options;
16223
- __publicField(this, "currentTaskIndex");
16224
- __publicField(this, "taskStatus", []);
16225
- __publicField(this, "status", "init");
16226
- __publicField(this, "reportFile");
16227
- __publicField(this, "result");
16228
- __publicField(this, "unnamedResultIndex", 0);
16229
- __publicField(this, "output");
16230
- __publicField(this, "errorInSetup");
16231
- this.result = {};
16232
- this.output = script.target.output;
16233
- this.taskStatus = (script.tasks || []).map((task, taskIndex) => {
16234
- var _a4;
16235
- return __spreadProps(__spreadValues({}, task), {
16236
- index: taskIndex,
16237
- status: "init",
16238
- totalSteps: ((_a4 = task.flow) == null ? void 0 : _a4.length) || 0
16239
- });
16240
- });
16241
- }
16242
- setPlayerStatus(status, error) {
16243
- this.status = status;
16244
- this.errorInSetup = error;
16245
- }
16246
- setTaskStatus(index, statusValue, error) {
16247
- var _a4;
16248
- this.taskStatus[index].status = statusValue;
16249
- if (error) {
16250
- this.taskStatus[index].error = error;
16251
- }
16252
- if ((_a4 = this.options) == null ? void 0 : _a4.onTaskStatusChange) {
16253
- this.options.onTaskStatusChange(this.taskStatus[index]);
16254
- }
16255
- }
16256
- setTaskIndex(taskIndex) {
16257
- this.currentTaskIndex = taskIndex;
16258
- }
16259
- flushResult() {
16260
- if (Object.keys(this.result).length && this.output) {
16261
- const output = (0, import_node_path4.join)(process.cwd(), this.output);
16262
- const outputDir = (0, import_node_path4.dirname)(output);
16263
- if (!(0, import_node_fs3.existsSync)(outputDir)) {
16264
- (0, import_node_fs3.mkdirSync)(outputDir, { recursive: true });
16265
- }
16266
- (0, import_node_fs3.writeFileSync)(output, JSON.stringify(this.result, void 0, 2));
16267
- }
16268
- }
16269
- playTask(_0, _1) {
16270
- return __async(this, arguments, function* (taskStatus, {
16271
- agent,
16272
- browser,
16273
- page
16274
- }) {
16275
- const { flow } = taskStatus;
16276
- (0, import_node_assert2.default)(flow, "missing flow in task");
16277
- for (const flowItemIndex in flow) {
16278
- taskStatus.currentStep = Number.parseInt(flowItemIndex, 10);
16279
- const flowItem = flow[flowItemIndex];
16280
- if (flowItem.aiAction || flowItem.ai) {
16281
- const actionTask = flowItem;
16282
- const prompt = actionTask.aiAction || actionTask.ai;
16283
- (0, import_node_assert2.default)(prompt, "missing prompt for ai (aiAction)");
16284
- (0, import_node_assert2.default)(
16285
- typeof prompt === "string",
16286
- "prompt for aiAction must be a string"
16287
- );
16288
- yield agent.aiAction(prompt, {
16289
- onTaskStart(task) {
16290
- const tip = `${(0, import_ui_utils.typeStr)(task)} - ${(0, import_ui_utils.paramStr)(task)}`;
16291
- flowItem.aiActionProgressTip = tip;
16292
- }
16293
- });
16294
- } else if (flowItem.aiAssert) {
16295
- const assertTask = flowItem;
16296
- const prompt = assertTask.aiAssert;
16297
- (0, import_node_assert2.default)(prompt, "missing prompt for aiAssert");
16298
- (0, import_node_assert2.default)(
16299
- typeof prompt === "string",
16300
- "prompt for aiAssert must be a string"
16301
- );
16302
- yield agent.aiAssert(prompt);
16303
- } else if (flowItem.aiQuery) {
16304
- const queryTask = flowItem;
16305
- const prompt = queryTask.aiQuery;
16306
- (0, import_node_assert2.default)(prompt, "missing prompt for aiQuery");
16307
- (0, import_node_assert2.default)(
16308
- typeof prompt === "string",
16309
- "prompt for aiQuery must be a string"
16310
- );
16311
- const queryResult = yield agent.aiQuery(prompt);
16312
- const resultKey = queryTask.name || this.unnamedResultIndex++;
16313
- if (this.result[resultKey]) {
16314
- console.warn(
16315
- `result key ${resultKey} already exists, will overwrite`
16316
- );
16317
- }
16318
- this.result[resultKey] = queryResult;
16319
- this.flushResult();
16320
- } else if (flowItem.aiWaitFor) {
16321
- const waitForTask = flowItem;
16322
- const prompt = waitForTask.aiWaitFor;
16323
- (0, import_node_assert2.default)(prompt, "missing prompt for aiWaitFor");
16324
- (0, import_node_assert2.default)(
16325
- typeof prompt === "string",
16326
- "prompt for aiWaitFor must be a string"
16327
- );
16328
- const timeout = waitForTask.timeout;
16329
- yield agent.aiWaitFor(prompt, { timeoutMs: timeout });
16330
- } else if (flowItem.sleep) {
16331
- const sleepTask = flowItem;
16332
- const ms = sleepTask.sleep;
16333
- (0, import_node_assert2.default)(
16334
- ms && ms > 0,
16335
- `ms for sleep must be greater than 0, but got ${ms}`
16336
- );
16337
- yield new Promise((resolve) => setTimeout(resolve, ms));
16338
- } else {
16339
- throw new Error(`unknown flowItem: ${JSON.stringify(flowItem)}`);
16340
- }
16341
- }
16342
- this.reportFile = agent.reportFile;
16343
- });
16344
- }
16345
- run() {
16346
- return __async(this, null, function* () {
16347
- var _a4, _b2, _c2, _d2, _e2, _f2;
16348
- const { target, tasks } = this.script;
16349
- this.setPlayerStatus("running");
16350
- const ua = target.userAgent || defaultUA;
16351
- let width = defaultViewportWidth;
16352
- if (target.viewportWidth) {
16353
- (0, import_node_assert2.default)(
16354
- typeof target.viewportWidth === "number",
16355
- "viewportWidth must be a number"
16356
- );
16357
- width = Number.parseInt(target.viewportWidth, 10);
16358
- (0, import_node_assert2.default)(
16359
- width > 0,
16360
- `viewportWidth must be greater than 0, but got ${width}`
16361
- );
16362
- }
16363
- let height = defaultViewportHeight;
16364
- if (target.viewportHeight) {
16365
- (0, import_node_assert2.default)(
16366
- typeof target.viewportHeight === "number",
16367
- "viewportHeight must be a number"
16368
- );
16369
- height = Number.parseInt(target.viewportHeight, 10);
16370
- (0, import_node_assert2.default)(
16371
- height > 0,
16372
- `viewportHeight must be greater than 0, but got ${height}`
16373
- );
16374
- }
16375
- let dpr = defaultViewportScale;
16376
- if (target.viewportScale) {
16377
- (0, import_node_assert2.default)(
16378
- typeof target.viewportScale === "number",
16379
- "viewportScale must be a number"
16380
- );
16381
- dpr = Number.parseInt(target.viewportScale, 10);
16382
- (0, import_node_assert2.default)(dpr > 0, `viewportScale must be greater than 0, but got ${dpr}`);
16383
- }
16384
- const viewportConfig = {
16385
- width,
16386
- height,
16387
- deviceScaleFactor: dpr
16388
- };
16389
- const freeFn = [];
16390
- let localServer;
16391
- let urlToVisit;
16392
- (0, import_node_assert2.default)(typeof target.url === "string", "url is required");
16393
- if (target.serve) {
16394
- localServer = yield launchServer(target.serve);
16395
- const serverAddress = localServer.server.address();
16396
- freeFn.push({
16397
- name: "local_server",
16398
- fn: () => localServer == null ? void 0 : localServer.server.close()
16399
- });
16400
- if (target.url.startsWith("/")) {
16401
- urlToVisit = `http://${serverAddress == null ? void 0 : serverAddress.address}:${serverAddress == null ? void 0 : serverAddress.port}${target.url}`;
16402
- } else {
16403
- urlToVisit = `http://${serverAddress == null ? void 0 : serverAddress.address}:${serverAddress == null ? void 0 : serverAddress.port}/${target.url}`;
16404
- }
16405
- } else {
16406
- urlToVisit = target.url;
16407
- }
16408
- const headed = ((_a4 = this.options) == null ? void 0 : _a4.headed) || ((_b2 = this.options) == null ? void 0 : _b2.keepWindow);
16409
- if (headed && process.env.CI === "1") {
16410
- console.warn(
16411
- "you are probably running headed mode in CI, this will usually fail."
16412
- );
16413
- }
16414
- const browser = yield import_puppeteer.default.launch({
16415
- headless: !headed,
16416
- args: [
16417
- "--no-sandbox",
16418
- "--disable-setuid-sandbox",
16419
- "--disable-features=PasswordLeakDetection",
16420
- "--disable-save-password-bubble"
16421
- ]
16422
- });
16423
- freeFn.push({
16424
- name: "puppeteer_browser",
16425
- fn: () => {
16426
- var _a5;
16427
- if (!((_a5 = this.options) == null ? void 0 : _a5.keepWindow)) {
16428
- browser.close();
16429
- }
16430
- }
16431
- });
16432
- const pages = yield browser.pages();
16433
- const page = pages[0];
16434
- yield page.setUserAgent(ua);
16435
- yield page.setViewport(viewportConfig);
16436
- if (target.cookie) {
16437
- const cookieFileContent = (0, import_node_fs3.readFileSync)(target.cookie, "utf-8");
16438
- yield page.setCookie(...JSON.parse(cookieFileContent));
16439
- }
16440
- yield page.goto(urlToVisit);
16441
- const waitForNetworkIdleTimeout = typeof ((_c2 = target.waitForNetworkIdle) == null ? void 0 : _c2.timeout) === "number" ? target.waitForNetworkIdle.timeout : defaultWaitForNetworkIdleTimeout;
16442
- try {
16443
- if (waitForNetworkIdleTimeout > 0) {
16444
- yield page.waitForNetworkIdle({
16445
- timeout: waitForNetworkIdleTimeout
16446
- });
16447
- }
16448
- } catch (e) {
16449
- if (typeof ((_d2 = target.waitForNetworkIdle) == null ? void 0 : _d2.continueOnNetworkIdleError) === "boolean" && !((_e2 = target.waitForNetworkIdle) == null ? void 0 : _e2.continueOnNetworkIdleError)) {
16450
- const newError = new Error(`failed to wait for network idle: ${e}`, {
16451
- cause: e
16452
- });
16453
- this.setPlayerStatus("error", newError);
16454
- return;
16455
- }
16456
- const newMessage = `failed to wait for network idle after ${waitForNetworkIdleTimeout}ms, but the script will continue.`;
16457
- console.warn(newMessage);
16458
- }
16459
- const agent = new import_puppeteer2.PuppeteerAgent(page, {
16460
- autoPrintReportMsg: false,
16461
- testId: (_f2 = this.options) == null ? void 0 : _f2.testId
16462
- });
16463
- freeFn.push({
16464
- name: "midscene_puppeteer_agent",
16465
- fn: () => agent.destroy()
16466
- });
16467
- let taskIndex = 0;
16468
- this.setPlayerStatus("running");
16469
- let errorFlag = false;
16470
- while (taskIndex < tasks.length) {
16471
- const task = tasks[taskIndex];
16472
- this.setTaskStatus(taskIndex, "running");
16473
- try {
16474
- this.setTaskIndex(taskIndex);
16475
- yield this.playTask(this.taskStatus[taskIndex], {
16476
- agent,
16477
- browser,
16478
- page
16479
- });
16480
- } catch (e) {
16481
- this.setTaskStatus(taskIndex, "error", e);
16482
- this.setPlayerStatus("error");
16483
- errorFlag = true;
16484
- this.reportFile = agent.reportFile;
16485
- taskIndex++;
16486
- continue;
16487
- }
16488
- this.reportFile = agent.reportFile;
16489
- this.setTaskStatus(taskIndex, "done");
16490
- taskIndex++;
16491
- }
16492
- if (!errorFlag) {
16493
- this.setPlayerStatus("done");
16494
- }
16495
- freeFn.forEach((fn) => {
16496
- try {
16497
- fn.fn();
16498
- } catch (e) {
16499
- }
16500
- });
16501
- });
16502
- }
16503
- };
16504
16155
 
16505
16156
  // src/index.ts
16506
16157
  Promise.resolve(
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@midscene/cli",
3
3
  "description": "An AI-powered automation SDK can control the page, perform assertions, and extract data in JSON format using natural language. See https://midscenejs.com/ for details.",
4
- "version": "0.8.9",
4
+ "version": "0.8.10-beta-20241225120902.0",
5
5
  "repository": "https://github.com/web-infra-dev/midscene",
6
6
  "homepage": "https://midscenejs.com/",
7
7
  "jsnext:source": "./src/index.ts",
@@ -15,10 +15,10 @@
15
15
  "bin"
16
16
  ],
17
17
  "dependencies": {
18
- "http-server": "14.1.1",
19
18
  "puppeteer": "23.0.2",
20
- "@midscene/core": "0.8.9",
21
- "@midscene/web": "0.8.9"
19
+ "http-server": "14.1.1",
20
+ "@midscene/core": "0.8.10-beta-20241225120902.0",
21
+ "@midscene/web": "0.8.10-beta-20241225120902.0"
22
22
  },
23
23
  "devDependencies": {
24
24
  "@modern-js/module-tools": "2.60.6",
@@ -38,7 +38,7 @@
38
38
  "yargs": "17.7.2"
39
39
  },
40
40
  "engines": {
41
- "node": ">=16.0.0"
41
+ "node": ">=18.0.0"
42
42
  },
43
43
  "publishConfig": {
44
44
  "access": "public",