@xcanwin/manyoyo 5.1.6 → 5.1.9

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,8 +236,7 @@ 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
- containerRuntime: 'podman',
239
+ containerRuntime: '',
240
240
  vncPasswordEnvKey: 'VNC_PASSWORD',
241
241
  headedImage: 'localhost/xcanwin/manyoyo-playwright-headed',
242
242
  configDir: path.join(pluginRootDir, 'config'),
@@ -272,6 +272,7 @@ class PlaywrightPlugin {
272
272
  this.runConfig.enabledScenes,
273
273
  asStringArray(this.globalConfig.enabledScenes, [...defaultConfig.enabledScenes])
274
274
  );
275
+ merged.containerRuntime = this.resolveContainerRuntime(merged.containerRuntime);
275
276
 
276
277
  if (merged.enabledScenes.length === 0) {
277
278
  throw new Error('playwright.enabledScenes 不能为空');
@@ -285,6 +286,22 @@ class PlaywrightPlugin {
285
286
  return merged;
286
287
  }
287
288
 
289
+ resolveContainerRuntime(configuredRuntime) {
290
+ const configured = String(configuredRuntime || '').trim().toLowerCase();
291
+ if (configured) {
292
+ return configured;
293
+ }
294
+
295
+ const candidates = ['docker', 'podman'];
296
+ for (const cmd of candidates) {
297
+ if (this.ensureCommandAvailable(cmd)) {
298
+ return cmd;
299
+ }
300
+ }
301
+
302
+ return 'docker';
303
+ }
304
+
288
305
  writeStdout(line = '') {
289
306
  this.stdout.write(`${line}\n`);
290
307
  }
@@ -293,6 +310,15 @@ class PlaywrightPlugin {
293
310
  this.stderr.write(`${line}\n`);
294
311
  }
295
312
 
313
+ randomAlnum(length = 16) {
314
+ const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
315
+ let out = '';
316
+ for (let i = 0; i < length; i += 1) {
317
+ out += chars[crypto.randomInt(0, chars.length)];
318
+ }
319
+ return out;
320
+ }
321
+
296
322
  runCmd(args, { env = null, captureOutput = false, check = true } = {}) {
297
323
  const result = spawnSync(args[0], args.slice(1), {
298
324
  encoding: 'utf8',
@@ -370,7 +396,7 @@ class PlaywrightPlugin {
370
396
  if (!this.sceneConfigMissing(sceneName)) {
371
397
  return;
372
398
  }
373
- this.runCmd(['npx', '-y', 'playwright-core', 'install', '--with-deps', this.defaultBrowserName(sceneName)], { check: true });
399
+ this.runCmd([this.localBinPath('playwright'), 'install', '--with-deps', this.defaultBrowserName(sceneName)], { check: true });
374
400
  }
375
401
 
376
402
  scenePidFile(sceneName) {
@@ -381,6 +407,15 @@ class PlaywrightPlugin {
381
407
  return path.join(this.config.runDir, `${sceneName}.log`);
382
408
  }
383
409
 
410
+ localBinPath(binName) {
411
+ const filename = process.platform === 'win32' ? `${binName}.cmd` : binName;
412
+ const binPath = path.join(this.projectRoot, 'node_modules', '.bin', filename);
413
+ if (!fs.existsSync(binPath)) {
414
+ throw new Error(`local binary not found: ${binPath}. Run npm install first.`);
415
+ }
416
+ return binPath;
417
+ }
418
+
384
419
  extensionDirPath() {
385
420
  return path.join(os.homedir(), '.manyoyo', 'plugin', 'playwright', 'extensions');
386
421
  }
@@ -600,7 +635,7 @@ class PlaywrightPlugin {
600
635
  }
601
636
 
602
637
  async waitForPort(port) {
603
- for (let i = 0; i < 30; i += 1) {
638
+ for (let i = 0; i < 60; i += 1) {
604
639
  // eslint-disable-next-line no-await-in-loop
605
640
  if (await this.portReady(port)) {
606
641
  return true;
@@ -626,13 +661,14 @@ class PlaywrightPlugin {
626
661
 
627
662
  if (sceneName === 'cont-headed') {
628
663
  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`);
664
+ let password = process.env[envKey];
665
+ if (!password) {
666
+ password = this.randomAlnum(16);
667
+ if (requireVncPassword) {
668
+ this.writeStdout(`[up] cont-headed ${envKey} not set; generated random 16-char password: ${password}`);
669
+ }
632
670
  }
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__';
671
+ env.VNC_PASSWORD = password;
636
672
  }
637
673
 
638
674
  return env;
@@ -813,8 +849,8 @@ class PlaywrightPlugin {
813
849
  return cp.returncode === 0 ? 0 : 1;
814
850
  }
815
851
 
816
- spawnHostProcess(cfgPath, logFd) {
817
- return spawn('npx', [`@playwright/mcp@${this.config.npmVersion}`, '--config', String(cfgPath)], {
852
+ spawnHostProcess(mcpBinPath, cfgPath, logFd) {
853
+ return spawn(mcpBinPath, ['--config', String(cfgPath)], {
818
854
  detached: true,
819
855
  stdio: ['ignore', logFd, logFd]
820
856
  });
@@ -872,14 +908,10 @@ class PlaywrightPlugin {
872
908
  }
873
909
 
874
910
  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
911
  try {
881
912
  this.ensureHostScenePrerequisites(sceneName);
882
913
  } catch (error) {
914
+ this.writeStderr(`[up] ${sceneName} failed: ${error.message || String(error)}`);
883
915
  return error.returncode || 1;
884
916
  }
885
917
 
@@ -903,7 +935,16 @@ class PlaywrightPlugin {
903
935
 
904
936
  fs.rmSync(pidFile, { force: true });
905
937
  const logFd = fs.openSync(logFile, 'a');
906
- const starter = this.spawnHostProcess(cfgPath, logFd);
938
+ let mcpBinPath = '';
939
+ try {
940
+ mcpBinPath = this.localBinPath('playwright-mcp');
941
+ } catch (error) {
942
+ fs.closeSync(logFd);
943
+ this.writeStderr(`[up] ${sceneName} failed: ${error.message || String(error)}`);
944
+ return 1;
945
+ }
946
+
947
+ const starter = this.spawnHostProcess(mcpBinPath, cfgPath, logFd);
907
948
  fs.closeSync(logFd);
908
949
  if (typeof starter.unref === 'function') {
909
950
  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.9",
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": {