@xcanwin/manyoyo 8.3.0 → 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.
@@ -0,0 +1,88 @@
1
+ 'use strict';
2
+
3
+ // Chrome 中继:把容器里的 CDP 连接转发给用户正在使用的 Chrome。
4
+ // 只放行带 token 路径的 WebSocket upgrade;每次新连接都重新读取 DevToolsActivePort,
5
+ // 所以 Chrome 重启(端口与路径变化)后不用重建容器;转发时改写 Host 头以通过 Chrome 的 Host 检查。
6
+
7
+ const fs = require('fs');
8
+ const http = require('http');
9
+ const crypto = require('crypto');
10
+ const { WebSocket, WebSocketServer } = require('ws');
11
+
12
+ function parseDevToolsActivePort(content) {
13
+ const lines = String(content || '').split(/\r?\n/).map(line => line.trim()).filter(Boolean);
14
+ const port = Number(lines[0]);
15
+ const wsPath = lines[1] || '';
16
+ if (!Number.isInteger(port) || port <= 0 || port > 65535 || !wsPath.startsWith('/devtools/browser/')) {
17
+ return null;
18
+ }
19
+ return { port, wsPath };
20
+ }
21
+
22
+ function findDevToolsActivePort(candidates) {
23
+ for (const filePath of candidates) {
24
+ try {
25
+ const parsed = parseDevToolsActivePort(fs.readFileSync(filePath, 'utf8'));
26
+ if (parsed) {
27
+ return { ...parsed, filePath };
28
+ }
29
+ } catch {
30
+ // 候选路径不存在,继续找下一个
31
+ }
32
+ }
33
+ return null;
34
+ }
35
+
36
+ function tokenMatches(actual, expected) {
37
+ const a = Buffer.from(String(actual));
38
+ const b = Buffer.from(String(expected));
39
+ return a.length === b.length && crypto.timingSafeEqual(a, b);
40
+ }
41
+
42
+ function createRelay({ token, candidates }) {
43
+ const wss = new WebSocketServer({ noServer: true });
44
+ const server = http.createServer((req, res) => {
45
+ res.writeHead(404);
46
+ res.end();
47
+ });
48
+
49
+ server.on('upgrade', (req, socket, head) => {
50
+ const pathname = String(req.url || '').split('?')[0];
51
+ if (!tokenMatches(pathname, `/${token}`)) {
52
+ socket.end('HTTP/1.1 404 Not Found\r\nConnection: close\r\n\r\n');
53
+ return;
54
+ }
55
+ const target = findDevToolsActivePort(candidates);
56
+ if (!target) {
57
+ socket.end('HTTP/1.1 502 Bad Gateway\r\nConnection: close\r\n\r\n');
58
+ return;
59
+ }
60
+ const upstream = new WebSocket(`ws://127.0.0.1:${target.port}${target.wsPath}`, {
61
+ headers: { Host: `127.0.0.1:${target.port}` },
62
+ perMessageDeflate: false
63
+ });
64
+ upstream.once('error', () => socket.destroy());
65
+ upstream.once('open', () => {
66
+ wss.handleUpgrade(req, socket, head, client => {
67
+ client.on('message', (data, isBinary) => upstream.send(data, { binary: isBinary }));
68
+ upstream.on('message', (data, isBinary) => client.send(data, { binary: isBinary }));
69
+ client.on('close', () => upstream.close());
70
+ upstream.on('close', () => client.close());
71
+ client.on('error', () => upstream.terminate());
72
+ upstream.on('error', () => client.terminate());
73
+ });
74
+ });
75
+ });
76
+
77
+ return server;
78
+ }
79
+
80
+ if (require.main === module) {
81
+ // 配置只经文件传入(含 token),不进命令行与环境变量
82
+ const config = JSON.parse(fs.readFileSync(process.argv[2], 'utf8'));
83
+ const server = createRelay(config);
84
+ server.listen(config.port, config.host);
85
+ process.on('SIGTERM', () => server.close(() => process.exit(0)));
86
+ }
87
+
88
+ module.exports = { createRelay, findDevToolsActivePort, parseDevToolsActivePort };
@@ -0,0 +1,23 @@
1
+ 'use strict';
2
+
3
+ // 宿主机(或 vnc 容器内)的 Playwright 浏览器服务:用 @playwright/cli 自带的 playwright-core 启动有头浏览器并开放 WebSocket。
4
+ // 用法:node playwright-server.js <playwright-core 目录> <配置文件>,配置为 chromium.launchServer 的选项。
5
+
6
+ const fs = require('fs');
7
+
8
+ async function main() {
9
+ const [corePath, configPath] = process.argv.slice(2);
10
+ const options = JSON.parse(fs.readFileSync(configPath, 'utf8'));
11
+ const { chromium } = require(corePath);
12
+ const server = await chromium.launchServer(options);
13
+ const shutdown = () => server.close().finally(() => process.exit(0));
14
+ process.on('SIGTERM', shutdown);
15
+ process.on('SIGINT', shutdown);
16
+ server.process().on('exit', () => process.exit(1));
17
+ console.log('ready');
18
+ }
19
+
20
+ main().catch(error => {
21
+ console.error(error && error.stack ? error.stack : String(error));
22
+ process.exit(1);
23
+ });