@xcanwin/manyoyo 5.1.5 → 5.1.8
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 +73 -20
- package/package.json +3 -1
package/lib/plugin/playwright.js
CHANGED
|
@@ -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 = [
|
|
@@ -56,6 +57,14 @@ const SCENE_DEFS = {
|
|
|
56
57
|
const VALID_RUNTIME = new Set(['container', 'host', 'mixed']);
|
|
57
58
|
const VALID_ACTIONS = new Set(['up', 'down', 'status', 'health', 'logs']);
|
|
58
59
|
const CONTAINER_EXTENSION_ROOT = '/app/extensions';
|
|
60
|
+
const DEFAULT_FINGERPRINT_PROFILE = {
|
|
61
|
+
userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36',
|
|
62
|
+
locale: 'zh-CN',
|
|
63
|
+
acceptLanguage: 'zh-CN,zh;q=0.9',
|
|
64
|
+
timezoneId: 'Asia/Shanghai',
|
|
65
|
+
width: 1366,
|
|
66
|
+
height: 768
|
|
67
|
+
};
|
|
59
68
|
|
|
60
69
|
function sleep(ms) {
|
|
61
70
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
@@ -227,7 +236,6 @@ class PlaywrightPlugin {
|
|
|
227
236
|
hostListen: '127.0.0.1',
|
|
228
237
|
mcpDefaultHost: 'host.docker.internal',
|
|
229
238
|
dockerTag: process.env.PLAYWRIGHT_MCP_DOCKER_TAG || 'latest',
|
|
230
|
-
npmVersion: process.env.PLAYWRIGHT_MCP_NPM_VERSION || 'latest',
|
|
231
239
|
containerRuntime: 'podman',
|
|
232
240
|
vncPasswordEnvKey: 'VNC_PASSWORD',
|
|
233
241
|
headedImage: 'localhost/xcanwin/manyoyo-playwright-headed',
|
|
@@ -285,6 +293,15 @@ class PlaywrightPlugin {
|
|
|
285
293
|
this.stderr.write(`${line}\n`);
|
|
286
294
|
}
|
|
287
295
|
|
|
296
|
+
randomAlnum(length = 16) {
|
|
297
|
+
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
298
|
+
let out = '';
|
|
299
|
+
for (let i = 0; i < length; i += 1) {
|
|
300
|
+
out += chars[crypto.randomInt(0, chars.length)];
|
|
301
|
+
}
|
|
302
|
+
return out;
|
|
303
|
+
}
|
|
304
|
+
|
|
288
305
|
runCmd(args, { env = null, captureOutput = false, check = true } = {}) {
|
|
289
306
|
const result = spawnSync(args[0], args.slice(1), {
|
|
290
307
|
encoding: 'utf8',
|
|
@@ -362,7 +379,7 @@ class PlaywrightPlugin {
|
|
|
362
379
|
if (!this.sceneConfigMissing(sceneName)) {
|
|
363
380
|
return;
|
|
364
381
|
}
|
|
365
|
-
this.runCmd(['
|
|
382
|
+
this.runCmd([this.localBinPath('playwright'), 'install', '--with-deps', this.defaultBrowserName(sceneName)], { check: true });
|
|
366
383
|
}
|
|
367
384
|
|
|
368
385
|
scenePidFile(sceneName) {
|
|
@@ -373,6 +390,15 @@ class PlaywrightPlugin {
|
|
|
373
390
|
return path.join(this.config.runDir, `${sceneName}.log`);
|
|
374
391
|
}
|
|
375
392
|
|
|
393
|
+
localBinPath(binName) {
|
|
394
|
+
const filename = process.platform === 'win32' ? `${binName}.cmd` : binName;
|
|
395
|
+
const binPath = path.join(this.projectRoot, 'node_modules', '.bin', filename);
|
|
396
|
+
if (!fs.existsSync(binPath)) {
|
|
397
|
+
throw new Error(`local binary not found: ${binPath}. Run npm install first.`);
|
|
398
|
+
}
|
|
399
|
+
return binPath;
|
|
400
|
+
}
|
|
401
|
+
|
|
376
402
|
extensionDirPath() {
|
|
377
403
|
return path.join(os.homedir(), '.manyoyo', 'plugin', 'playwright', 'extensions');
|
|
378
404
|
}
|
|
@@ -510,13 +536,21 @@ class PlaywrightPlugin {
|
|
|
510
536
|
const def = SCENE_DEFS[sceneName];
|
|
511
537
|
const port = this.scenePort(sceneName);
|
|
512
538
|
const extensionPaths = asStringArray(options.extensionPaths, []);
|
|
539
|
+
const baseLaunchArgs = [
|
|
540
|
+
`--user-agent=${DEFAULT_FINGERPRINT_PROFILE.userAgent}`,
|
|
541
|
+
`--lang=${DEFAULT_FINGERPRINT_PROFILE.locale}`,
|
|
542
|
+
`--window-size=${DEFAULT_FINGERPRINT_PROFILE.width},${DEFAULT_FINGERPRINT_PROFILE.height}`,
|
|
543
|
+
'--disable-blink-features=AutomationControlled',
|
|
544
|
+
'--force-webrtc-ip-handling-policy=disable_non_proxied_udp'
|
|
545
|
+
];
|
|
513
546
|
const launchOptions = {
|
|
514
547
|
channel: 'chromium',
|
|
515
|
-
headless: def.headless
|
|
548
|
+
headless: def.headless,
|
|
549
|
+
args: [...baseLaunchArgs]
|
|
516
550
|
};
|
|
517
551
|
|
|
518
552
|
if (extensionPaths.length > 0) {
|
|
519
|
-
launchOptions.args
|
|
553
|
+
launchOptions.args.push(...this.buildExtensionLaunchArgs(extensionPaths));
|
|
520
554
|
}
|
|
521
555
|
|
|
522
556
|
return {
|
|
@@ -535,7 +569,20 @@ class PlaywrightPlugin {
|
|
|
535
569
|
browserName: 'chromium',
|
|
536
570
|
launchOptions,
|
|
537
571
|
contextOptions: {
|
|
538
|
-
userAgent:
|
|
572
|
+
userAgent: DEFAULT_FINGERPRINT_PROFILE.userAgent,
|
|
573
|
+
locale: DEFAULT_FINGERPRINT_PROFILE.locale,
|
|
574
|
+
timezoneId: DEFAULT_FINGERPRINT_PROFILE.timezoneId,
|
|
575
|
+
viewport: {
|
|
576
|
+
width: DEFAULT_FINGERPRINT_PROFILE.width,
|
|
577
|
+
height: DEFAULT_FINGERPRINT_PROFILE.height
|
|
578
|
+
},
|
|
579
|
+
screen: {
|
|
580
|
+
width: DEFAULT_FINGERPRINT_PROFILE.width,
|
|
581
|
+
height: DEFAULT_FINGERPRINT_PROFILE.height
|
|
582
|
+
},
|
|
583
|
+
extraHTTPHeaders: {
|
|
584
|
+
'Accept-Language': DEFAULT_FINGERPRINT_PROFILE.acceptLanguage
|
|
585
|
+
}
|
|
539
586
|
}
|
|
540
587
|
}
|
|
541
588
|
};
|
|
@@ -571,7 +618,7 @@ class PlaywrightPlugin {
|
|
|
571
618
|
}
|
|
572
619
|
|
|
573
620
|
async waitForPort(port) {
|
|
574
|
-
for (let i = 0; i <
|
|
621
|
+
for (let i = 0; i < 60; i += 1) {
|
|
575
622
|
// eslint-disable-next-line no-await-in-loop
|
|
576
623
|
if (await this.portReady(port)) {
|
|
577
624
|
return true;
|
|
@@ -597,13 +644,14 @@ class PlaywrightPlugin {
|
|
|
597
644
|
|
|
598
645
|
if (sceneName === 'cont-headed') {
|
|
599
646
|
const envKey = this.config.vncPasswordEnvKey;
|
|
600
|
-
|
|
601
|
-
if (!password
|
|
602
|
-
|
|
647
|
+
let password = process.env[envKey];
|
|
648
|
+
if (!password) {
|
|
649
|
+
password = this.randomAlnum(16);
|
|
650
|
+
if (requireVncPassword) {
|
|
651
|
+
this.writeStdout(`[up] cont-headed ${envKey} not set; generated random 16-char password: ${password}`);
|
|
652
|
+
}
|
|
603
653
|
}
|
|
604
|
-
|
|
605
|
-
// Keep `up` strict, but use a non-empty placeholder for non-up actions.
|
|
606
|
-
env.VNC_PASSWORD = password || '__MANYOYO_PLACEHOLDER__';
|
|
654
|
+
env.VNC_PASSWORD = password;
|
|
607
655
|
}
|
|
608
656
|
|
|
609
657
|
return env;
|
|
@@ -784,8 +832,8 @@ class PlaywrightPlugin {
|
|
|
784
832
|
return cp.returncode === 0 ? 0 : 1;
|
|
785
833
|
}
|
|
786
834
|
|
|
787
|
-
spawnHostProcess(cfgPath, logFd) {
|
|
788
|
-
return spawn(
|
|
835
|
+
spawnHostProcess(mcpBinPath, cfgPath, logFd) {
|
|
836
|
+
return spawn(mcpBinPath, ['--config', String(cfgPath)], {
|
|
789
837
|
detached: true,
|
|
790
838
|
stdio: ['ignore', logFd, logFd]
|
|
791
839
|
});
|
|
@@ -843,14 +891,10 @@ class PlaywrightPlugin {
|
|
|
843
891
|
}
|
|
844
892
|
|
|
845
893
|
async startHost(sceneName, options = {}) {
|
|
846
|
-
if (!this.ensureCommandAvailable('npx')) {
|
|
847
|
-
this.writeStderr(`[up] ${sceneName} failed: npx command not found.`);
|
|
848
|
-
return 1;
|
|
849
|
-
}
|
|
850
|
-
|
|
851
894
|
try {
|
|
852
895
|
this.ensureHostScenePrerequisites(sceneName);
|
|
853
896
|
} catch (error) {
|
|
897
|
+
this.writeStderr(`[up] ${sceneName} failed: ${error.message || String(error)}`);
|
|
854
898
|
return error.returncode || 1;
|
|
855
899
|
}
|
|
856
900
|
|
|
@@ -874,7 +918,16 @@ class PlaywrightPlugin {
|
|
|
874
918
|
|
|
875
919
|
fs.rmSync(pidFile, { force: true });
|
|
876
920
|
const logFd = fs.openSync(logFile, 'a');
|
|
877
|
-
|
|
921
|
+
let mcpBinPath = '';
|
|
922
|
+
try {
|
|
923
|
+
mcpBinPath = this.localBinPath('playwright-mcp');
|
|
924
|
+
} catch (error) {
|
|
925
|
+
fs.closeSync(logFd);
|
|
926
|
+
this.writeStderr(`[up] ${sceneName} failed: ${error.message || String(error)}`);
|
|
927
|
+
return 1;
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
const starter = this.spawnHostProcess(mcpBinPath, cfgPath, logFd);
|
|
878
931
|
fs.closeSync(logFd);
|
|
879
932
|
if (typeof starter.unref === 'function') {
|
|
880
933
|
starter.unref();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xcanwin/manyoyo",
|
|
3
|
-
"version": "5.1.
|
|
3
|
+
"version": "5.1.8",
|
|
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": {
|