@xcanwin/manyoyo 5.3.10 → 5.4.4

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/README.md CHANGED
@@ -53,7 +53,7 @@ AI Agent CLI 往往需要:
53
53
  ```bash
54
54
  npm install -g @xcanwin/manyoyo
55
55
  podman pull ubuntu:24.04 # 仅 Podman 需要
56
- manyoyo build --iv 1.8.4-common
56
+ manyoyo build --iv 1.8.8-common
57
57
  manyoyo init all
58
58
  manyoyo run -r claude
59
59
  ```
@@ -137,10 +137,10 @@ manyoyo config command
137
137
 
138
138
  ```bash
139
139
  # common 版本
140
- manyoyo build --iv 1.8.4-common
140
+ manyoyo build --iv 1.8.8-common
141
141
 
142
142
  # full 版本
143
- manyoyo build --iv 1.8.4-full
143
+ manyoyo build --iv 1.8.8-full
144
144
 
145
145
  # 自定义工具集
146
146
  manyoyo build --iba TOOL=go,codex,java,gemini
package/bin/manyoyo.js CHANGED
@@ -14,7 +14,7 @@ const { buildContainerRunArgs, buildContainerRunCommand } = require('../lib/cont
14
14
  const { initAgentConfigs } = require('../lib/init-config');
15
15
  const { buildImage } = require('../lib/image-build');
16
16
  const { resolveAgentResumeArg, buildAgentResumeCommand } = require('../lib/agent-resume');
17
- const { runPluginCommand } = require('../lib/plugin');
17
+ const { runPluginCommand, createPlugin } = require('../lib/plugin');
18
18
  const { buildManyoyoLogPath } = require('../lib/log-path');
19
19
  const {
20
20
  sanitizeSensitiveData,
@@ -71,6 +71,7 @@ let CONTAINER_ENVS = [];
71
71
  let FIRST_CONTAINER_ENVS = [];
72
72
  let CONTAINER_VOLUMES = [];
73
73
  let CONTAINER_PORTS = [];
74
+ let CONTAINER_EXTRA_ARGS = [];
74
75
  const MANYOYO_NAME = detectCommandName();
75
76
  let CONT_MODE_ARGS = [];
76
77
  let QUIET = {};
@@ -559,6 +560,63 @@ function addEnvFile(envFile) {
559
560
  return addEnvFileTo(CONTAINER_ENVS, envFile);
560
561
  }
561
562
 
563
+ function hasEnvKey(targetEnvs, key) {
564
+ for (let i = 0; i < targetEnvs.length; i += 2) {
565
+ if (targetEnvs[i] !== '--env') {
566
+ continue;
567
+ }
568
+ const text = String(targetEnvs[i + 1] || '');
569
+ const idx = text.indexOf('=');
570
+ if (idx > 0 && text.slice(0, idx) === key) {
571
+ return true;
572
+ }
573
+ }
574
+ return false;
575
+ }
576
+
577
+ function appendUniqueArgs(targetArgs, extraArgs) {
578
+ const joinedExisting = new Set();
579
+ for (let i = 0; i < targetArgs.length; i += 2) {
580
+ const head = String(targetArgs[i] || '');
581
+ const value = String(targetArgs[i + 1] || '');
582
+ if (head.startsWith('--')) {
583
+ joinedExisting.add(`${head}\u0000${value}`);
584
+ }
585
+ }
586
+
587
+ for (let i = 0; i < extraArgs.length; i += 2) {
588
+ const head = String(extraArgs[i] || '');
589
+ const value = String(extraArgs[i + 1] || '');
590
+ const signature = `${head}\u0000${value}`;
591
+ if (!joinedExisting.has(signature)) {
592
+ joinedExisting.add(signature);
593
+ targetArgs.push(head, value);
594
+ }
595
+ }
596
+ }
597
+
598
+ function applyPlaywrightCliSessionIntegration(config, runConfig) {
599
+ try {
600
+ const plugin = createPlugin('playwright', {
601
+ globalConfig: config,
602
+ runConfig,
603
+ projectRoot: path.join(__dirname, '..')
604
+ });
605
+ const integration = plugin.buildCliSessionIntegration(DOCKER_CMD);
606
+ for (const entry of integration.envEntries) {
607
+ const parsed = parseEnvEntry(entry);
608
+ if (!hasEnvKey(CONTAINER_ENVS, parsed.key)) {
609
+ addEnv(`${parsed.key}=${parsed.value}`);
610
+ }
611
+ }
612
+ appendUniqueArgs(CONTAINER_EXTRA_ARGS, integration.extraArgs);
613
+ appendUniqueArgs(CONTAINER_VOLUMES, integration.volumeEntries || []);
614
+ } catch (error) {
615
+ console.error(`${RED}⚠️ 错误: Playwright CLI 会话注入失败: ${error.message || String(error)}${NC}`);
616
+ process.exit(1);
617
+ }
618
+ }
619
+
562
620
  function addVolume(volume) {
563
621
  CONTAINER_VOLUMES.push("--volume", volume);
564
622
  }
@@ -954,7 +1012,7 @@ async function setupCommander() {
954
1012
  ...options,
955
1013
  pluginAction: params.action || 'ls',
956
1014
  pluginName: params.pluginName || 'playwright',
957
- pluginScene: params.scene || 'host-headless',
1015
+ pluginScene: params.scene || 'mcp-host-headless',
958
1016
  pluginHost: params.host || '',
959
1017
  pluginExtensionPaths: Array.isArray(params.extensionPaths) ? params.extensionPaths : [],
960
1018
  pluginExtensionNames: Array.isArray(params.extensionNames) ? params.extensionNames : [],
@@ -975,7 +1033,7 @@ async function setupCommander() {
975
1033
  const actions = ['up', 'down', 'status', 'health', 'logs'];
976
1034
  actions.forEach(action => {
977
1035
  const sceneCommand = command.command(`${action} [scene]`)
978
- .description(`执行 playwright ${action} 场景(scene 默认 host-headless)`)
1036
+ .description(`执行 playwright ${action} 场景(scene 默认 mcp-host-headless)`)
979
1037
  .option('-r, --run <name>', '加载运行配置 (从 ~/.manyoyo/manyoyo.json 的 runs.<name> 读取)');
980
1038
 
981
1039
  if (action === 'up') {
@@ -986,7 +1044,7 @@ async function setupCommander() {
986
1044
  sceneCommand.action((scene, options) => selectPluginAction({
987
1045
  action,
988
1046
  pluginName: 'playwright',
989
- scene: scene || 'host-headless',
1047
+ scene: scene || 'mcp-host-headless',
990
1048
  extensionPaths: action === 'up' ? (options.extPath || []) : [],
991
1049
  extensionNames: action === 'up' ? (options.extName || []) : []
992
1050
  }, options));
@@ -1041,8 +1099,8 @@ async function setupCommander() {
1041
1099
  ${MANYOYO_NAME} serve 127.0.0.1:3000 启动本机网页服务
1042
1100
  ${MANYOYO_NAME} serve 127.0.0.1:3000 -d 后台启动;未设密码时会打印本次随机密码
1043
1101
  ${MANYOYO_NAME} serve 0.0.0.0:3000 -U admin -P 123 -d 后台启动并监听全部网卡
1044
- ${MANYOYO_NAME} playwright up host-headless 启动 playwright 默认场景(推荐)
1045
- ${MANYOYO_NAME} plugin playwright up host-headless 通过 plugin 命名空间启动
1102
+ ${MANYOYO_NAME} playwright up mcp-host-headless 启动 playwright 默认场景(推荐)
1103
+ ${MANYOYO_NAME} plugin playwright up mcp-host-headless 通过 plugin 命名空间启动
1046
1104
  ${MANYOYO_NAME} run -n test -q tip -q cmd 多次使用静默选项
1047
1105
  `);
1048
1106
 
@@ -1208,7 +1266,7 @@ Notes:
1208
1266
  pluginRequest: {
1209
1267
  action: options.pluginAction,
1210
1268
  pluginName: options.pluginName,
1211
- scene: options.pluginScene || 'host-headless',
1269
+ scene: options.pluginScene || 'mcp-host-headless',
1212
1270
  host: options.pluginHost || '',
1213
1271
  extensionPaths: Array.isArray(options.pluginExtensionPaths) ? options.pluginExtensionPaths : [],
1214
1272
  extensionNames: Array.isArray(options.pluginExtensionNames) ? options.pluginExtensionNames : [],
@@ -1302,6 +1360,8 @@ Notes:
1302
1360
  };
1303
1361
  Object.entries(firstEnvMap).forEach(([key, value]) => addEnvTo(FIRST_CONTAINER_ENVS, `${key}=${value}`));
1304
1362
 
1363
+ applyPlaywrightCliSessionIntegration(config, runConfig);
1364
+
1305
1365
  const volumeList = mergeArrayConfig(config.volumes, runConfig.volumes, options.volume);
1306
1366
  volumeList.forEach(v => addVolume(v));
1307
1367
 
@@ -1438,6 +1498,7 @@ function createRuntimeContext(modeState = {}) {
1438
1498
  firstExecCommandPrefix: FIRST_EXEC_COMMAND_PREFIX,
1439
1499
  firstExecCommandSuffix: FIRST_EXEC_COMMAND_SUFFIX,
1440
1500
  contModeArgs: CONT_MODE_ARGS,
1501
+ containerExtraArgs: CONTAINER_EXTRA_ARGS,
1441
1502
  containerEnvs: CONTAINER_ENVS,
1442
1503
  firstContainerEnvs: FIRST_CONTAINER_ENVS,
1443
1504
  containerVolumes: CONTAINER_VOLUMES,
@@ -1662,6 +1723,7 @@ function buildDockerRunArgs(runtime) {
1662
1723
  imageName: runtime.imageName,
1663
1724
  imageVersion: runtime.imageVersion,
1664
1725
  contModeArgs: runtime.contModeArgs,
1726
+ containerExtraArgs: runtime.containerExtraArgs,
1665
1727
  containerEnvs: runtime.containerEnvs,
1666
1728
  containerVolumes: runtime.containerVolumes,
1667
1729
  containerPorts: runtime.containerPorts,
@@ -1826,6 +1888,7 @@ async function runWebServerMode(runtime) {
1826
1888
  execCommand: runtime.execCommand,
1827
1889
  execCommandSuffix: runtime.execCommandSuffix,
1828
1890
  contModeArgs: runtime.contModeArgs,
1891
+ containerExtraArgs: runtime.containerExtraArgs,
1829
1892
  containerEnvs: runtime.containerEnvs,
1830
1893
  containerVolumes: runtime.containerVolumes,
1831
1894
  containerPorts: runtime.containerPorts,
@@ -64,7 +64,7 @@ FROM ubuntu:24.04
64
64
 
65
65
  ARG TARGETARCH
66
66
  ARG NODE_VERSION=24
67
- ARG TOOL="full"
67
+ ARG TOOL="common"
68
68
 
69
69
  # 镜像源参数化(默认使用阿里云,可按需覆盖)
70
70
  ARG APT_MIRROR=https://mirrors.aliyun.com
@@ -74,9 +74,11 @@ ARG PIP_INDEX_URL=https://mirrors.tencent.com/pypi/simple
74
74
  ARG PY_TEXT_PIP_PACKAGES="PyYAML python-dotenv tomlkit pyjson5 jsonschema"
75
75
  ARG PY_TEXT_EXTRA_PIP_PACKAGES=""
76
76
  ENV LANG=C.UTF-8 \
77
- LC_ALL=C.UTF-8
77
+ LC_ALL=C.UTF-8 \
78
+ PIP_ROOT_USER_ACTION=ignore \
79
+ PLAYWRIGHT_MCP_CONFIG=/app/config/cli-cont-headless.json
78
80
 
79
- # 合并系统依赖安装为单层,减少镜像体积
81
+ # 合并系统依赖与 Python 安装为单层,减少镜像体积
80
82
  RUN <<EOX
81
83
  # 配置 APT 镜像源
82
84
  sed -i "s|http://[^/]*\.ubuntu\.com|${APT_MIRROR}|g" /etc/apt/sources.list.d/ubuntu.sources
@@ -87,12 +89,14 @@ RUN <<EOX
87
89
  # 开发与构建
88
90
  # 系统管理
89
91
  # 通用工具
92
+ # Python
90
93
  apt-get -o Acquire::https::Verify-Peer=false update -y
91
94
  apt-get -o Acquire::https::Verify-Peer=false install -y --no-install-recommends \
92
95
  ca-certificates openssl curl wget net-tools iputils-ping dnsutils socat ncat ssh \
93
96
  git gh g++ make sqlite3 \
94
- procps psmisc lsof supervisor man-db \
95
- nano jq file tree ripgrep less bc xxd tar zip unzip gzip
97
+ procps psmisc lsof supervisor \
98
+ nano jq file tree ripgrep less bc xxd tar zip unzip gzip \
99
+ python3.12 python3.12-dev python3.12-venv python3-pip
96
100
 
97
101
  # 更新 CA 证书
98
102
  update-ca-certificates
@@ -107,15 +111,7 @@ RUN <<EOX
107
111
  apt-get install -y --no-install-recommends docker.io
108
112
  ;; esac
109
113
 
110
- # 清理
111
- apt-get clean
112
- rm -rf /tmp/* /var/tmp/* /var/log/apt /var/log/*.log /var/lib/apt/lists/* ~/.cache ~/.npm ~/go/pkg/mod/cache
113
- EOX
114
-
115
- RUN <<EOX
116
- # 安装 python
117
- apt-get update -y
118
- apt-get install -y --no-install-recommends python3.12 python3.12-dev python3.12-venv python3-pip
114
+ # 配置 python
119
115
  ln -sf /usr/bin/python3 /usr/bin/python
120
116
  ln -sf /usr/bin/pip3 /usr/bin/pip
121
117
  pip config set global.index-url "${PIP_INDEX_URL}"
@@ -131,6 +127,8 @@ EOX
131
127
 
132
128
  # 从 cache-stage 复制 Node.js(缓存或下载)
133
129
  COPY --from=cache-stage /opt/node /usr/local
130
+ COPY ./docker/res/playwright/cli-cont-headless.init.js /app/config/cli-cont-headless.init.js
131
+ COPY ./docker/res/playwright/cli-cont-headless.json /app/config/cli-cont-headless.json
134
132
  COPY ./docker/res/ /tmp/docker-res/
135
133
  ARG GIT_SSL_NO_VERIFY=false
136
134
 
@@ -203,9 +201,31 @@ RUN <<EOX
203
201
  cp /tmp/docker-res/opencode/opencode.json ~/.config/opencode/opencode.json
204
202
  ;; esac
205
203
 
204
+ # 安装 Playwright CLI skills(不在镜像构建阶段下载浏览器)
205
+ npm install -g @playwright/cli@latest
206
+ PLAYWRIGHT_CLI_INSTALL_DIR=/tmp/playwright-cli-install
207
+ mkdir -p "${PLAYWRIGHT_CLI_INSTALL_DIR}/.playwright"
208
+ cat > "${PLAYWRIGHT_CLI_INSTALL_DIR}/.playwright/cli.config.json" <<'EOF'
209
+ {
210
+ "browser": {
211
+ "browserName": "chromium",
212
+ "launchOptions": {
213
+ "channel": "chrome"
214
+ }
215
+ }
216
+ }
217
+ EOF
218
+ cd "${PLAYWRIGHT_CLI_INSTALL_DIR}"
219
+ playwright-cli install --skills
220
+ mkdir -p "$HOME/.codex/skills/playwright-cli" ~/.gemini/skills/playwright-cli
221
+ cp -R "${PLAYWRIGHT_CLI_INSTALL_DIR}/.claude/skills/playwright-cli/." "$HOME/.codex/skills/playwright-cli/"
222
+ cp -R "${PLAYWRIGHT_CLI_INSTALL_DIR}/.claude/skills/playwright-cli/." "$HOME/.gemini/skills/playwright-cli/"
223
+ cd $OLDPWD
224
+ rm -rf "${PLAYWRIGHT_CLI_INSTALL_DIR}"
225
+
206
226
  # 清理
207
227
  npm cache clean --force
208
- rm -rf /tmp/* /var/tmp/* /var/log/apt /var/log/*.log /var/lib/apt/lists/* ~/.cache ~/.npm ~/go/pkg/mod/cache
228
+ rm -rf /tmp/* /var/tmp/* /var/log/apt /var/log/*.log /var/lib/apt/lists/* ~/.npm ~/go/pkg/mod/cache
209
229
  EOX
210
230
 
211
231
  # 从 cache-stage 复制 JDT LSP(缓存或下载)
@@ -224,7 +244,7 @@ RUN <<EOX
224
244
 
225
245
  # 清理
226
246
  apt-get clean
227
- rm -rf /tmp/* /var/tmp/* /var/log/apt /var/log/*.log /var/lib/apt/lists/* ~/.cache ~/.npm ~/go/pkg/mod/cache
247
+ rm -rf /tmp/* /var/tmp/* /var/log/apt /var/log/*.log /var/lib/apt/lists/* ~/.npm ~/go/pkg/mod/cache
228
248
  ;; esac
229
249
  rm -rf /tmp/jdtls-cache
230
250
  EOX
@@ -236,7 +256,7 @@ RUN <<EOX
236
256
  # 安装 go
237
257
  case ",$TOOL," in *,full,*|*,go,*)
238
258
  apt-get update -y
239
- apt-get install -y --no-install-recommends golang golang-src gcc
259
+ apt-get install -y --no-install-recommends golang gcc
240
260
  go env -w GOPROXY=https://mirrors.tencent.com/go
241
261
 
242
262
  # 安装 LSP服务(go)
@@ -252,7 +272,7 @@ RUN <<EOX
252
272
  # 清理
253
273
  apt-get clean
254
274
  go clean -modcache -cache
255
- rm -rf /tmp/* /var/tmp/* /var/log/apt /var/log/*.log /var/lib/apt/lists/* ~/.cache ~/.npm ~/go/pkg/mod/cache
275
+ rm -rf /tmp/* /var/tmp/* /var/log/apt /var/log/*.log /var/lib/apt/lists/* ~/.npm ~/go/pkg/mod/cache
256
276
  ;; esac
257
277
  rm -rf /tmp/gopls-cache
258
278
  EOX
@@ -262,7 +282,7 @@ COPY ./docker/res/supervisor/s.conf /etc/supervisor/conf.d/s.conf
262
282
 
263
283
  RUN <<EOX
264
284
  # 清理
265
- rm -rf /tmp/* /var/tmp/* /var/log/apt /var/log/*.log /var/lib/apt/lists/* ~/.cache ~/.npm ~/go/pkg/mod/cache
285
+ rm -rf /tmp/* /var/tmp/* /var/log/apt /var/log/*.log /var/lib/apt/lists/* ~/.npm ~/go/pkg/mod/cache
266
286
  EOX
267
287
 
268
288
  WORKDIR /tmp
@@ -0,0 +1,12 @@
1
+ 'use strict';
2
+
3
+ (function () {
4
+ const platformValue = 'MacIntel';
5
+ try {
6
+ const navProto = Object.getPrototypeOf(navigator);
7
+ Object.defineProperty(navProto, 'platform', {
8
+ configurable: true,
9
+ get: () => platformValue
10
+ });
11
+ } catch (_) {}
12
+ })();
@@ -0,0 +1,37 @@
1
+ {
2
+ "outputDir": "/tmp/.playwright-cli",
3
+ "browser": {
4
+ "chromiumSandbox": true,
5
+ "browserName": "chromium",
6
+ "initScript": [
7
+ "/app/config/cli-cont-headless.init.js"
8
+ ],
9
+ "launchOptions": {
10
+ "channel": "chromium",
11
+ "headless": true,
12
+ "args": [
13
+ "--user-agent=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",
14
+ "--lang=zh-CN",
15
+ "--window-size=1366,768",
16
+ "--disable-blink-features=AutomationControlled",
17
+ "--force-webrtc-ip-handling-policy=disable_non_proxied_udp"
18
+ ]
19
+ },
20
+ "contextOptions": {
21
+ "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",
22
+ "locale": "zh-CN",
23
+ "timezoneId": "Asia/Shanghai",
24
+ "viewport": {
25
+ "width": 1366,
26
+ "height": 768
27
+ },
28
+ "screen": {
29
+ "width": 1366,
30
+ "height": 768
31
+ },
32
+ "extraHTTPHeaders": {
33
+ "Accept-Language": "zh-CN,zh;q=0.9"
34
+ }
35
+ }
36
+ }
37
+ }
@@ -10,6 +10,7 @@ function buildContainerRunArgs(options) {
10
10
  '--name', options.containerName,
11
11
  '--entrypoint', '',
12
12
  ...(options.contModeArgs || []),
13
+ ...(options.containerExtraArgs || []),
13
14
  ...(options.containerEnvs || []),
14
15
  ...(options.containerVolumes || []),
15
16
  ...(options.containerPorts || []),
@@ -193,18 +193,7 @@ function collectCodexInitData(homeDir, ctx) {
193
193
 
194
194
  if (configToml && typeof configToml === 'object') {
195
195
  setInitValue(values, 'OPENAI_MODEL', configToml.model);
196
- const providers = configToml.model_providers;
197
- let providerConfig = null;
198
- if (providers && typeof providers === 'object') {
199
- const selectedProviderName =
200
- (typeof configToml.model_provider === 'string' && providers[configToml.model_provider])
201
- ? configToml.model_provider
202
- : Object.keys(providers)[0];
203
- providerConfig = selectedProviderName ? providers[selectedProviderName] : null;
204
- }
205
- if (providerConfig && typeof providerConfig === 'object') {
206
- setInitValue(values, 'OPENAI_BASE_URL', providerConfig.base_url);
207
- }
196
+ setInitValue(values, 'OPENAI_BASE_URL', configToml.openai_base_url);
208
197
  }
209
198
 
210
199
  if (fs.existsSync(codexDir)) {
@@ -6,7 +6,7 @@ services:
6
6
  args:
7
7
  PLAYWRIGHT_MCP_BASE_IMAGE: mcr.microsoft.com/playwright/mcp:${PLAYWRIGHT_MCP_DOCKER_TAG:-latest}
8
8
  image: ${PLAYWRIGHT_MCP_IMAGE:-localhost/xcanwin/manyoyo-playwright-headed}
9
- container_name: ${PLAYWRIGHT_MCP_CONTAINER_NAME:-my-playwright-cont-headed}
9
+ container_name: ${PLAYWRIGHT_MCP_CONTAINER_NAME:-my-playwright-mcp-cont-headed}
10
10
  init: true
11
11
  stdin_open: true
12
12
  ports:
@@ -1,7 +1,7 @@
1
1
  services:
2
2
  playwright:
3
3
  image: mcr.microsoft.com/playwright/mcp:${PLAYWRIGHT_MCP_DOCKER_TAG:-latest}
4
- container_name: ${PLAYWRIGHT_MCP_CONTAINER_NAME:-my-playwright-cont-headless}
4
+ container_name: ${PLAYWRIGHT_MCP_CONTAINER_NAME:-my-playwright-mcp-cont-headless}
5
5
  init: true
6
6
  stdin_open: true
7
7
  ports:
@@ -15,42 +15,62 @@ const EXTENSIONS = [
15
15
  ['webgl-fingerprint-defender', 'olnbjpaejebpnokblkepbphhembdicik']
16
16
  ];
17
17
 
18
- const SCENE_ORDER = ['cont-headless', 'cont-headed', 'host-headless', 'host-headed'];
18
+ const SCENE_ORDER = ['mcp-cont-headless', 'mcp-cont-headed', 'mcp-host-headless', 'mcp-host-headed', 'cli-host-headless', 'cli-host-headed'];
19
19
 
20
20
  const SCENE_DEFS = {
21
- 'cont-headless': {
21
+ 'mcp-cont-headless': {
22
22
  type: 'container',
23
- configFile: 'container-headless.json',
23
+ engine: 'mcp',
24
+ configFile: 'mcp-cont-headless.json',
24
25
  composeFile: 'compose-headless.yaml',
25
- projectName: 'my-playwright-cont-headless',
26
- containerName: 'my-playwright-cont-headless',
26
+ projectName: 'my-playwright-mcp-cont-headless',
27
+ containerName: 'my-playwright-mcp-cont-headless',
27
28
  portKey: 'contHeadless',
28
29
  headless: true,
29
30
  listenHost: '0.0.0.0'
30
31
  },
31
- 'cont-headed': {
32
+ 'mcp-cont-headed': {
32
33
  type: 'container',
33
- configFile: 'container-headed.json',
34
+ engine: 'mcp',
35
+ configFile: 'mcp-cont-headed.json',
34
36
  composeFile: 'compose-headed.yaml',
35
- projectName: 'my-playwright-cont-headed',
36
- containerName: 'my-playwright-cont-headed',
37
+ projectName: 'my-playwright-mcp-cont-headed',
38
+ containerName: 'my-playwright-mcp-cont-headed',
37
39
  portKey: 'contHeaded',
38
40
  headless: false,
39
41
  listenHost: '0.0.0.0'
40
42
  },
41
- 'host-headless': {
43
+ 'mcp-host-headless': {
42
44
  type: 'host',
43
- configFile: 'host-headless.json',
45
+ engine: 'mcp',
46
+ configFile: 'mcp-host-headless.json',
44
47
  portKey: 'hostHeadless',
45
48
  headless: true,
46
49
  listenHost: '127.0.0.1'
47
50
  },
48
- 'host-headed': {
51
+ 'mcp-host-headed': {
49
52
  type: 'host',
50
- configFile: 'host-headed.json',
53
+ engine: 'mcp',
54
+ configFile: 'mcp-host-headed.json',
51
55
  portKey: 'hostHeaded',
52
56
  headless: false,
53
57
  listenHost: '127.0.0.1'
58
+ },
59
+ 'cli-host-headless': {
60
+ type: 'host',
61
+ engine: 'cli',
62
+ configFile: 'cli-host-headless.json',
63
+ portKey: 'cliHostHeadless',
64
+ headless: true,
65
+ listenHost: '0.0.0.0'
66
+ },
67
+ 'cli-host-headed': {
68
+ type: 'host',
69
+ engine: 'cli',
70
+ configFile: 'cli-host-headed.json',
71
+ portKey: 'cliHostHeaded',
72
+ headless: false,
73
+ listenHost: '0.0.0.0'
54
74
  }
55
75
  };
56
76
 
@@ -67,6 +87,14 @@ const DEFAULT_FINGERPRINT_PROFILE = {
67
87
  };
68
88
  const DISABLE_WEBRTC_LAUNCH_ARGS = ['--disable-webrtc'];
69
89
 
90
+ function isMcpScene(sceneName) {
91
+ return Boolean(SCENE_DEFS[sceneName] && SCENE_DEFS[sceneName].engine === 'mcp');
92
+ }
93
+
94
+ function isCliScene(sceneName) {
95
+ return Boolean(SCENE_DEFS[sceneName] && SCENE_DEFS[sceneName].engine === 'cli');
96
+ }
97
+
70
98
  function platformFromUserAgent(userAgent) {
71
99
  const ua = String(userAgent || '').toLowerCase();
72
100
  if (ua.includes('macintosh') || ua.includes('mac os x')) {
@@ -264,6 +292,7 @@ class PlaywrightPlugin {
264
292
  const defaultConfig = {
265
293
  runtime: 'mixed',
266
294
  enabledScenes: [...SCENE_ORDER],
295
+ cliSessionScene: 'cli-host-headless',
267
296
  hostListen: '127.0.0.1',
268
297
  mcpDefaultHost: 'host.docker.internal',
269
298
  dockerTag: process.env.PLAYWRIGHT_MCP_DOCKER_TAG || 'latest',
@@ -281,6 +310,8 @@ class PlaywrightPlugin {
281
310
  contHeaded: 8932,
282
311
  hostHeadless: 8933,
283
312
  hostHeaded: 8934,
313
+ cliHostHeadless: 8935,
314
+ cliHostHeaded: 8936,
284
315
  contHeadedNoVnc: 6080
285
316
  }
286
317
  };
@@ -306,6 +337,7 @@ class PlaywrightPlugin {
306
337
  asStringArray(this.globalConfig.enabledScenes, [...defaultConfig.enabledScenes])
307
338
  );
308
339
  merged.containerRuntime = this.resolveContainerRuntime(merged.containerRuntime);
340
+ merged.cliSessionScene = String(merged.cliSessionScene || defaultConfig.cliSessionScene).trim();
309
341
  merged.navigatorPlatform = String(merged.navigatorPlatform || defaultConfig.navigatorPlatform).trim() || defaultConfig.navigatorPlatform;
310
342
  merged.disableWebRTC = asBoolean(merged.disableWebRTC, defaultConfig.disableWebRTC);
311
343
 
@@ -317,6 +349,9 @@ class PlaywrightPlugin {
317
349
  if (invalidScene) {
318
350
  throw new Error(`playwright.enabledScenes 包含未知场景: ${invalidScene}`);
319
351
  }
352
+ if (merged.cliSessionScene && !isCliScene(merged.cliSessionScene)) {
353
+ throw new Error(`playwright.cliSessionScene 无效: ${merged.cliSessionScene}`);
354
+ }
320
355
 
321
356
  return merged;
322
357
  }
@@ -412,6 +447,40 @@ class PlaywrightPlugin {
412
447
  return !fs.existsSync(this.sceneConfigPath(sceneName));
413
448
  }
414
449
 
450
+ sceneEndpointPath(sceneName) {
451
+ return path.join(this.config.runDir, `${sceneName}.endpoint.json`);
452
+ }
453
+
454
+ readSceneEndpoint(sceneName) {
455
+ const filePath = this.sceneEndpointPath(sceneName);
456
+ if (!fs.existsSync(filePath)) {
457
+ return null;
458
+ }
459
+ try {
460
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
461
+ } catch {
462
+ return null;
463
+ }
464
+ }
465
+
466
+ writeSceneEndpoint(sceneName, payload) {
467
+ fs.mkdirSync(this.config.runDir, { recursive: true });
468
+ fs.writeFileSync(this.sceneEndpointPath(sceneName), `${JSON.stringify(payload, null, 4)}\n`, 'utf8');
469
+ }
470
+
471
+ removeSceneEndpoint(sceneName) {
472
+ fs.rmSync(this.sceneEndpointPath(sceneName), { force: true });
473
+ }
474
+
475
+ sceneCliAttachConfigPath(sceneName) {
476
+ return path.join(this.config.runDir, `${sceneName}.cli-attach.json`);
477
+ }
478
+
479
+ writeSceneCliAttachConfig(sceneName, payload) {
480
+ fs.mkdirSync(this.config.runDir, { recursive: true });
481
+ fs.writeFileSync(this.sceneCliAttachConfigPath(sceneName), `${JSON.stringify(payload, null, 4)}\n`, 'utf8');
482
+ }
483
+
415
484
  sceneInitScriptPath(sceneName) {
416
485
  const configFile = path.basename(this.sceneConfigPath(sceneName), '.json');
417
486
  return path.join(this.config.configDir, `${configFile}.init.js`);
@@ -479,6 +548,9 @@ class PlaywrightPlugin {
479
548
  }
480
549
 
481
550
  defaultBrowserName(sceneName) {
551
+ if (isCliScene(sceneName)) {
552
+ return 'chromium';
553
+ }
482
554
  const cfg = this.buildSceneConfig(sceneName);
483
555
  const browserName = cfg && cfg.browser && cfg.browser.browserName;
484
556
  return String(browserName || 'chromium');
@@ -494,10 +566,10 @@ class PlaywrightPlugin {
494
566
  }
495
567
 
496
568
  ensureHostScenePrerequisites(sceneName) {
497
- if (!this.sceneConfigMissing(sceneName)) {
569
+ if (!isCliScene(sceneName) && !this.sceneConfigMissing(sceneName)) {
498
570
  return;
499
571
  }
500
- this.runCmd([this.localBinPath('playwright'), 'install', '--with-deps', this.defaultBrowserName(sceneName)], { check: true });
572
+ this.runCmd([this.playwrightBinPath(sceneName), 'install', '--with-deps', this.defaultBrowserName(sceneName)], { check: true });
501
573
  }
502
574
 
503
575
  scenePidFile(sceneName) {
@@ -517,6 +589,26 @@ class PlaywrightPlugin {
517
589
  return binPath;
518
590
  }
519
591
 
592
+ playwrightBinPath(sceneName) {
593
+ if (!isCliScene(sceneName)) {
594
+ return this.localBinPath('playwright');
595
+ }
596
+
597
+ const filename = process.platform === 'win32' ? 'playwright.cmd' : 'playwright';
598
+ const candidates = [
599
+ path.join(this.projectRoot, 'node_modules', '@playwright', 'mcp', 'node_modules', '.bin', filename),
600
+ path.join(this.projectRoot, 'node_modules', '.bin', filename)
601
+ ];
602
+
603
+ for (const candidate of candidates) {
604
+ if (fs.existsSync(candidate)) {
605
+ return candidate;
606
+ }
607
+ }
608
+
609
+ throw new Error(`local binary not found for ${sceneName}. Run npm install first.`);
610
+ }
611
+
520
612
  extensionDirPath() {
521
613
  return path.join(os.homedir(), '.manyoyo', 'plugin', 'playwright', 'extensions');
522
614
  }
@@ -650,22 +742,25 @@ class PlaywrightPlugin {
650
742
  return { containerPaths, volumeMounts };
651
743
  }
652
744
 
653
- buildSceneConfig(sceneName, options = {}) {
654
- const def = SCENE_DEFS[sceneName];
655
- const port = this.scenePort(sceneName);
656
- const extensionPaths = asStringArray(options.extensionPaths, []);
657
- const initScript = asStringArray(options.initScript, []);
658
- const baseLaunchArgs = [
745
+ baseLaunchArgs() {
746
+ return [
659
747
  `--user-agent=${DEFAULT_FINGERPRINT_PROFILE.userAgent}`,
660
748
  `--lang=${DEFAULT_FINGERPRINT_PROFILE.locale}`,
661
749
  `--window-size=${DEFAULT_FINGERPRINT_PROFILE.width},${DEFAULT_FINGERPRINT_PROFILE.height}`,
662
750
  '--disable-blink-features=AutomationControlled',
663
751
  '--force-webrtc-ip-handling-policy=disable_non_proxied_udp'
664
752
  ];
753
+ }
754
+
755
+ buildMcpSceneConfig(sceneName, options = {}) {
756
+ const def = SCENE_DEFS[sceneName];
757
+ const port = this.scenePort(sceneName);
758
+ const extensionPaths = asStringArray(options.extensionPaths, []);
759
+ const initScript = asStringArray(options.initScript, []);
665
760
  const launchOptions = {
666
761
  channel: 'chromium',
667
762
  headless: def.headless,
668
- args: [...baseLaunchArgs]
763
+ args: [...this.baseLaunchArgs()]
669
764
  };
670
765
 
671
766
  if (extensionPaths.length > 0) {
@@ -676,7 +771,7 @@ class PlaywrightPlugin {
676
771
  }
677
772
 
678
773
  return {
679
- outputDir: '/tmp/playwright-mcp',
774
+ outputDir: '/tmp/.playwright-mcp',
680
775
  server: {
681
776
  host: def.listenHost,
682
777
  port,
@@ -712,8 +807,44 @@ class PlaywrightPlugin {
712
807
  };
713
808
  }
714
809
 
810
+ buildCliSceneConfig(sceneName, options = {}) {
811
+ const def = SCENE_DEFS[sceneName];
812
+ const extensionPaths = asStringArray(options.extensionPaths, []);
813
+ const payload = {
814
+ host: def.listenHost,
815
+ port: this.scenePort(sceneName),
816
+ wsPath: `/${sceneName}-${crypto.randomBytes(8).toString('hex')}`,
817
+ headless: def.headless,
818
+ channel: 'chromium',
819
+ chromiumSandbox: true,
820
+ args: [...this.baseLaunchArgs()]
821
+ };
822
+
823
+ if (extensionPaths.length > 0) {
824
+ payload.args.push(...this.buildExtensionLaunchArgs(extensionPaths));
825
+ }
826
+ if (this.config.disableWebRTC) {
827
+ payload.args.push(...DISABLE_WEBRTC_LAUNCH_ARGS);
828
+ }
829
+
830
+ return payload;
831
+ }
832
+
833
+ buildSceneConfig(sceneName, options = {}) {
834
+ if (isCliScene(sceneName)) {
835
+ return this.buildCliSceneConfig(sceneName, options);
836
+ }
837
+ return this.buildMcpSceneConfig(sceneName, options);
838
+ }
839
+
715
840
  ensureSceneConfig(sceneName, options = {}) {
716
841
  fs.mkdirSync(this.config.configDir, { recursive: true });
842
+ if (isCliScene(sceneName)) {
843
+ const payload = this.buildCliSceneConfig(sceneName, options);
844
+ const filePath = this.sceneConfigPath(sceneName);
845
+ fs.writeFileSync(filePath, `${JSON.stringify(payload, null, 4)}\n`, 'utf8');
846
+ return filePath;
847
+ }
717
848
  const initScriptPath = this.ensureSceneInitScript(sceneName);
718
849
  const configuredInitScript = asStringArray(options.initScript, []);
719
850
  const initScript = configuredInitScript.length > 0 ? configuredInitScript : [initScriptPath];
@@ -772,13 +903,13 @@ class PlaywrightPlugin {
772
903
  PLAYWRIGHT_MCP_NOVNC_PORT: String(this.config.ports.contHeadedNoVnc)
773
904
  };
774
905
 
775
- if (sceneName === 'cont-headed') {
906
+ if (sceneName === 'mcp-cont-headed') {
776
907
  const envKey = this.config.vncPasswordEnvKey;
777
908
  let password = process.env[envKey];
778
909
  if (!password) {
779
910
  password = this.randomAlnum(16);
780
911
  if (requireVncPassword) {
781
- this.writeStdout(`[up] cont-headed ${envKey} not set; generated random 16-char password: ${password}`);
912
+ this.writeStdout(`[up] mcp-cont-headed ${envKey} not set; generated random 16-char password: ${password}`);
782
913
  }
783
914
  }
784
915
  env.VNC_PASSWORD = password;
@@ -816,6 +947,37 @@ class PlaywrightPlugin {
816
947
  return overridePath;
817
948
  }
818
949
 
950
+ buildCliSessionIntegration(dockerCmd) {
951
+ const sceneName = this.config.cliSessionScene;
952
+ if (!sceneName) {
953
+ return { envEntries: [], extraArgs: [], volumeEntries: [] };
954
+ }
955
+
956
+ const endpoint = this.readSceneEndpoint(sceneName);
957
+ if (!endpoint || !Number.isInteger(endpoint.port) || endpoint.port <= 0 || typeof endpoint.wsPath !== 'string' || !endpoint.wsPath) {
958
+ return { envEntries: [], extraArgs: [], volumeEntries: [] };
959
+ }
960
+
961
+ const normalizedDockerCmd = String(dockerCmd || '').trim().toLowerCase();
962
+ const connectHost = normalizedDockerCmd === 'podman' ? 'host.containers.internal' : 'host.docker.internal';
963
+ const remoteEndpoint = `ws://${connectHost}:${endpoint.port}${endpoint.wsPath}`;
964
+ const hostConfigPath = this.sceneCliAttachConfigPath(sceneName);
965
+ const containerConfigPath = `/tmp/manyoyo-playwright/${sceneName}.cli-attach.json`;
966
+ this.writeSceneCliAttachConfig(sceneName, {
967
+ browser: {
968
+ remoteEndpoint
969
+ }
970
+ });
971
+ const envEntries = [
972
+ `PLAYWRIGHT_MCP_CONFIG=${containerConfigPath}`
973
+ ];
974
+ const extraArgs = normalizedDockerCmd === 'docker'
975
+ ? ['--add-host', 'host.docker.internal:host-gateway']
976
+ : [];
977
+ const volumeEntries = ['--volume', `${hostConfigPath}:${containerConfigPath}:ro`];
978
+ return { envEntries, extraArgs, volumeEntries };
979
+ }
980
+
819
981
  async startContainer(sceneName, options = {}) {
820
982
  const runtime = this.config.containerRuntime;
821
983
  if (!this.ensureCommandAvailable(runtime)) {
@@ -972,8 +1134,21 @@ class PlaywrightPlugin {
972
1134
  return cp.returncode === 0 ? 0 : 1;
973
1135
  }
974
1136
 
975
- spawnHostProcess(mcpBinPath, cfgPath, logFd) {
976
- return spawn(mcpBinPath, ['--config', String(cfgPath)], {
1137
+ hostLaunchCommand(sceneName, cfgPath) {
1138
+ if (isCliScene(sceneName)) {
1139
+ return {
1140
+ command: this.playwrightBinPath(sceneName),
1141
+ args: ['launch-server', '--browser', this.defaultBrowserName(sceneName), '--config', String(cfgPath)]
1142
+ };
1143
+ }
1144
+ return {
1145
+ command: this.localBinPath('playwright-mcp'),
1146
+ args: ['--config', String(cfgPath)]
1147
+ };
1148
+ }
1149
+
1150
+ spawnHostProcess(command, args, logFd) {
1151
+ return spawn(command, args, {
977
1152
  detached: true,
978
1153
  stdio: ['ignore', logFd, logFd]
979
1154
  });
@@ -998,7 +1173,9 @@ class PlaywrightPlugin {
998
1173
 
999
1174
  hostScenePids(sceneName) {
1000
1175
  const cfgPath = this.sceneConfigPath(sceneName);
1001
- const pattern = `playwright-mcp.*--config ${cfgPath}`;
1176
+ const pattern = isCliScene(sceneName)
1177
+ ? `playwright.*launch-server.*--config ${cfgPath}`
1178
+ : `playwright-mcp.*--config ${cfgPath}`;
1002
1179
  const cp = this.runCmd(['pgrep', '-f', pattern], { captureOutput: true, check: false });
1003
1180
 
1004
1181
  if (cp.returncode !== 0 || !cp.stdout.trim()) {
@@ -1043,6 +1220,7 @@ class PlaywrightPlugin {
1043
1220
  const pidFile = this.scenePidFile(sceneName);
1044
1221
  const logFile = this.sceneLogFile(sceneName);
1045
1222
  const port = this.scenePort(sceneName);
1223
+ this.removeSceneEndpoint(sceneName);
1046
1224
 
1047
1225
  let managedPids = this.hostScenePids(sceneName);
1048
1226
  if (managedPids.length > 0 && (await this.portReady(port))) {
@@ -1058,22 +1236,29 @@ class PlaywrightPlugin {
1058
1236
 
1059
1237
  fs.rmSync(pidFile, { force: true });
1060
1238
  const logFd = fs.openSync(logFile, 'a');
1061
- let mcpBinPath = '';
1239
+ let launchCommand = null;
1062
1240
  try {
1063
- mcpBinPath = this.localBinPath('playwright-mcp');
1241
+ launchCommand = this.hostLaunchCommand(sceneName, cfgPath);
1064
1242
  } catch (error) {
1065
1243
  fs.closeSync(logFd);
1066
1244
  this.writeStderr(`[up] ${sceneName} failed: ${error.message || String(error)}`);
1067
1245
  return 1;
1068
1246
  }
1069
1247
 
1070
- const starter = this.spawnHostProcess(mcpBinPath, cfgPath, logFd);
1248
+ const starter = this.spawnHostProcess(launchCommand.command, launchCommand.args, logFd);
1071
1249
  fs.closeSync(logFd);
1072
1250
  if (typeof starter.unref === 'function') {
1073
1251
  starter.unref();
1074
1252
  }
1075
1253
 
1076
1254
  if (await this.waitForPort(port)) {
1255
+ if (isCliScene(sceneName)) {
1256
+ const cfg = JSON.parse(fs.readFileSync(cfgPath, 'utf8'));
1257
+ this.writeSceneEndpoint(sceneName, {
1258
+ port,
1259
+ wsPath: String(cfg.wsPath || '')
1260
+ });
1261
+ }
1077
1262
  managedPids = await this.waitForHostPids(sceneName, starter.pid);
1078
1263
  if (managedPids.length > 0) {
1079
1264
  fs.writeFileSync(pidFile, `${managedPids[0]}`, 'utf8');
@@ -1099,6 +1284,7 @@ class PlaywrightPlugin {
1099
1284
  const pidFile = this.scenePidFile(sceneName);
1100
1285
  const port = this.scenePort(sceneName);
1101
1286
  const managedPids = this.hostScenePids(sceneName);
1287
+ this.removeSceneEndpoint(sceneName);
1102
1288
 
1103
1289
  for (const pid of managedPids) {
1104
1290
  try {
@@ -1337,7 +1523,7 @@ class PlaywrightPlugin {
1337
1523
  return 1;
1338
1524
  }
1339
1525
 
1340
- const scenes = this.resolveTargets('all');
1526
+ const scenes = this.resolveTargets('all').filter(sceneName => isMcpScene(sceneName));
1341
1527
  for (const sceneName of scenes) {
1342
1528
  const url = `http://${host}:${this.scenePort(sceneName)}/mcp`;
1343
1529
  this.writeStdout(`claude mcp add -t http -s user playwright-${sceneName} ${url}`);
@@ -1391,7 +1577,7 @@ class PlaywrightPlugin {
1391
1577
  return 1;
1392
1578
  }
1393
1579
 
1394
- async run({ action, scene = 'host-headless', host = '', extensionPaths = [], extensionNames = [], prodversion = '' }) {
1580
+ async run({ action, scene = 'mcp-host-headless', host = '', extensionPaths = [], extensionNames = [], prodversion = '' }) {
1395
1581
  if (action === 'ls') {
1396
1582
  return this.printSummary();
1397
1583
  }
package/lib/web/server.js CHANGED
@@ -778,6 +778,7 @@ function buildStaticContainerRuntime(ctx, containerName) {
778
778
  imageName: ctx.imageName,
779
779
  imageVersion: ctx.imageVersion,
780
780
  contModeArgs: Array.isArray(ctx.contModeArgs) ? ctx.contModeArgs.slice() : [],
781
+ containerExtraArgs: Array.isArray(ctx.containerExtraArgs) ? ctx.containerExtraArgs.slice() : [],
781
782
  containerEnvs: Array.isArray(ctx.containerEnvs) ? ctx.containerEnvs.slice() : [],
782
783
  containerVolumes: Array.isArray(ctx.containerVolumes) ? ctx.containerVolumes.slice() : [],
783
784
  containerPorts: Array.isArray(ctx.containerPorts) ? ctx.containerPorts.slice() : [],
@@ -918,6 +919,7 @@ function buildCreateRuntime(ctx, state, payload) {
918
919
  imageName,
919
920
  imageVersion,
920
921
  contModeArgs,
922
+ containerExtraArgs: Array.isArray(ctx.containerExtraArgs) ? ctx.containerExtraArgs.slice() : [],
921
923
  containerEnvs,
922
924
  containerVolumes,
923
925
  containerPorts,
@@ -1019,6 +1021,7 @@ async function ensureWebContainer(ctx, state, containerInput) {
1019
1021
  imageName: runtime.imageName,
1020
1022
  imageVersion: runtime.imageVersion,
1021
1023
  contModeArgs: runtime.contModeArgs,
1024
+ containerExtraArgs: runtime.containerExtraArgs,
1022
1025
  containerEnvs: runtime.containerEnvs,
1023
1026
  containerVolumes: runtime.containerVolumes,
1024
1027
  containerPorts: runtime.containerPorts,
@@ -1770,6 +1773,7 @@ async function startWebServer(options) {
1770
1773
  execCommand: options.execCommand,
1771
1774
  execCommandSuffix: options.execCommandSuffix,
1772
1775
  contModeArgs: options.contModeArgs,
1776
+ containerExtraArgs: options.containerExtraArgs,
1773
1777
  containerEnvs: options.containerEnvs,
1774
1778
  containerVolumes: options.containerVolumes,
1775
1779
  containerPorts: options.containerPorts,
@@ -6,7 +6,7 @@
6
6
  "hostPath": "/path/to/your/project",
7
7
  "containerPath": "/path/to/your/project",
8
8
  "imageName": "localhost/xcanwin/manyoyo",
9
- "imageVersion": "1.8.4-common",
9
+ "imageVersion": "1.8.8-common",
10
10
  "containerMode": "common",
11
11
 
12
12
  // 容器启动环境(env 按 key 合并覆盖;其余数组参数会累加)
@@ -50,10 +50,12 @@
50
50
  // mixed: 支持容器+宿主机;container: 仅容器;host: 仅宿主机
51
51
  "runtime": "mixed",
52
52
  // 启用场景(可按需裁剪)
53
- "enabledScenes": ["cont-headless", "cont-headed", "host-headless", "host-headed"],
53
+ "enabledScenes": ["mcp-cont-headless", "mcp-cont-headed", "mcp-host-headless", "mcp-host-headed", "cli-host-headless", "cli-host-headed"],
54
+ // my run 默认注入的 playwright-cli 宿主场景
55
+ "cliSessionScene": "cli-host-headless",
54
56
  // mcp-add 默认 host(可改为 localhost / 127.0.0.1)
55
57
  "mcpDefaultHost": "host.docker.internal",
56
- // cont-headed 场景读取的密码环境变量名(默认 VNC_PASSWORD)
58
+ // mcp-cont-headed 场景读取的密码环境变量名(默认 VNC_PASSWORD)
57
59
  "vncPasswordEnvKey": "VNC_PASSWORD",
58
60
  // playwright ext-download 的 CRX prodversion 参数
59
61
  "extensionProdversion": "132.0.0.0",
@@ -66,6 +68,8 @@
66
68
  "contHeaded": 8932,
67
69
  "hostHeadless": 8933,
68
70
  "hostHeaded": 8934,
71
+ "cliHostHeadless": 8935,
72
+ "cliHostHeaded": 8936,
69
73
  "contHeadedNoVnc": 6080
70
74
  }
71
75
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@xcanwin/manyoyo",
3
- "version": "5.3.10",
4
- "imageVersion": "1.8.4-common",
3
+ "version": "5.4.4",
4
+ "imageVersion": "1.8.8-common",
5
5
  "description": "AI Agent CLI Security Sandbox for Docker and Podman",
6
6
  "keywords": [
7
7
  "manyoyo",