@xcanwin/manyoyo 8.3.1 → 8.3.2
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.
- package/lib/plugin/playwright.js +41 -16
- package/lib/web/index.html +1 -1
- package/package.json +2 -1
package/lib/plugin/playwright.js
CHANGED
|
@@ -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
|
|
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'),
|
|
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
|
|
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:
|
|
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:
|
|
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
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
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
|
|
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
|
-
|
|
766
|
-
|
|
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
|
};
|