@xcanwin/manyoyo 5.1.5 → 5.1.8

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.
@@ -4,6 +4,7 @@ const fs = require('fs');
4
4
  const net = require('net');
5
5
  const os = require('os');
6
6
  const path = require('path');
7
+ const crypto = require('crypto');
7
8
  const { spawn, spawnSync } = require('child_process');
8
9
 
9
10
  const EXTENSIONS = [
@@ -56,6 +57,14 @@ const SCENE_DEFS = {
56
57
  const VALID_RUNTIME = new Set(['container', 'host', 'mixed']);
57
58
  const VALID_ACTIONS = new Set(['up', 'down', 'status', 'health', 'logs']);
58
59
  const CONTAINER_EXTENSION_ROOT = '/app/extensions';
60
+ const DEFAULT_FINGERPRINT_PROFILE = {
61
+ userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36',
62
+ locale: 'zh-CN',
63
+ acceptLanguage: 'zh-CN,zh;q=0.9',
64
+ timezoneId: 'Asia/Shanghai',
65
+ width: 1366,
66
+ height: 768
67
+ };
59
68
 
60
69
  function sleep(ms) {
61
70
  return new Promise(resolve => setTimeout(resolve, ms));
@@ -227,7 +236,6 @@ class PlaywrightPlugin {
227
236
  hostListen: '127.0.0.1',
228
237
  mcpDefaultHost: 'host.docker.internal',
229
238
  dockerTag: process.env.PLAYWRIGHT_MCP_DOCKER_TAG || 'latest',
230
- npmVersion: process.env.PLAYWRIGHT_MCP_NPM_VERSION || 'latest',
231
239
  containerRuntime: 'podman',
232
240
  vncPasswordEnvKey: 'VNC_PASSWORD',
233
241
  headedImage: 'localhost/xcanwin/manyoyo-playwright-headed',
@@ -285,6 +293,15 @@ class PlaywrightPlugin {
285
293
  this.stderr.write(`${line}\n`);
286
294
  }
287
295
 
296
+ randomAlnum(length = 16) {
297
+ const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
298
+ let out = '';
299
+ for (let i = 0; i < length; i += 1) {
300
+ out += chars[crypto.randomInt(0, chars.length)];
301
+ }
302
+ return out;
303
+ }
304
+
288
305
  runCmd(args, { env = null, captureOutput = false, check = true } = {}) {
289
306
  const result = spawnSync(args[0], args.slice(1), {
290
307
  encoding: 'utf8',
@@ -362,7 +379,7 @@ class PlaywrightPlugin {
362
379
  if (!this.sceneConfigMissing(sceneName)) {
363
380
  return;
364
381
  }
365
- this.runCmd(['npx', '-y', 'playwright-core', 'install', '--with-deps', this.defaultBrowserName(sceneName)], { check: true });
382
+ this.runCmd([this.localBinPath('playwright'), 'install', '--with-deps', this.defaultBrowserName(sceneName)], { check: true });
366
383
  }
367
384
 
368
385
  scenePidFile(sceneName) {
@@ -373,6 +390,15 @@ class PlaywrightPlugin {
373
390
  return path.join(this.config.runDir, `${sceneName}.log`);
374
391
  }
375
392
 
393
+ localBinPath(binName) {
394
+ const filename = process.platform === 'win32' ? `${binName}.cmd` : binName;
395
+ const binPath = path.join(this.projectRoot, 'node_modules', '.bin', filename);
396
+ if (!fs.existsSync(binPath)) {
397
+ throw new Error(`local binary not found: ${binPath}. Run npm install first.`);
398
+ }
399
+ return binPath;
400
+ }
401
+
376
402
  extensionDirPath() {
377
403
  return path.join(os.homedir(), '.manyoyo', 'plugin', 'playwright', 'extensions');
378
404
  }
@@ -510,13 +536,21 @@ class PlaywrightPlugin {
510
536
  const def = SCENE_DEFS[sceneName];
511
537
  const port = this.scenePort(sceneName);
512
538
  const extensionPaths = asStringArray(options.extensionPaths, []);
539
+ const baseLaunchArgs = [
540
+ `--user-agent=${DEFAULT_FINGERPRINT_PROFILE.userAgent}`,
541
+ `--lang=${DEFAULT_FINGERPRINT_PROFILE.locale}`,
542
+ `--window-size=${DEFAULT_FINGERPRINT_PROFILE.width},${DEFAULT_FINGERPRINT_PROFILE.height}`,
543
+ '--disable-blink-features=AutomationControlled',
544
+ '--force-webrtc-ip-handling-policy=disable_non_proxied_udp'
545
+ ];
513
546
  const launchOptions = {
514
547
  channel: 'chromium',
515
- headless: def.headless
548
+ headless: def.headless,
549
+ args: [...baseLaunchArgs]
516
550
  };
517
551
 
518
552
  if (extensionPaths.length > 0) {
519
- launchOptions.args = this.buildExtensionLaunchArgs(extensionPaths);
553
+ launchOptions.args.push(...this.buildExtensionLaunchArgs(extensionPaths));
520
554
  }
521
555
 
522
556
  return {
@@ -535,7 +569,20 @@ class PlaywrightPlugin {
535
569
  browserName: 'chromium',
536
570
  launchOptions,
537
571
  contextOptions: {
538
- userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36'
572
+ userAgent: DEFAULT_FINGERPRINT_PROFILE.userAgent,
573
+ locale: DEFAULT_FINGERPRINT_PROFILE.locale,
574
+ timezoneId: DEFAULT_FINGERPRINT_PROFILE.timezoneId,
575
+ viewport: {
576
+ width: DEFAULT_FINGERPRINT_PROFILE.width,
577
+ height: DEFAULT_FINGERPRINT_PROFILE.height
578
+ },
579
+ screen: {
580
+ width: DEFAULT_FINGERPRINT_PROFILE.width,
581
+ height: DEFAULT_FINGERPRINT_PROFILE.height
582
+ },
583
+ extraHTTPHeaders: {
584
+ 'Accept-Language': DEFAULT_FINGERPRINT_PROFILE.acceptLanguage
585
+ }
539
586
  }
540
587
  }
541
588
  };
@@ -571,7 +618,7 @@ class PlaywrightPlugin {
571
618
  }
572
619
 
573
620
  async waitForPort(port) {
574
- for (let i = 0; i < 30; i += 1) {
621
+ for (let i = 0; i < 60; i += 1) {
575
622
  // eslint-disable-next-line no-await-in-loop
576
623
  if (await this.portReady(port)) {
577
624
  return true;
@@ -597,13 +644,14 @@ class PlaywrightPlugin {
597
644
 
598
645
  if (sceneName === 'cont-headed') {
599
646
  const envKey = this.config.vncPasswordEnvKey;
600
- const password = process.env[envKey];
601
- if (!password && requireVncPassword) {
602
- throw new Error(`${envKey} is required for cont-headed`);
647
+ let password = process.env[envKey];
648
+ if (!password) {
649
+ password = this.randomAlnum(16);
650
+ if (requireVncPassword) {
651
+ this.writeStdout(`[up] cont-headed ${envKey} not set; generated random 16-char password: ${password}`);
652
+ }
603
653
  }
604
- // podman-compose resolves ${VNC_PASSWORD:?..} even for `down`.
605
- // Keep `up` strict, but use a non-empty placeholder for non-up actions.
606
- env.VNC_PASSWORD = password || '__MANYOYO_PLACEHOLDER__';
654
+ env.VNC_PASSWORD = password;
607
655
  }
608
656
 
609
657
  return env;
@@ -784,8 +832,8 @@ class PlaywrightPlugin {
784
832
  return cp.returncode === 0 ? 0 : 1;
785
833
  }
786
834
 
787
- spawnHostProcess(cfgPath, logFd) {
788
- return spawn('npx', [`@playwright/mcp@${this.config.npmVersion}`, '--config', String(cfgPath)], {
835
+ spawnHostProcess(mcpBinPath, cfgPath, logFd) {
836
+ return spawn(mcpBinPath, ['--config', String(cfgPath)], {
789
837
  detached: true,
790
838
  stdio: ['ignore', logFd, logFd]
791
839
  });
@@ -843,14 +891,10 @@ class PlaywrightPlugin {
843
891
  }
844
892
 
845
893
  async startHost(sceneName, options = {}) {
846
- if (!this.ensureCommandAvailable('npx')) {
847
- this.writeStderr(`[up] ${sceneName} failed: npx command not found.`);
848
- return 1;
849
- }
850
-
851
894
  try {
852
895
  this.ensureHostScenePrerequisites(sceneName);
853
896
  } catch (error) {
897
+ this.writeStderr(`[up] ${sceneName} failed: ${error.message || String(error)}`);
854
898
  return error.returncode || 1;
855
899
  }
856
900
 
@@ -874,7 +918,16 @@ class PlaywrightPlugin {
874
918
 
875
919
  fs.rmSync(pidFile, { force: true });
876
920
  const logFd = fs.openSync(logFile, 'a');
877
- const starter = this.spawnHostProcess(cfgPath, logFd);
921
+ let mcpBinPath = '';
922
+ try {
923
+ mcpBinPath = this.localBinPath('playwright-mcp');
924
+ } catch (error) {
925
+ fs.closeSync(logFd);
926
+ this.writeStderr(`[up] ${sceneName} failed: ${error.message || String(error)}`);
927
+ return 1;
928
+ }
929
+
930
+ const starter = this.spawnHostProcess(mcpBinPath, cfgPath, logFd);
878
931
  fs.closeSync(logFd);
879
932
  if (typeof starter.unref === 'function') {
880
933
  starter.unref();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xcanwin/manyoyo",
3
- "version": "5.1.5",
3
+ "version": "5.1.8",
4
4
  "imageVersion": "1.8.1-common",
5
5
  "description": "AI Agent CLI Security Sandbox for Docker and Podman",
6
6
  "keywords": [
@@ -53,10 +53,12 @@
53
53
  "config.example.json"
54
54
  ],
55
55
  "dependencies": {
56
+ "@playwright/mcp": "0.0.68",
56
57
  "@xterm/addon-fit": "^0.11.0",
57
58
  "@xterm/xterm": "^6.0.0",
58
59
  "commander": "^12.0.0",
59
60
  "json5": "^2.2.3",
61
+ "playwright": "1.58.2",
60
62
  "ws": "^8.19.0"
61
63
  },
62
64
  "devDependencies": {