@xcanwin/manyoyo 8.3.1 → 8.3.3

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.
@@ -33,6 +33,8 @@ const DEFAULT_PORTS = { port: 8935, vncPort: 5900, novncPort: 6080 };
33
33
  const PROXY_BYPASS_HOSTS = ['host.docker.internal', 'host.containers.internal'];
34
34
  const SERVER_START_TIMEOUT_MS = 30000;
35
35
  const PROBE_TIMEOUT_MS = 5000;
36
+ // chrome 首次连接要等用户在 Chrome 弹出的“要允许远程调试吗?”里点允许
37
+ const CHROME_APPROVAL_TIMEOUT_MS = 60000;
36
38
 
37
39
  function asObject(value) {
38
40
  return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
@@ -104,6 +106,12 @@ function defaultListenHost(platform = process.platform) {
104
106
  return platform === 'darwin' ? '127.0.0.1' : '0.0.0.0';
105
107
  }
106
108
 
109
+ // headed 的浏览器服务必须监听 0.0.0.0:playwright 只绑 loopback 时会按 Host 头做 DNS 重绑定防护,
110
+ // 容器用 host.containers.internal 访问会被 403(macOS 上实测);监听 0.0.0.0 则不检查 Host,靠 token 保护
111
+ function headedListenHost() {
112
+ return '0.0.0.0';
113
+ }
114
+
107
115
  function runtimeKind(runtimeCommand) {
108
116
  return path.basename(String(runtimeCommand || '').trim()).toLowerCase() === 'podman' ? 'podman' : 'docker';
109
117
  }
@@ -410,17 +418,17 @@ class PlaywrightPlugin {
410
418
 
411
419
  // ---------- 探测 ----------
412
420
 
413
- probe(kind, endpoint) {
421
+ probe(kind, endpoint, timeout = PROBE_TIMEOUT_MS) {
414
422
  return new Promise(resolve => {
415
423
  const child = spawn(process.execPath, [path.join(__dirname, 'playwright-probe.js')], {
416
424
  env: {
417
425
  ...process.env,
418
- MANYOYO_PROBE: JSON.stringify({ core: this.corePath(), kind, endpoint, timeout: PROBE_TIMEOUT_MS })
426
+ MANYOYO_PROBE: JSON.stringify({ core: this.corePath(), kind, endpoint, timeout })
419
427
  },
420
428
  stdio: ['ignore', 'pipe', 'ignore']
421
429
  });
422
430
  let output = '';
423
- const timer = setTimeout(() => child.kill('SIGKILL'), PROBE_TIMEOUT_MS + 3000);
431
+ const timer = setTimeout(() => child.kill('SIGKILL'), timeout + 3000);
424
432
  child.stdout.on('data', chunk => { output += chunk; });
425
433
  child.on('error', () => {
426
434
  clearTimeout(timer);
@@ -439,7 +447,9 @@ class PlaywrightPlugin {
439
447
 
440
448
  async probeState(state) {
441
449
  const endpoint = `ws://127.0.0.1:${state.port}/${this.ensureToken()}`;
442
- return await this.probe(state.mode === 'chrome' ? 'cdp' : 'ws', endpoint);
450
+ return state.mode === 'chrome'
451
+ ? await this.probe('cdp', endpoint, CHROME_APPROVAL_TIMEOUT_MS)
452
+ : await this.probe('ws', endpoint);
443
453
  }
444
454
 
445
455
  // ---------- 进程管理 ----------
@@ -539,7 +549,7 @@ class PlaywrightPlugin {
539
549
  let child = null;
540
550
  for (const chromiumSandbox of [true, false]) {
541
551
  writeConfigFileSecure(serverConfigPath, `${JSON.stringify(fingerprint.buildServerConfig({
542
- host: defaultListenHost(),
552
+ host: headedListenHost(),
543
553
  port,
544
554
  wsPath: `/${token}`,
545
555
  chromiumSandbox,
@@ -559,20 +569,26 @@ class PlaywrightPlugin {
559
569
  }
560
570
  this.out('[up] 系统不允许非特权用户命名空间,Chromium 沙箱不可用,改为无沙箱启动。');
561
571
  }
562
- return { mode: 'headed', port, pid: child.pid, listenHost: defaultListenHost(), extensionPaths };
572
+ return { mode: 'headed', port, pid: child.pid, listenHost: headedListenHost(), extensionPaths };
563
573
  }
564
574
 
565
575
  async startChrome() {
566
576
  const { port } = this.config;
567
577
  const candidates = this.devtoolsCandidates();
568
- if (!findDevToolsActivePort(candidates)) {
569
- return this.startFailure('chrome', '没有找到正在运行且开启了远程调试的 Chrome。',
570
- '在 Chrome 地址栏打开 chrome://inspect/#remote-debugging 并开启后重试;\n'
571
- + '自定义用户目录时,在 plugins.playwright.devtoolsActivePortPath 指定 DevToolsActivePort 文件。');
578
+ const enableHint = '请在 Chrome 地址栏进入 chrome://inspect/#remote-debugging,勾选“Allow remote debugging”(Chrome 没开就先打开它),然后重试。\n'
579
+ + '(自定义用户目录时,用 plugins.playwright.devtoolsActivePortPath 指定 DevToolsActivePort 文件。)';
580
+ const target = findDevToolsActivePort(candidates);
581
+ if (!target) {
582
+ return this.startFailure('chrome', '没有找到开启了远程调试的 Chrome。', enableHint);
583
+ }
584
+ // Chrome 退出后会留下旧的 DevToolsActivePort,所以还要确认调试端口真的在监听
585
+ if (!(await portReady(target.port))) {
586
+ return this.startFailure('chrome', 'Chrome 没有在运行(只找到上次留下的调试端口记录)。', enableHint);
572
587
  }
573
588
  if (!(await this.portPreflight('chrome', port))) {
574
589
  return 1;
575
590
  }
591
+ fs.rmSync(this.logPath(), { force: true });
576
592
  const relayConfigPath = path.join(this.runDir, 'relay.json');
577
593
  writeConfigFileSecure(relayConfigPath, `${JSON.stringify({
578
594
  host: defaultListenHost(), port, token: this.ensureToken(), candidates
@@ -724,9 +740,9 @@ class PlaywrightPlugin {
724
740
  this.out(`原生 VNC: 127.0.0.1:${state.vncPort}(密码见 manyoyo playwright status)`);
725
741
  }
726
742
  if (state.mode === 'chrome') {
727
- this.out('⚠️ 风险: 容器内 Agent 能操作你 Chrome 里已登录的所有网站。Chrome 可能弹出“允许远程调试”确认框,请点允许。');
743
+ this.out('⚠️ 风险: 容器内 Agent 能操作你 Chrome 里已登录的所有网站。Chrome 每次新连接(每次 playwright-cli open、每次 status)都会弹出“要允许远程调试吗?”,请点“允许”。');
728
744
  }
729
- if (process.platform !== 'darwin' && state.mode !== 'default') {
745
+ if (state.mode !== 'default' && !(process.platform === 'darwin' && state.mode !== 'headed')) {
730
746
  this.out(`安全: 端口 ${state.port} 监听 0.0.0.0,仅靠 token 保护;建议用防火墙限制来源。`);
731
747
  }
732
748
  }
@@ -760,15 +776,23 @@ class PlaywrightPlugin {
760
776
  this.writeState(state);
761
777
  this.writeCurrentConfig(state, this.runtimeCommand());
762
778
 
779
+ if (mode === 'chrome') {
780
+ this.out('等待 Chrome 确认:请在 Chrome 弹出的“要允许远程调试吗?”里点“允许”(最多等 60 秒)…');
781
+ }
763
782
  const result = await this.probeState(state);
764
783
  if (!result.ok) {
765
- this.err(`[up] ${mode} 已启动,但真实探测失败: ${result.error}`);
766
- this.err(tailText(this.logPath(), 10));
784
+ if (mode === 'chrome') {
785
+ this.err('[up] chrome 失败: 连不上 Chrome 的调试接口。');
786
+ this.err('请确认已在 chrome://inspect/#remote-debugging 开启远程调试,并在 Chrome 弹出的“要允许远程调试吗?”里点“允许”(60 秒内),然后重试。');
787
+ } else {
788
+ this.err(`[up] ${mode} 失败: 浏览器服务已启动,但打不开页面(${result.error})。`);
789
+ this.err(tailText(this.logPath(), 10));
790
+ }
767
791
  // 回滚:不让坏掉的模式留在配置里
768
792
  await this.stopCurrent(state);
769
793
  this.writeState({ mode: 'default' });
770
794
  this.writeCurrentConfig({ mode: 'default' }, this.runtimeCommand());
771
- this.err('已回滚到默认模式。');
795
+ this.err('已回到默认模式。');
772
796
  return 1;
773
797
  }
774
798
  this.out(`✅ ${mode} 模式已就绪(真实探测通过,${result.ms}ms)`);
@@ -892,5 +916,6 @@ module.exports = {
892
916
  PlaywrightPlugin,
893
917
  appendNoProxy,
894
918
  connectHostFor,
895
- defaultListenHost
919
+ defaultListenHost,
920
+ headedListenHost
896
921
  };