@xcanwin/manyoyo 5.11.9 → 5.11.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.
@@ -31,7 +31,9 @@ function createPlugin(pluginName, options = {}) {
31
31
  stdout: options.stdout,
32
32
  stderr: options.stderr,
33
33
  globalConfig: cfg.globalPluginConfig,
34
- runConfig: cfg.runPluginConfig
34
+ runConfig: cfg.runPluginConfig,
35
+ rootGlobalConfig: asObject(options.globalConfig),
36
+ rootRunConfig: asObject(options.runConfig)
35
37
  });
36
38
  }
37
39
 
@@ -312,6 +312,8 @@ class PlaywrightPlugin {
312
312
  this.stderr = options.stderr || process.stderr;
313
313
  this.globalConfig = asObject(options.globalConfig);
314
314
  this.runConfig = asObject(options.runConfig);
315
+ this.rootGlobalConfig = asObject(options.rootGlobalConfig);
316
+ this.rootRunConfig = asObject(options.rootRunConfig);
315
317
  this.config = this.resolveConfig();
316
318
  }
317
319
 
@@ -421,7 +423,7 @@ class PlaywrightPlugin {
421
423
  if (sceneName !== 'cli-host-headed') {
422
424
  return;
423
425
  }
424
- if (this.config.cliSessionScene === sceneName) {
426
+ if (this.cliHostContainerAttachConfigReady()) {
425
427
  return;
426
428
  }
427
429
  this.writeStdout('[tip] 如果希望容器内 manyoyo run 自动附着到当前 CLI 宿主场景,请在 ~/.manyoyo/manyoyo.json 中设置:');
@@ -437,6 +439,55 @@ class PlaywrightPlugin {
437
439
  this.writeStdout('}');
438
440
  }
439
441
 
442
+ remoteDebuggingPageUrl() {
443
+ return 'chrome://inspect/#remote-debugging';
444
+ }
445
+
446
+ openChromeRemoteDebuggingPage() {
447
+ const url = this.remoteDebuggingPageUrl();
448
+ const platform = process.platform;
449
+ let commands = [];
450
+
451
+ if (platform === 'darwin') {
452
+ commands = [
453
+ ['open', '-a', 'Google Chrome', url],
454
+ ['open', url]
455
+ ];
456
+ } else if (platform === 'win32') {
457
+ commands = [
458
+ ['cmd', '/c', 'start', '', 'chrome', url]
459
+ ];
460
+ } else {
461
+ commands = [
462
+ ['google-chrome', url],
463
+ ['google-chrome-stable', url],
464
+ ['chromium', url],
465
+ ['chromium-browser', url]
466
+ ];
467
+ }
468
+
469
+ for (const args of commands) {
470
+ const result = this.runCmd(args, { check: false, captureOutput: true });
471
+ if (result.returncode === 0) {
472
+ return true;
473
+ }
474
+ }
475
+ return false;
476
+ }
477
+
478
+ writeDevHostUsage(configPath) {
479
+ this.writeStdout('');
480
+ this.writeStdout('若用容器 Agent 操作宿主机浏览器,在宿主机执行:');
481
+ ['claude', 'codex', 'gemini'].forEach(runName => {
482
+ this.writeStdout(`my run -r ${runName}`);
483
+ });
484
+ this.writeStdout('');
485
+ this.writeStdout('若用宿主机 Agent 操作宿主机浏览器,在宿主机执行:');
486
+ ['claude', 'codex', 'gemini'].forEach(command => {
487
+ this.writeStdout(this.devHostAttachCommand(configPath, command));
488
+ });
489
+ }
490
+
440
491
  randomAlnum(length = 16) {
441
492
  const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
442
493
  let out = '';
@@ -542,6 +593,109 @@ class PlaywrightPlugin {
542
593
  fs.rmSync(this.sceneCliAttachConfigPath(sceneName), { force: true });
543
594
  }
544
595
 
596
+ buildDevHostAttachConfig(endpoint, cdpHost) {
597
+ return {
598
+ outputDir: '/tmp/.playwright-cli',
599
+ browser: {
600
+ cdpEndpoint: `ws://${cdpHost}:${endpoint.port}${endpoint.wsPath}`,
601
+ cdpHeaders: {
602
+ Host: `127.0.0.1:${endpoint.port}`
603
+ },
604
+ cdpTimeout: this.config.devtoolsCdpTimeout
605
+ }
606
+ };
607
+ }
608
+
609
+ writeDevHostAttachConfig(sceneName, endpoint) {
610
+ fs.mkdirSync(this.config.configDir, { recursive: true });
611
+ const filePath = this.sceneConfigPath(sceneName);
612
+ fs.writeFileSync(filePath, `${JSON.stringify(this.buildDevHostAttachConfig(endpoint, '127.0.0.1'), null, 4)}\n`, 'utf8');
613
+ return filePath;
614
+ }
615
+
616
+ removeDevHostAttachConfig(sceneName) {
617
+ fs.rmSync(this.sceneConfigPath(sceneName), { force: true });
618
+ }
619
+
620
+ userDisplayPath(filePath) {
621
+ const normalizedPath = path.resolve(filePath);
622
+ const normalizedHome = path.resolve(this.config.homeDir);
623
+ if (normalizedPath === normalizedHome) {
624
+ return '~';
625
+ }
626
+ if (normalizedPath.startsWith(`${normalizedHome}${path.sep}`)) {
627
+ return `~${normalizedPath.slice(normalizedHome.length)}`;
628
+ }
629
+ return filePath;
630
+ }
631
+
632
+ shellValue(value) {
633
+ const text = String(value);
634
+ if (/^[A-Za-z0-9_@%+=:,./~-]+$/.test(text)) {
635
+ return text;
636
+ }
637
+ return `'${text.replace(/'/g, "'\\''")}'`;
638
+ }
639
+
640
+ devHostAttachEnvEntries(configPath) {
641
+ return [
642
+ `PLAYWRIGHT_MCP_CONFIG=${this.shellValue(this.userDisplayPath(configPath))}`
643
+ ];
644
+ }
645
+
646
+ devHostAttachCommand(configPath, command) {
647
+ return `${this.devHostAttachEnvEntries(configPath).join(' ')} ${command}`;
648
+ }
649
+
650
+ expandHomePath(value) {
651
+ const text = String(value || '').trim();
652
+ if (text === '~') {
653
+ return this.config.homeDir;
654
+ }
655
+ if (text.startsWith('~/')) {
656
+ return path.join(this.config.homeDir, text.slice(2));
657
+ }
658
+ return text;
659
+ }
660
+
661
+ parseVolumeEntry(value) {
662
+ const parts = String(value || '').split(':');
663
+ if (parts.length < 2) {
664
+ return null;
665
+ }
666
+ return {
667
+ hostPath: path.resolve(this.expandHomePath(parts[0])),
668
+ containerPath: parts[1]
669
+ };
670
+ }
671
+
672
+ mergedRootVolumes() {
673
+ return [
674
+ ...asStringArray(this.rootGlobalConfig.volumes, []),
675
+ ...asStringArray(this.rootRunConfig.volumes, []),
676
+ ...asStringArray(this.globalConfig.volumes, []),
677
+ ...asStringArray(this.runConfig.volumes, [])
678
+ ];
679
+ }
680
+
681
+ hasRootVolume(required) {
682
+ const parsedRequired = this.parseVolumeEntry(required);
683
+ if (!parsedRequired) {
684
+ return false;
685
+ }
686
+ return this.mergedRootVolumes().some(item => {
687
+ const parsed = this.parseVolumeEntry(item);
688
+ return parsed
689
+ && parsed.hostPath === parsedRequired.hostPath
690
+ && parsed.containerPath === parsedRequired.containerPath;
691
+ });
692
+ }
693
+
694
+ cliHostContainerAttachConfigReady() {
695
+ return this.config.cliSessionScene === 'cli-host-headed'
696
+ && this.hasRootVolume('~/.manyoyo/.cache/ms-playwright:/root/.cache/ms-playwright');
697
+ }
698
+
545
699
  devtoolsActivePortCandidates() {
546
700
  const homeDir = this.config.homeDir;
547
701
  const configured = this.config.devtoolsActivePortPath;
@@ -1116,24 +1270,13 @@ class PlaywrightPlugin {
1116
1270
  const endpoint = this.readDevToolsActivePort();
1117
1271
  const normalizedDockerCmd = String(dockerCmd || '').trim().toLowerCase();
1118
1272
  const connectHost = normalizedDockerCmd === 'podman' ? 'host.containers.internal' : 'host.docker.internal';
1119
- const cdpEndpoint = `ws://${connectHost}:${endpoint.port}${endpoint.wsPath}`;
1120
1273
  const hostConfigPath = this.sceneCliAttachConfigPath(sceneName);
1121
1274
  const containerConfigPath = `/tmp/manyoyo-playwright/${sceneName}.cli-attach.json`;
1122
1275
 
1123
- this.writeSceneCliAttachConfig(sceneName, {
1124
- outputDir: '/tmp/.playwright-cli',
1125
- browser: {
1126
- cdpEndpoint,
1127
- cdpHeaders: {
1128
- Host: `127.0.0.1:${endpoint.port}`
1129
- },
1130
- cdpTimeout: this.config.devtoolsCdpTimeout
1131
- }
1132
- });
1276
+ this.writeSceneCliAttachConfig(sceneName, this.buildDevHostAttachConfig(endpoint, connectHost));
1133
1277
 
1134
1278
  const envEntries = [
1135
- `PLAYWRIGHT_MCP_CONFIG=${containerConfigPath}`,
1136
- 'PW_CHROMIUM_ATTACH_TO_OTHER=1'
1279
+ `PLAYWRIGHT_MCP_CONFIG=${containerConfigPath}`
1137
1280
  ];
1138
1281
  const extraArgs = normalizedDockerCmd === 'docker'
1139
1282
  ? ['--add-host', 'host.docker.internal:host-gateway']
@@ -1447,10 +1590,17 @@ class PlaywrightPlugin {
1447
1590
  return 1;
1448
1591
  }
1449
1592
 
1450
- startDevHost(sceneName) {
1593
+ async startDevHost(sceneName) {
1594
+ this.openChromeRemoteDebuggingPage();
1451
1595
  try {
1452
1596
  const endpoint = this.readDevToolsActivePort();
1597
+ if (!(await this.portReady(endpoint.port))) {
1598
+ throw new Error(`DevToolsActivePort 指向 127.0.0.1:${endpoint.port},但该端口不可连接;请确认 Chrome remote debugging 已启用,或重新打开 Chrome 后再试。`);
1599
+ }
1600
+ const cfgPath = this.writeDevHostAttachConfig(sceneName, endpoint);
1453
1601
  this.writeStdout(`[up] ${sceneName} ready via DevToolsActivePort (${endpoint.filePath}, 127.0.0.1:${endpoint.port})`);
1602
+ this.writeStdout(`[tip] 已尝试打开 ${this.remoteDebuggingPageUrl()},请确认 remote debugging 已启用。`);
1603
+ this.writeDevHostUsage(cfgPath);
1454
1604
  return 0;
1455
1605
  } catch (error) {
1456
1606
  this.writeStderr(`[up] ${sceneName} failed: ${error.message || String(error)}`);
@@ -1501,6 +1651,7 @@ class PlaywrightPlugin {
1501
1651
  stopDevHost(sceneName) {
1502
1652
  this.removeSceneEndpoint(sceneName);
1503
1653
  this.removeSceneCliAttachConfig(sceneName);
1654
+ this.removeDevHostAttachConfig(sceneName);
1504
1655
  this.writeStdout(`[down] ${sceneName} no managed process`);
1505
1656
  return 0;
1506
1657
  }
@@ -1736,6 +1887,7 @@ class PlaywrightPlugin {
1736
1887
  }
1737
1888
 
1738
1889
  const scenes = this.resolveTargets('all').filter(sceneName => isMcpScene(sceneName));
1890
+ this.writeStdout('# 在容器中执行');
1739
1891
  for (const sceneName of scenes) {
1740
1892
  const url = `http://${host}:${this.scenePort(sceneName)}/mcp`;
1741
1893
  this.writeStdout(`claude mcp add -t http -s user playwright-${sceneName} ${url}`);
@@ -1756,6 +1908,7 @@ class PlaywrightPlugin {
1756
1908
 
1757
1909
  printCliAdd() {
1758
1910
  const lines = [
1911
+ '# 在宿主机中执行',
1759
1912
  'PLAYWRIGHT_CLI_INSTALL_DIR="${TMPDIR:-/tmp}/manyoyo-playwright-cli-install-$$"',
1760
1913
  'mkdir -p "$PLAYWRIGHT_CLI_INSTALL_DIR/.playwright"',
1761
1914
  'cd "$PLAYWRIGHT_CLI_INSTALL_DIR"',
@@ -1783,7 +1936,7 @@ class PlaywrightPlugin {
1783
1936
  const def = SCENE_DEFS[sceneName];
1784
1937
  if (isDevScene(sceneName)) {
1785
1938
  if (action === 'up') {
1786
- return this.startDevHost(sceneName);
1939
+ return await this.startDevHost(sceneName);
1787
1940
  }
1788
1941
  if (action === 'down') {
1789
1942
  return this.stopDevHost(sceneName);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xcanwin/manyoyo",
3
- "version": "5.11.9",
3
+ "version": "5.11.11",
4
4
  "imageVersion": "1.9.1-common",
5
5
  "playwrightCliVersion": "0.1.14",
6
6
  "description": "AI Agent CLI Security Sandbox for Docker and Podman",