@xcanwin/manyoyo 5.4.4 → 5.4.6

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/bin/manyoyo.js CHANGED
@@ -8,9 +8,9 @@ const crypto = require('crypto');
8
8
  const net = require('net');
9
9
  const readline = require('readline');
10
10
  const { Command } = require('commander');
11
- const JSON5 = require('json5');
12
11
  const { startWebServer } = require('../lib/web/server');
13
12
  const { buildContainerRunArgs, buildContainerRunCommand } = require('../lib/container-run');
13
+ const { getManyoyoConfigPath, readManyoyoConfig, syncGlobalImageVersion } = require('../lib/global-config');
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');
@@ -309,19 +309,29 @@ function installServeProcessDiagnostics(logger) {
309
309
  * @returns {Config} 配置对象
310
310
  */
311
311
  function loadConfig() {
312
- const configPath = path.join(os.homedir(), '.manyoyo', 'manyoyo.json');
313
- if (fs.existsSync(configPath)) {
314
- try {
315
- const config = JSON5.parse(fs.readFileSync(configPath, 'utf-8'));
316
- return config;
317
- } catch (e) {
318
- console.error(`${YELLOW}⚠️ 配置文件格式错误: ${configPath}${NC}`);
312
+ const result = readManyoyoConfig();
313
+ if (result.exists) {
314
+ if (result.parseError) {
315
+ console.error(`${YELLOW}⚠️ 配置文件格式错误: ${result.path}${NC}`);
319
316
  return {};
320
317
  }
318
+ return result.config;
321
319
  }
322
320
  return {};
323
321
  }
324
322
 
323
+ function syncBuiltImageVersionToGlobalConfig(imageVersion) {
324
+ const syncResult = syncGlobalImageVersion(imageVersion);
325
+ if (syncResult.updated) {
326
+ console.log(`${GREEN}✅ 已同步 ${path.basename(getManyoyoConfigPath())} 的 imageVersion: ${imageVersion}${NC}`);
327
+ return;
328
+ }
329
+ if (syncResult.reason === 'unchanged') {
330
+ return;
331
+ }
332
+ console.log(`${YELLOW}⚠️ 镜像构建成功,但未更新 imageVersion: ${syncResult.path}${NC}`);
333
+ }
334
+
325
335
  function loadRunConfig(name, config) {
326
336
  const runName = String(name || '').trim();
327
337
  if (!runName) {
@@ -1971,6 +1981,7 @@ async function main() {
1971
1981
  pruneDanglingImages,
1972
1982
  colors: { RED, GREEN, YELLOW, BLUE, CYAN, NC }
1973
1983
  });
1984
+ syncBuiltImageVersionToGlobalConfig(runtime.imageVersion);
1974
1985
  process.exit(0);
1975
1986
  }
1976
1987
 
@@ -0,0 +1,88 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const os = require('os');
5
+ const path = require('path');
6
+ const JSON5 = require('json5');
7
+
8
+ function getManyoyoConfigPath(homeDir = os.homedir()) {
9
+ return path.join(homeDir, '.manyoyo', 'manyoyo.json');
10
+ }
11
+
12
+ function readManyoyoConfig(homeDir = os.homedir()) {
13
+ const configPath = getManyoyoConfigPath(homeDir);
14
+ if (!fs.existsSync(configPath)) {
15
+ return {
16
+ path: configPath,
17
+ exists: false,
18
+ config: {}
19
+ };
20
+ }
21
+
22
+ try {
23
+ const config = JSON5.parse(fs.readFileSync(configPath, 'utf-8'));
24
+ return {
25
+ path: configPath,
26
+ exists: true,
27
+ config
28
+ };
29
+ } catch (error) {
30
+ return {
31
+ path: configPath,
32
+ exists: true,
33
+ config: {},
34
+ parseError: error
35
+ };
36
+ }
37
+ }
38
+
39
+ function syncGlobalImageVersion(imageVersion, options = {}) {
40
+ const homeDir = options.homeDir || os.homedir();
41
+ const result = readManyoyoConfig(homeDir);
42
+ const configPath = result.path;
43
+
44
+ if (result.parseError) {
45
+ return {
46
+ updated: false,
47
+ path: configPath,
48
+ reason: 'parse-error'
49
+ };
50
+ }
51
+
52
+ const currentConfig = result.config;
53
+ if (typeof currentConfig !== 'object' || currentConfig === null || Array.isArray(currentConfig)) {
54
+ return {
55
+ updated: false,
56
+ path: configPath,
57
+ reason: 'invalid-root'
58
+ };
59
+ }
60
+
61
+ if (currentConfig.imageVersion === imageVersion) {
62
+ return {
63
+ updated: false,
64
+ path: configPath,
65
+ reason: 'unchanged'
66
+ };
67
+ }
68
+
69
+ const nextConfig = {
70
+ ...currentConfig,
71
+ imageVersion
72
+ };
73
+
74
+ fs.mkdirSync(path.dirname(configPath), { recursive: true });
75
+ fs.writeFileSync(configPath, `${JSON.stringify(nextConfig, null, 4)}\n`);
76
+
77
+ return {
78
+ updated: true,
79
+ path: configPath,
80
+ reason: result.exists ? 'updated' : 'created'
81
+ };
82
+ }
83
+
84
+ module.exports = {
85
+ getManyoyoConfigPath,
86
+ readManyoyoConfig,
87
+ syncGlobalImageVersion
88
+ };
@@ -25,7 +25,7 @@ const SCENE_DEFS = {
25
25
  composeFile: 'compose-headless.yaml',
26
26
  projectName: 'my-playwright-mcp-cont-headless',
27
27
  containerName: 'my-playwright-mcp-cont-headless',
28
- portKey: 'contHeadless',
28
+ portKey: 'mcpContHeadless',
29
29
  headless: true,
30
30
  listenHost: '0.0.0.0'
31
31
  },
@@ -36,7 +36,7 @@ const SCENE_DEFS = {
36
36
  composeFile: 'compose-headed.yaml',
37
37
  projectName: 'my-playwright-mcp-cont-headed',
38
38
  containerName: 'my-playwright-mcp-cont-headed',
39
- portKey: 'contHeaded',
39
+ portKey: 'mcpContHeaded',
40
40
  headless: false,
41
41
  listenHost: '0.0.0.0'
42
42
  },
@@ -44,7 +44,7 @@ const SCENE_DEFS = {
44
44
  type: 'host',
45
45
  engine: 'mcp',
46
46
  configFile: 'mcp-host-headless.json',
47
- portKey: 'hostHeadless',
47
+ portKey: 'mcpHostHeadless',
48
48
  headless: true,
49
49
  listenHost: '127.0.0.1'
50
50
  },
@@ -52,7 +52,7 @@ const SCENE_DEFS = {
52
52
  type: 'host',
53
53
  engine: 'mcp',
54
54
  configFile: 'mcp-host-headed.json',
55
- portKey: 'hostHeaded',
55
+ portKey: 'mcpHostHeaded',
56
56
  headless: false,
57
57
  listenHost: '127.0.0.1'
58
58
  },
@@ -293,7 +293,6 @@ class PlaywrightPlugin {
293
293
  runtime: 'mixed',
294
294
  enabledScenes: [...SCENE_ORDER],
295
295
  cliSessionScene: 'cli-host-headless',
296
- hostListen: '127.0.0.1',
297
296
  mcpDefaultHost: 'host.docker.internal',
298
297
  dockerTag: process.env.PLAYWRIGHT_MCP_DOCKER_TAG || 'latest',
299
298
  containerRuntime: '',
@@ -306,13 +305,13 @@ class PlaywrightPlugin {
306
305
  disableWebRTC: false,
307
306
  composeDir: path.join(__dirname, 'playwright-assets'),
308
307
  ports: {
309
- contHeadless: 8931,
310
- contHeaded: 8932,
311
- hostHeadless: 8933,
312
- hostHeaded: 8934,
308
+ mcpContHeadless: 8931,
309
+ mcpContHeaded: 8932,
310
+ mcpHostHeadless: 8933,
311
+ mcpHostHeaded: 8934,
313
312
  cliHostHeadless: 8935,
314
313
  cliHostHeaded: 8936,
315
- contHeadedNoVnc: 6080
314
+ mcpContHeadedNoVnc: 6080
316
315
  }
317
316
  };
318
317
 
@@ -481,15 +480,15 @@ class PlaywrightPlugin {
481
480
  fs.writeFileSync(this.sceneCliAttachConfigPath(sceneName), `${JSON.stringify(payload, null, 4)}\n`, 'utf8');
482
481
  }
483
482
 
483
+ removeSceneCliAttachConfig(sceneName) {
484
+ fs.rmSync(this.sceneCliAttachConfigPath(sceneName), { force: true });
485
+ }
486
+
484
487
  sceneInitScriptPath(sceneName) {
485
488
  const configFile = path.basename(this.sceneConfigPath(sceneName), '.json');
486
489
  return path.join(this.config.configDir, `${configFile}.init.js`);
487
490
  }
488
491
 
489
- legacySceneInitScriptPath(sceneName) {
490
- return path.join(this.config.configDir, `${sceneName}.init.js`);
491
- }
492
-
493
492
  buildInitScriptContent() {
494
493
  const lines = [
495
494
  "'use strict';",
@@ -540,10 +539,6 @@ class PlaywrightPlugin {
540
539
  const filePath = this.sceneInitScriptPath(sceneName);
541
540
  const content = this.buildInitScriptContent();
542
541
  fs.writeFileSync(filePath, content, 'utf8');
543
- const legacyFilePath = this.legacySceneInitScriptPath(sceneName);
544
- if (legacyFilePath !== filePath) {
545
- fs.rmSync(legacyFilePath, { force: true });
546
- }
547
542
  return filePath;
548
543
  }
549
544
 
@@ -900,7 +895,7 @@ class PlaywrightPlugin {
900
895
  PLAYWRIGHT_MCP_CONFIG_PATH: cfgPath,
901
896
  PLAYWRIGHT_MCP_CONTAINER_NAME: def.containerName,
902
897
  PLAYWRIGHT_MCP_IMAGE: this.config.headedImage,
903
- PLAYWRIGHT_MCP_NOVNC_PORT: String(this.config.ports.contHeadedNoVnc)
898
+ PLAYWRIGHT_MCP_NOVNC_PORT: String(this.config.ports.mcpContHeadedNoVnc)
904
899
  };
905
900
 
906
901
  if (sceneName === 'mcp-cont-headed') {
@@ -1221,6 +1216,7 @@ class PlaywrightPlugin {
1221
1216
  const logFile = this.sceneLogFile(sceneName);
1222
1217
  const port = this.scenePort(sceneName);
1223
1218
  this.removeSceneEndpoint(sceneName);
1219
+ this.removeSceneCliAttachConfig(sceneName);
1224
1220
 
1225
1221
  let managedPids = this.hostScenePids(sceneName);
1226
1222
  if (managedPids.length > 0 && (await this.portReady(port))) {
@@ -1285,6 +1281,7 @@ class PlaywrightPlugin {
1285
1281
  const port = this.scenePort(sceneName);
1286
1282
  const managedPids = this.hostScenePids(sceneName);
1287
1283
  this.removeSceneEndpoint(sceneName);
1284
+ this.removeSceneCliAttachConfig(sceneName);
1288
1285
 
1289
1286
  for (const pid of managedPids) {
1290
1287
  try {
@@ -64,13 +64,13 @@
64
64
  // 是否禁用 WebRTC(默认 false)
65
65
  "disableWebRTC": false,
66
66
  "ports": {
67
- "contHeadless": 8931,
68
- "contHeaded": 8932,
69
- "hostHeadless": 8933,
70
- "hostHeaded": 8934,
67
+ "mcpContHeadless": 8931,
68
+ "mcpContHeaded": 8932,
69
+ "mcpHostHeadless": 8933,
70
+ "mcpHostHeaded": 8934,
71
71
  "cliHostHeadless": 8935,
72
72
  "cliHostHeaded": 8936,
73
- "contHeadedNoVnc": 6080
73
+ "mcpContHeadedNoVnc": 6080
74
74
  }
75
75
  }
76
76
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xcanwin/manyoyo",
3
- "version": "5.4.4",
3
+ "version": "5.4.6",
4
4
  "imageVersion": "1.8.8-common",
5
5
  "description": "AI Agent CLI Security Sandbox for Docker and Podman",
6
6
  "keywords": [