@xcanwin/manyoyo 5.1.6 → 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 = [
@@ -235,7 +236,6 @@ class PlaywrightPlugin {
235
236
  hostListen: '127.0.0.1',
236
237
  mcpDefaultHost: 'host.docker.internal',
237
238
  dockerTag: process.env.PLAYWRIGHT_MCP_DOCKER_TAG || 'latest',
238
- npmVersion: process.env.PLAYWRIGHT_MCP_NPM_VERSION || 'latest',
239
239
  containerRuntime: 'podman',
240
240
  vncPasswordEnvKey: 'VNC_PASSWORD',
241
241
  headedImage: 'localhost/xcanwin/manyoyo-playwright-headed',
@@ -293,6 +293,15 @@ class PlaywrightPlugin {
293
293
  this.stderr.write(`${line}\n`);
294
294
  }
295
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
+
296
305
  runCmd(args, { env = null, captureOutput = false, check = true } = {}) {
297
306
  const result = spawnSync(args[0], args.slice(1), {
298
307
  encoding: 'utf8',
@@ -370,7 +379,7 @@ class PlaywrightPlugin {
370
379
  if (!this.sceneConfigMissing(sceneName)) {
371
380
  return;
372
381
  }
373
- 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 });
374
383
  }
375
384
 
376
385
  scenePidFile(sceneName) {
@@ -381,6 +390,15 @@ class PlaywrightPlugin {
381
390
  return path.join(this.config.runDir, `${sceneName}.log`);
382
391
  }
383
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
+
384
402
  extensionDirPath() {
385
403
  return path.join(os.homedir(), '.manyoyo', 'plugin', 'playwright', 'extensions');
386
404
  }
@@ -600,7 +618,7 @@ class PlaywrightPlugin {
600
618
  }
601
619
 
602
620
  async waitForPort(port) {
603
- for (let i = 0; i < 30; i += 1) {
621
+ for (let i = 0; i < 60; i += 1) {
604
622
  // eslint-disable-next-line no-await-in-loop
605
623
  if (await this.portReady(port)) {
606
624
  return true;
@@ -626,13 +644,14 @@ class PlaywrightPlugin {
626
644
 
627
645
  if (sceneName === 'cont-headed') {
628
646
  const envKey = this.config.vncPasswordEnvKey;
629
- const password = process.env[envKey];
630
- if (!password && requireVncPassword) {
631
- 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
+ }
632
653
  }
633
- // podman-compose resolves ${VNC_PASSWORD:?..} even for `down`.
634
- // Keep `up` strict, but use a non-empty placeholder for non-up actions.
635
- env.VNC_PASSWORD = password || '__MANYOYO_PLACEHOLDER__';
654
+ env.VNC_PASSWORD = password;
636
655
  }
637
656
 
638
657
  return env;
@@ -813,8 +832,8 @@ class PlaywrightPlugin {
813
832
  return cp.returncode === 0 ? 0 : 1;
814
833
  }
815
834
 
816
- spawnHostProcess(cfgPath, logFd) {
817
- return spawn('npx', [`@playwright/mcp@${this.config.npmVersion}`, '--config', String(cfgPath)], {
835
+ spawnHostProcess(mcpBinPath, cfgPath, logFd) {
836
+ return spawn(mcpBinPath, ['--config', String(cfgPath)], {
818
837
  detached: true,
819
838
  stdio: ['ignore', logFd, logFd]
820
839
  });
@@ -872,14 +891,10 @@ class PlaywrightPlugin {
872
891
  }
873
892
 
874
893
  async startHost(sceneName, options = {}) {
875
- if (!this.ensureCommandAvailable('npx')) {
876
- this.writeStderr(`[up] ${sceneName} failed: npx command not found.`);
877
- return 1;
878
- }
879
-
880
894
  try {
881
895
  this.ensureHostScenePrerequisites(sceneName);
882
896
  } catch (error) {
897
+ this.writeStderr(`[up] ${sceneName} failed: ${error.message || String(error)}`);
883
898
  return error.returncode || 1;
884
899
  }
885
900
 
@@ -903,7 +918,16 @@ class PlaywrightPlugin {
903
918
 
904
919
  fs.rmSync(pidFile, { force: true });
905
920
  const logFd = fs.openSync(logFile, 'a');
906
- 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);
907
931
  fs.closeSync(logFd);
908
932
  if (typeof starter.unref === 'function') {
909
933
  starter.unref();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xcanwin/manyoyo",
3
- "version": "5.1.6",
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": {