@xcanwin/manyoyo 3.8.7 → 3.9.3

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
@@ -120,6 +120,8 @@ manyoyo -y oc # OpenCode(或 opencode)
120
120
  manyoyo -l
121
121
  manyoyo -n myy-dev -x /bin/bash
122
122
  manyoyo -n myy-dev --crm
123
+ manyoyo --server 3000
124
+ manyoyo --server 3000 --server-user admin --server-pass 123456
123
125
 
124
126
  # 调试配置与命令拼装
125
127
  manyoyo --show-config
package/bin/manyoyo.js CHANGED
@@ -12,6 +12,7 @@ const crypto = require('crypto');
12
12
  const readline = require('readline');
13
13
  const { Command } = require('commander');
14
14
  const JSON5 = require('json5');
15
+ const { startWebServer } = require('../lib/web/server');
15
16
  const { version: BIN_VERSION, imageVersion: IMAGE_VERSION_BASE } = require('../package.json');
16
17
 
17
18
  // Helper function to format date like bash $(date +%m%d-%H%M)
@@ -57,6 +58,12 @@ let QUIET = {};
57
58
  let SHOW_COMMAND = false;
58
59
  let YES_MODE = false;
59
60
  let RM_ON_EXIT = false;
61
+ let SERVER_MODE = false;
62
+ let SERVER_PORT = 3000;
63
+ let SERVER_AUTH_USER = "";
64
+ let SERVER_AUTH_PASS = "";
65
+ let SERVER_AUTH_PASS_AUTO = false;
66
+ const SAFE_CONTAINER_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_.-]*$/;
60
67
 
61
68
  // Color definitions using ANSI codes
62
69
  const RED = '\x1b[0;31m';
@@ -83,6 +90,37 @@ function normalizeCommandSuffix(suffix) {
83
90
  return trimmed ? ` ${trimmed}` : "";
84
91
  }
85
92
 
93
+ function parseServerPort(rawPort) {
94
+ if (rawPort === true || rawPort === undefined || rawPort === null || rawPort === '') {
95
+ return 3000;
96
+ }
97
+
98
+ const value = String(rawPort).trim();
99
+ if (!/^\d+$/.test(value)) {
100
+ console.error(`${RED}⚠️ 错误: --server 端口必须是 1-65535 的整数: ${rawPort}${NC}`);
101
+ process.exit(1);
102
+ }
103
+
104
+ const port = Number(value);
105
+ if (port < 1 || port > 65535) {
106
+ console.error(`${RED}⚠️ 错误: --server 端口超出范围 (1-65535): ${rawPort}${NC}`);
107
+ process.exit(1);
108
+ }
109
+
110
+ return port;
111
+ }
112
+
113
+ function ensureWebServerAuthCredentials() {
114
+ if (!SERVER_AUTH_USER) {
115
+ SERVER_AUTH_USER = 'admin';
116
+ }
117
+
118
+ if (!SERVER_AUTH_PASS) {
119
+ SERVER_AUTH_PASS = crypto.randomBytes(12).toString('hex');
120
+ SERVER_AUTH_PASS_AUTO = true;
121
+ }
122
+ }
123
+
86
124
  /**
87
125
  * 计算文件的 SHA256 哈希值(跨平台)
88
126
  * @param {string} filePath - 文件路径
@@ -101,7 +139,7 @@ function getFileSha256(filePath) {
101
139
  * @returns {Object} 脱敏后的配置对象
102
140
  */
103
141
  function sanitizeSensitiveData(obj) {
104
- const sensitiveKeys = ['KEY', 'TOKEN', 'SECRET', 'PASSWORD', 'AUTH', 'CREDENTIAL'];
142
+ const sensitiveKeys = ['KEY', 'TOKEN', 'SECRET', 'PASSWORD', 'PASS', 'AUTH', 'CREDENTIAL'];
105
143
 
106
144
  function sanitizeValue(key, value) {
107
145
  if (typeof value !== 'string') return value;
@@ -264,6 +302,10 @@ function validateName(label, value, pattern) {
264
302
  }
265
303
  }
266
304
 
305
+ function isValidContainerName(value) {
306
+ return typeof value === 'string' && SAFE_CONTAINER_NAME_PATTERN.test(value);
307
+ }
308
+
267
309
  // ==============================================================================
268
310
  // SECTION: Environment Variables and Volume Handling
269
311
  // ==============================================================================
@@ -858,6 +900,8 @@ function setupCommander() {
858
900
  ${MANYOYO_NAME} -n test --ef ./myenv.env -y c 使用当前目录 ./myenv.env 环境变量文件
859
901
  ${MANYOYO_NAME} -n test -- -c 恢复之前会话
860
902
  ${MANYOYO_NAME} -x echo 123 指定命令执行
903
+ ${MANYOYO_NAME} --server --server-user admin --server-pass 123456 启动带登录认证的网页服务
904
+ ${MANYOYO_NAME} --server 3000 启动网页交互服务
861
905
  ${MANYOYO_NAME} -n test -q tip -q cmd 多次使用静默选项
862
906
  `);
863
907
 
@@ -886,6 +930,9 @@ function setupCommander() {
886
930
  .option('--install <name>', '安装manyoyo命令 (docker-cli-plugin)')
887
931
  .option('--show-config', '显示最终生效配置并退出')
888
932
  .option('--show-command', '显示将执行的 docker run 命令并退出')
933
+ .option('--server [port]', '启动网页交互服务 (默认端口: 3000)')
934
+ .option('--server-user <username>', '网页服务登录用户名 (默认 admin)')
935
+ .option('--server-pass <password>', '网页服务登录密码 (默认自动生成随机密码)')
889
936
  .option('--yes', '所有提示自动确认 (用于CI/脚本)')
890
937
  .option('--rm-on-exit', '退出后自动删除容器 (一次性模式)')
891
938
  .option('-q, --quiet <item>', '静默显示 (可多次使用: cnew,crm,tip,cmd,full)', (value, previous) => [...(previous || []), value], []);
@@ -955,7 +1002,7 @@ function setupCommander() {
955
1002
  }
956
1003
 
957
1004
  // Basic name validation to reduce injection risk
958
- validateName('containerName', CONTAINER_NAME, /^[A-Za-z0-9][A-Za-z0-9_.-]*$/);
1005
+ validateName('containerName', CONTAINER_NAME, SAFE_CONTAINER_NAME_PATTERN);
959
1006
  validateName('imageName', IMAGE_NAME, /^[A-Za-z0-9][A-Za-z0-9._/:-]*$/);
960
1007
  validateName('imageVersion', IMAGE_VERSION, /^[A-Za-z0-9][A-Za-z0-9_.-]*$/);
961
1008
 
@@ -1010,6 +1057,26 @@ function setupCommander() {
1010
1057
  RM_ON_EXIT = true;
1011
1058
  }
1012
1059
 
1060
+ if (options.server !== undefined) {
1061
+ SERVER_MODE = true;
1062
+ SERVER_PORT = parseServerPort(options.server);
1063
+ }
1064
+
1065
+ const serverUserValue = options.serverUser || runConfig.serverUser || config.serverUser || process.env.MANYOYO_SERVER_USER;
1066
+ if (serverUserValue) {
1067
+ SERVER_AUTH_USER = String(serverUserValue);
1068
+ }
1069
+
1070
+ const serverPassValue = options.serverPass || runConfig.serverPass || config.serverPass || process.env.MANYOYO_SERVER_PASS;
1071
+ if (serverPassValue) {
1072
+ SERVER_AUTH_PASS = String(serverPassValue);
1073
+ SERVER_AUTH_PASS_AUTO = false;
1074
+ }
1075
+
1076
+ if (SERVER_MODE) {
1077
+ ensureWebServerAuthCredentials();
1078
+ }
1079
+
1013
1080
  if (options.showConfig) {
1014
1081
  const finalConfig = {
1015
1082
  hostPath: HOST_PATH,
@@ -1027,6 +1094,10 @@ function setupCommander() {
1027
1094
  shellSuffix: EXEC_COMMAND_SUFFIX || "",
1028
1095
  yolo: yoloValue || "",
1029
1096
  quiet: quietValue || [],
1097
+ server: SERVER_MODE,
1098
+ serverPort: SERVER_MODE ? SERVER_PORT : null,
1099
+ serverUser: SERVER_AUTH_USER || "",
1100
+ serverPass: SERVER_AUTH_PASS || "",
1030
1101
  exec: {
1031
1102
  prefix: EXEC_COMMAND_PREFIX,
1032
1103
  shell: EXEC_COMMAND,
@@ -1292,6 +1363,50 @@ async function handlePostExit(defaultCommand) {
1292
1363
  }
1293
1364
  }
1294
1365
 
1366
+ // ==============================================================================
1367
+ // SECTION: Web Server
1368
+ // ==============================================================================
1369
+
1370
+ async function runWebServerMode() {
1371
+ ensureWebServerAuthCredentials();
1372
+
1373
+ await startWebServer({
1374
+ serverPort: SERVER_PORT,
1375
+ authUser: SERVER_AUTH_USER,
1376
+ authPass: SERVER_AUTH_PASS,
1377
+ authPassAuto: SERVER_AUTH_PASS_AUTO,
1378
+ dockerCmd: DOCKER_CMD,
1379
+ hostPath: HOST_PATH,
1380
+ containerPath: CONTAINER_PATH,
1381
+ imageName: IMAGE_NAME,
1382
+ imageVersion: IMAGE_VERSION,
1383
+ execCommandPrefix: EXEC_COMMAND_PREFIX,
1384
+ execCommand: EXEC_COMMAND,
1385
+ execCommandSuffix: EXEC_COMMAND_SUFFIX,
1386
+ contModeArgs: CONT_MODE_ARGS,
1387
+ containerEnvs: CONTAINER_ENVS,
1388
+ containerVolumes: CONTAINER_VOLUMES,
1389
+ validateHostPath,
1390
+ formatDate,
1391
+ isValidContainerName,
1392
+ containerExists,
1393
+ getContainerStatus,
1394
+ waitForContainerReady,
1395
+ dockerExecArgs,
1396
+ showImagePullHint,
1397
+ removeContainer,
1398
+ webHistoryDir: path.join(os.homedir(), '.manyoyo', 'web-history'),
1399
+ colors: {
1400
+ RED,
1401
+ GREEN,
1402
+ YELLOW,
1403
+ BLUE,
1404
+ CYAN,
1405
+ NC
1406
+ }
1407
+ });
1408
+ }
1409
+
1295
1410
  // ==============================================================================
1296
1411
  // Main Function
1297
1412
  // ==============================================================================
@@ -1301,25 +1416,31 @@ async function main() {
1301
1416
  // 1. Setup commander and parse arguments
1302
1417
  setupCommander();
1303
1418
 
1304
- // 2. Handle image build operation
1419
+ // 2. Start web server mode
1420
+ if (SERVER_MODE) {
1421
+ await runWebServerMode();
1422
+ return;
1423
+ }
1424
+
1425
+ // 3. Handle image build operation
1305
1426
  if (IMAGE_BUILD_NEED) {
1306
1427
  await buildImage(IMAGE_BUILD_ARGS, IMAGE_NAME, IMAGE_VERSION.split('-')[0]);
1307
1428
  process.exit(0);
1308
1429
  }
1309
1430
 
1310
- // 3. Handle remove container operation
1431
+ // 4. Handle remove container operation
1311
1432
  handleRemoveContainer();
1312
1433
 
1313
- // 4. Validate host path safety
1434
+ // 5. Validate host path safety
1314
1435
  validateHostPath();
1315
1436
 
1316
- // 5. Setup container (create or connect)
1437
+ // 6. Setup container (create or connect)
1317
1438
  const defaultCommand = await setupContainer();
1318
1439
 
1319
- // 6. Execute command in container
1440
+ // 7. Execute command in container
1320
1441
  executeInContainer(defaultCommand);
1321
1442
 
1322
- // 7. Handle post-exit interactions
1443
+ // 8. Handle post-exit interactions
1323
1444
  await handlePostExit(defaultCommand);
1324
1445
 
1325
1446
  } catch (e) {
@@ -12,6 +12,8 @@
12
12
  "shell": "",
13
13
  "shellSuffix": "",
14
14
  "yolo": "",
15
+ "serverUser": "admin",
16
+ "serverPass": "change-this-password",
15
17
 
16
18
  // 合并型参数(配置文件和命令行会累加)
17
19
  "env": ["IS_SANDBOX=1"],
@@ -183,6 +183,9 @@ EOF
183
183
  mkdir -p ~/.codex
184
184
  cat > ~/.codex/config.toml <<EOF
185
185
  check_for_update_on_startup = false
186
+
187
+ [analytics]
188
+ enabled = false
186
189
  EOF
187
190
  ;; esac
188
191