@midscene/cli 1.3.11-beta-20260211054343.0 → 1.3.11

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.mjs CHANGED
@@ -11,20 +11,14 @@ import { createAgent } from "@midscene/core/agent";
11
11
  import { processCacheConfig } from "@midscene/core/utils";
12
12
  import { getDebug } from "@midscene/shared/logger";
13
13
  import { AgentOverChromeBridge } from "@midscene/web/bridge-mode";
14
- import { parseArgs, stripVTControlCharacters } from "node:util";
14
+ import { stripVTControlCharacters } from "node:util";
15
15
  import node_process, { cwd as external_node_process_cwd } from "node:process";
16
16
  import { fileURLToPath } from "node:url";
17
- import { lstat, mkdir, readFile, readdir, readlink, realpath, unlink, writeFile } from "node:fs/promises";
17
+ import { lstat, readdir, readlink, realpath } from "node:fs/promises";
18
18
  import { EventEmitter } from "node:events";
19
19
  import node_stream from "node:stream";
20
20
  import { StringDecoder } from "node:string_decoder";
21
21
  import { fileURLToPath as external_url_fileURLToPath } from "url";
22
- import { spawn } from "node:child_process";
23
- import { tmpdir } from "node:os";
24
- import { agentFromAdbDevice as android_agentFromAdbDevice } from "@midscene/android";
25
- import { agentFromComputer as computer_agentFromComputer } from "@midscene/computer";
26
- import { agentFromWebDriverAgent as ios_agentFromWebDriverAgent } from "@midscene/ios";
27
- import { PuppeteerAgent } from "@midscene/web/puppeteer";
28
22
  import * as __rspack_external_assert from "assert";
29
23
  import * as __rspack_external_crypto from "crypto";
30
24
  import * as __rspack_external_fs from "fs";
@@ -3026,7 +3020,11 @@ var __webpack_modules__ = {
3026
3020
  module.exports = (string, columns, options)=>String(string).normalize().replace(/\r\n/g, '\n').split('\n').map((line)=>exec(line, columns, options)).join('\n');
3027
3021
  },
3028
3022
  "./src/index.ts" (__unused_rspack_module, __unused_rspack___webpack_exports__, __webpack_require__) {
3029
- var package_namespaceObject = JSON.parse('{"rE":"1.3.11-beta-20260211054343.0"}');
3023
+ var main = __webpack_require__("../../node_modules/.pnpm/dotenv@16.4.5/node_modules/dotenv/lib/main.js");
3024
+ var main_default = /*#__PURE__*/ __webpack_require__.n(main);
3025
+ var package_namespaceObject = {
3026
+ rE: "1.3.11"
3027
+ };
3030
3028
  class Node {
3031
3029
  value;
3032
3030
  next;
@@ -4104,8 +4102,6 @@ var __webpack_modules__ = {
4104
4102
  this.config = config;
4105
4103
  }
4106
4104
  }
4107
- var main = __webpack_require__("../../node_modules/.pnpm/dotenv@16.4.5/node_modules/dotenv/lib/main.js");
4108
- var main_default = /*#__PURE__*/ __webpack_require__.n(main);
4109
4105
  var brace_expansion = __webpack_require__("../../node_modules/.pnpm/brace-expansion@2.0.1/node_modules/brace-expansion/index.js");
4110
4106
  const MAX_PATTERN_LENGTH = 65536;
4111
4107
  const assertValidPattern = (pattern)=>{
@@ -6152,7 +6148,7 @@ var __webpack_modules__ = {
6152
6148
  }
6153
6149
  }
6154
6150
  var external_fs_ = __webpack_require__("fs");
6155
- const esm_proc = 'object' == typeof process && process ? process : {
6151
+ const proc = 'object' == typeof process && process ? process : {
6156
6152
  stdout: null,
6157
6153
  stderr: null
6158
6154
  };
@@ -6456,7 +6452,7 @@ var __webpack_modules__ = {
6456
6452
  this[DISCARDED] = false;
6457
6453
  const ended = this[EMITTED_END];
6458
6454
  opts = opts || {};
6459
- if (dest === esm_proc.stdout || dest === esm_proc.stderr) opts.end = false;
6455
+ if (dest === proc.stdout || dest === proc.stderr) opts.end = false;
6460
6456
  else opts.end = false !== opts.end;
6461
6457
  opts.proxyErrors = !!opts.proxyErrors;
6462
6458
  if (ended) {
@@ -11869,16 +11865,6 @@ var __webpack_modules__ = {
11869
11865
  }
11870
11866
  };
11871
11867
  }
11872
- function loadEnv(options) {
11873
- const envFile = join(process.cwd(), '.env');
11874
- if (!(0, __rspack_external_node_fs_5ea92f0c.existsSync)(envFile)) return;
11875
- if (options?.verbose) console.log(` Env file: ${envFile}`);
11876
- main_default().config({
11877
- path: envFile,
11878
- debug: options?.debug,
11879
- override: options?.override
11880
- });
11881
- }
11882
11868
  const cli_utils_debug = getDebug('midscene:cli');
11883
11869
  function kebabToCamel(str) {
11884
11870
  return str.replace(/-([a-z])/g, (_, letter)=>letter.toUpperCase());
@@ -11938,7 +11924,7 @@ Usage:
11938
11924
  type: 'boolean',
11939
11925
  description: `Turn on logging to help debug why certain keys or values are not being set as you expect, default is ${config_factory_defaultConfig.dotenvDebug}`
11940
11926
  }
11941
- }).version('version', 'Show version number', "1.3.11-beta-20260211054343.0").help().epilogue(`For complete list of configuration options, please visit:
11927
+ }).version('version', 'Show version number', "1.3.11").help().epilogue(`For complete list of configuration options, please visit:
11942
11928
  • Web options: https://midscenejs.com/automate-with-scripts-in-yaml#the-web-part
11943
11929
  • Android options: https://midscenejs.com/automate-with-scripts-in-yaml#the-android-part
11944
11930
  • iOS options: https://midscenejs.com/automate-with-scripts-in-yaml#the-ios-part
@@ -11986,350 +11972,7 @@ Examples:
11986
11972
  });
11987
11973
  return files.filter((file)=>file.endsWith('.yml') || file.endsWith('.yaml')).sort();
11988
11974
  }
11989
- const VALID_PLATFORMS = [
11990
- 'computer',
11991
- 'web',
11992
- 'android',
11993
- 'ios'
11994
- ];
11995
- const puppeteerBrowserManager = {
11996
- endpointFile: join(tmpdir(), 'midscene-puppeteer-endpoint'),
11997
- activeBrowser: null,
11998
- async getOrLaunch () {
11999
- if ((0, __rspack_external_node_fs_5ea92f0c.existsSync)(this.endpointFile)) try {
12000
- const endpoint = (await readFile(this.endpointFile, 'utf-8')).trim();
12001
- const browser = await puppeteer.connect({
12002
- browserWSEndpoint: endpoint,
12003
- defaultViewport: null
12004
- });
12005
- return {
12006
- browser,
12007
- reused: true
12008
- };
12009
- } catch {
12010
- try {
12011
- await unlink(this.endpointFile);
12012
- } catch {}
12013
- }
12014
- const wsEndpoint = await this.launchDetachedChrome();
12015
- await writeFile(this.endpointFile, wsEndpoint);
12016
- const browser = await puppeteer.connect({
12017
- browserWSEndpoint: wsEndpoint,
12018
- defaultViewport: null
12019
- });
12020
- return {
12021
- browser,
12022
- reused: false
12023
- };
12024
- },
12025
- async closeBrowser () {
12026
- if (!(0, __rspack_external_node_fs_5ea92f0c.existsSync)(this.endpointFile)) return;
12027
- try {
12028
- const endpoint = (await readFile(this.endpointFile, 'utf-8')).trim();
12029
- const browser = await puppeteer.connect({
12030
- browserWSEndpoint: endpoint
12031
- });
12032
- await browser.close();
12033
- } catch {}
12034
- try {
12035
- await unlink(this.endpointFile);
12036
- } catch {}
12037
- },
12038
- disconnect () {
12039
- if (this.activeBrowser) {
12040
- this.activeBrowser.disconnect();
12041
- this.activeBrowser = null;
12042
- }
12043
- },
12044
- async launchDetachedChrome () {
12045
- const chromePath = puppeteer.executablePath();
12046
- const args = [
12047
- '--remote-debugging-port=0',
12048
- '--no-first-run',
12049
- '--no-default-browser-check',
12050
- '--disable-extensions',
12051
- '--disable-default-apps',
12052
- '--disable-sync',
12053
- '--disable-background-networking',
12054
- '--password-store=basic',
12055
- '--use-mock-keychain',
12056
- '--window-size=1280,800',
12057
- '--force-color-profile=srgb'
12058
- ];
12059
- const proc = spawn(chromePath, args, {
12060
- detached: true,
12061
- stdio: [
12062
- 'ignore',
12063
- 'ignore',
12064
- 'pipe'
12065
- ]
12066
- });
12067
- proc.unref();
12068
- return new Promise((resolve, reject)=>{
12069
- let output = '';
12070
- const onData = (data)=>{
12071
- output += data.toString();
12072
- const match = output.match(/DevTools listening on (ws:\/\/[^\s]+)/);
12073
- if (match) {
12074
- proc.stderr.removeListener('data', onData);
12075
- resolve(match[1]);
12076
- }
12077
- };
12078
- proc.stderr.on('data', onData);
12079
- setTimeout(()=>reject(new Error('Chrome launch timeout')), 15000);
12080
- });
12081
- }
12082
- };
12083
- async function saveScreenshot(base64) {
12084
- const dir = join(tmpdir(), 'midscene-screenshots');
12085
- await mkdir(dir, {
12086
- recursive: true
12087
- });
12088
- const filename = `screenshot-${new Date().toISOString().replace(/[:.]/g, '-')}.png`;
12089
- const filepath = join(dir, filename);
12090
- const raw = base64.replace(/^data:image\/\w+;base64,/, '');
12091
- await writeFile(filepath, Buffer.from(raw, 'base64'));
12092
- return filepath;
12093
- }
12094
- async function captureScreenshot(agent) {
12095
- const base64 = await agent.page?.screenshotBase64?.();
12096
- return base64 ? saveScreenshot(base64) : void 0;
12097
- }
12098
- async function createPlatformAgent(platform, opts) {
12099
- switch(platform){
12100
- case 'computer':
12101
- return computer_agentFromComputer(opts.display ? {
12102
- displayId: opts.display
12103
- } : void 0);
12104
- case 'web':
12105
- {
12106
- if (opts.bridge) {
12107
- const agent = new AgentOverChromeBridge({
12108
- closeConflictServer: true
12109
- });
12110
- if (opts.url) await agent.connectNewTabWithUrl(opts.url);
12111
- else await agent.connectCurrentTab();
12112
- return agent;
12113
- }
12114
- const { browser, reused } = await puppeteerBrowserManager.getOrLaunch();
12115
- puppeteerBrowserManager.activeBrowser = browser;
12116
- const pages = await browser.pages();
12117
- let page;
12118
- if (opts.url) {
12119
- page = await browser.newPage();
12120
- await page.goto(opts.url, {
12121
- timeout: 30000,
12122
- waitUntil: 'domcontentloaded'
12123
- });
12124
- } else {
12125
- const webPages = pages.filter((p)=>/^https?:\/\//.test(p.url()));
12126
- page = webPages.length > 0 ? webPages[webPages.length - 1] : pages[pages.length - 1] || await browser.newPage();
12127
- if (reused) await page.bringToFront();
12128
- }
12129
- return new PuppeteerAgent(page);
12130
- }
12131
- case 'android':
12132
- return android_agentFromAdbDevice(opts.device);
12133
- case 'ios':
12134
- return ios_agentFromWebDriverAgent();
12135
- default:
12136
- throw new Error(`Unknown platform: ${platform}`);
12137
- }
12138
- }
12139
- async function handleCommand(platform, command, args, opts) {
12140
- const agentOpts = {
12141
- ...opts,
12142
- url: 'navigate' === command ? args : opts.url
12143
- };
12144
- if ('web' === platform && !opts.bridge && 'close' === command) {
12145
- await puppeteerBrowserManager.closeBrowser();
12146
- return {
12147
- success: true,
12148
- message: 'Browser closed'
12149
- };
12150
- }
12151
- const agent = await createPlatformAgent(platform, agentOpts);
12152
- try {
12153
- switch(command){
12154
- case 'act':
12155
- if (!args) throw new Error("act command requires an action description");
12156
- await agent.aiAction(args);
12157
- return {
12158
- success: true,
12159
- message: `Action performed: ${args}`,
12160
- screenshot: await captureScreenshot(agent)
12161
- };
12162
- case 'query':
12163
- {
12164
- if (!args) throw new Error('query command requires a query string');
12165
- const result = await agent.aiQuery(args);
12166
- return {
12167
- success: true,
12168
- result,
12169
- message: `Query completed: ${args}`,
12170
- screenshot: await captureScreenshot(agent)
12171
- };
12172
- }
12173
- case 'assert':
12174
- if (!args) throw new Error('assert command requires a condition');
12175
- try {
12176
- await agent.aiAssert(args);
12177
- return {
12178
- success: true,
12179
- message: `Assertion passed: ${args}`
12180
- };
12181
- } catch (e1) {
12182
- const message = e1 instanceof Error ? e1.message : String(e1);
12183
- return {
12184
- success: false,
12185
- message: `Assertion failed: ${args}`,
12186
- error: message
12187
- };
12188
- }
12189
- case 'screenshot':
12190
- {
12191
- const screenshotPath = await captureScreenshot(agent);
12192
- if (!screenshotPath) return {
12193
- success: false,
12194
- error: 'Screenshot not available'
12195
- };
12196
- return {
12197
- success: true,
12198
- message: 'Screenshot captured',
12199
- screenshot: screenshotPath
12200
- };
12201
- }
12202
- case 'navigate':
12203
- return {
12204
- success: true,
12205
- message: `Navigated to: ${args}`,
12206
- screenshot: await captureScreenshot(agent)
12207
- };
12208
- case 'close':
12209
- await agent.destroy();
12210
- return {
12211
- success: true,
12212
- message: 'Agent destroyed'
12213
- };
12214
- case 'connect':
12215
- return {
12216
- success: true,
12217
- message: `Connected to ${platform}`,
12218
- screenshot: await captureScreenshot(agent)
12219
- };
12220
- default:
12221
- throw new Error(`Unknown command: ${command} for platform: ${platform}`);
12222
- }
12223
- } finally{
12224
- if ('close' !== command) {
12225
- const keepBrowserAlive = 'web' === platform && !opts.bridge;
12226
- if (keepBrowserAlive) puppeteerBrowserManager.disconnect();
12227
- else try {
12228
- await agent.destroy();
12229
- } catch {}
12230
- }
12231
- }
12232
- }
12233
- function printUsage() {
12234
- console.log(`
12235
- Usage: midscene <platform> <command> [args] [options]
12236
-
12237
- Platforms:
12238
- computer Desktop automation (macOS)
12239
- web Browser automation (Puppeteer or Bridge mode)
12240
- android Android device automation
12241
- ios iOS device automation
12242
-
12243
- Commands:
12244
- act "<action>" Perform an action described in natural language
12245
- query "<query>" Extract information from current screen/page
12246
- assert "<condition>" Verify a condition on current screen/page
12247
- screenshot Capture screenshot
12248
- navigate "<url>" Navigate to URL (web only)
12249
- close Close/destroy the agent
12250
- connect Connect to device/browser
12251
-
12252
- Options:
12253
- --bridge Use Chrome Bridge mode instead of Puppeteer (web only)
12254
- --url <url> Specify URL for web commands (web only, Puppeteer mode)
12255
- --device <id> Specify device ID (android only)
12256
- --display <id> Specify display ID (computer only)
12257
-
12258
- Examples:
12259
- midscene computer screenshot
12260
- midscene computer act "press Cmd+Space to open Spotlight"
12261
- midscene web navigate "https://example.com"
12262
- midscene web navigate "https://example.com" --bridge
12263
- midscene web act "click the login button" --url "https://example.com"
12264
- midscene web query "what is the page title?" --url "https://example.com"
12265
- midscene android screenshot
12266
- midscene ios act "tap the search field"
12267
- `);
12268
- }
12269
- function printResult(result) {
12270
- console.log(JSON.stringify(result, null, 2));
12271
- }
12272
- function parsePlatformArgs(argv) {
12273
- const { values, positionals } = parseArgs({
12274
- args: argv.slice(2),
12275
- options: {
12276
- bridge: {
12277
- type: 'boolean',
12278
- default: false
12279
- },
12280
- url: {
12281
- type: 'string'
12282
- },
12283
- device: {
12284
- type: 'string'
12285
- },
12286
- display: {
12287
- type: 'string'
12288
- }
12289
- },
12290
- allowPositionals: true
12291
- });
12292
- return {
12293
- args: positionals[0],
12294
- opts: {
12295
- bridge: values.bridge,
12296
- url: values.url,
12297
- device: values.device,
12298
- display: values.display
12299
- }
12300
- };
12301
- }
12302
- async function runPlatformCli(argv) {
12303
- const platform = argv[0];
12304
- const command = argv[1];
12305
- if (!platform || !command) {
12306
- printUsage();
12307
- process.exit(1);
12308
- }
12309
- if (!VALID_PLATFORMS.includes(platform)) {
12310
- console.error(`Invalid platform: ${platform}`);
12311
- printUsage();
12312
- process.exit(1);
12313
- }
12314
- const { args, opts } = parsePlatformArgs(argv);
12315
- loadEnv();
12316
- try {
12317
- const result = await handleCommand(platform, command, args, opts);
12318
- printResult(result);
12319
- if (!result.success) process.exit(1);
12320
- process.exit(0);
12321
- } catch (e1) {
12322
- const message = e1 instanceof Error ? e1.message : String(e1);
12323
- printResult({
12324
- success: false,
12325
- error: message
12326
- });
12327
- process.exit(1);
12328
- }
12329
- }
12330
- const rawArgs = process.argv.slice(2);
12331
- if (VALID_PLATFORMS.includes(rawArgs[0])) runPlatformCli(rawArgs);
12332
- else Promise.resolve((async ()=>{
11975
+ Promise.resolve((async ()=>{
12333
11976
  const { options, path, files: cmdFiles } = await parseProcessArgs();
12334
11977
  const welcome = `\nWelcome to @midscene/cli v${package_namespaceObject.rE}\n`;
12335
11978
  console.log(welcome);
@@ -12376,11 +12019,15 @@ Examples:
12376
12019
  console.error('Could not create a valid configuration.');
12377
12020
  process.exit(1);
12378
12021
  }
12379
- loadEnv({
12380
- debug: config.dotenvDebug,
12381
- override: config.dotenvOverride,
12382
- verbose: true
12383
- });
12022
+ const dotEnvConfigFile = join(process.cwd(), '.env');
12023
+ if ((0, __rspack_external_node_fs_5ea92f0c.existsSync)(dotEnvConfigFile)) {
12024
+ console.log(` Env file: ${dotEnvConfigFile}`);
12025
+ main_default().config({
12026
+ path: dotEnvConfigFile,
12027
+ debug: config.dotenvDebug,
12028
+ override: config.dotenvOverride
12029
+ });
12030
+ }
12384
12031
  const executor = new BatchRunner(config);
12385
12032
  await executor.run();
12386
12033
  const success = executor.printExecutionSummary();