@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.
- package/lib/plugin/index.js +3 -1
- package/lib/plugin/playwright.js +169 -16
- package/package.json +1 -1
package/lib/plugin/index.js
CHANGED
|
@@ -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
|
|
package/lib/plugin/playwright.js
CHANGED
|
@@ -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.
|
|
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);
|