@xcanwin/manyoyo 5.8.6 → 5.8.9
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 +121 -173
- package/lib/global-config.js +1 -198
- package/lib/image-build.js +20 -4
- package/lib/init-config.js +22 -10
- package/lib/json5-text-edit.js +238 -0
- package/lib/plugin/playwright-bootstrap.js +116 -0
- package/lib/plugin/playwright-command-output.js +95 -0
- package/lib/plugin/playwright-container-runtime.js +94 -0
- package/lib/plugin/playwright-extension-manager.js +265 -0
- package/lib/plugin/playwright-extension-paths.js +98 -0
- package/lib/plugin/playwright-host-runtime.js +114 -0
- package/lib/plugin/playwright-scene-config.js +137 -0
- package/lib/plugin/playwright-scene-drivers.js +285 -0
- package/lib/plugin/playwright-scene-state.js +80 -0
- package/lib/plugin/playwright.js +169 -1049
- package/lib/runtime-normalizers.js +65 -0
- package/lib/runtime-resolver.js +195 -0
- package/lib/web/agent-command.js +153 -0
- package/lib/web/api-route-helpers.js +88 -0
- package/lib/web/container-exec.js +215 -0
- package/lib/web/http-handlers.js +163 -0
- package/lib/web/runtime-state.js +50 -0
- package/lib/web/server-context.js +71 -0
- package/lib/web/server-lifecycle.js +129 -0
- package/lib/web/server.js +293 -2496
- package/lib/web/session-api-routes.js +390 -0
- package/lib/web/structured-output.js +149 -0
- package/lib/web/structured-trace.js +603 -0
- package/lib/web/system-api-routes.js +114 -0
- package/lib/web/terminal-session.js +205 -0
- package/lib/web/upgrade-handler.js +94 -0
- package/package.json +1 -1
package/bin/manyoyo.js
CHANGED
|
@@ -16,6 +16,12 @@ const { buildImage } = require('../lib/image-build');
|
|
|
16
16
|
const { resolveAgentResumeArg, buildAgentResumeCommand } = require('../lib/agent-resume');
|
|
17
17
|
const { runPluginCommand, createPlugin } = require('../lib/plugin');
|
|
18
18
|
const { buildManyoyoLogPath } = require('../lib/log-path');
|
|
19
|
+
const { resolveRuntimeConfig } = require('../lib/runtime-resolver');
|
|
20
|
+
const {
|
|
21
|
+
parseEnvEntry: parseEnvEntryOrThrow,
|
|
22
|
+
expandHomeAliasPath,
|
|
23
|
+
normalizeVolume
|
|
24
|
+
} = require('../lib/runtime-normalizers');
|
|
19
25
|
const {
|
|
20
26
|
sanitizeSensitiveData,
|
|
21
27
|
sanitizeServeLogText,
|
|
@@ -454,23 +460,13 @@ async function askQuestion(prompt) {
|
|
|
454
460
|
* @param {string} env - 环境变量字符串 (KEY=VALUE)
|
|
455
461
|
*/
|
|
456
462
|
function parseEnvEntry(env) {
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
}
|
|
463
|
-
const key = envText.slice(0, idx);
|
|
464
|
-
const value = envText.slice(idx + 1);
|
|
465
|
-
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {
|
|
466
|
-
console.error(`${RED}⚠️ 错误: env key 非法: ${key}${NC}`);
|
|
467
|
-
process.exit(1);
|
|
468
|
-
}
|
|
469
|
-
if (/[\r\n\0]/.test(value) || /[;&|`$<>]/.test(value)) {
|
|
470
|
-
console.error(`${RED}⚠️ 错误: env value 含非法字符: ${key}${NC}`);
|
|
463
|
+
try {
|
|
464
|
+
return parseEnvEntryOrThrow(env);
|
|
465
|
+
} catch (e) {
|
|
466
|
+
const message = e && e.message ? e.message : String(e);
|
|
467
|
+
console.error(`${RED}⚠️ 错误: ${message}${NC}`);
|
|
471
468
|
process.exit(1);
|
|
472
469
|
}
|
|
473
|
-
return { key, value };
|
|
474
470
|
}
|
|
475
471
|
|
|
476
472
|
function normalizeJsonEnvMap(envConfig, sourceLabel) {
|
|
@@ -570,42 +566,6 @@ function addEnvFile(envFile) {
|
|
|
570
566
|
return addEnvFileTo(CONTAINER_ENVS, envFile);
|
|
571
567
|
}
|
|
572
568
|
|
|
573
|
-
function expandHomeAliasPath(filePath) {
|
|
574
|
-
const text = String(filePath || '').trim();
|
|
575
|
-
const homeDir = process.env.HOME || os.homedir();
|
|
576
|
-
|
|
577
|
-
if (text === '~') {
|
|
578
|
-
return homeDir;
|
|
579
|
-
}
|
|
580
|
-
if (text.startsWith('~/')) {
|
|
581
|
-
return path.join(homeDir, text.slice(2));
|
|
582
|
-
}
|
|
583
|
-
if (text === '$HOME') {
|
|
584
|
-
return homeDir;
|
|
585
|
-
}
|
|
586
|
-
if (text.startsWith('$HOME/')) {
|
|
587
|
-
return path.join(homeDir, text.slice('$HOME/'.length));
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
return text;
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
function normalizeVolume(volume) {
|
|
594
|
-
const text = String(volume || '').trim();
|
|
595
|
-
if (!text.startsWith('~') && !text.startsWith('$HOME')) {
|
|
596
|
-
return text;
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
const separatorIndex = text.indexOf(':');
|
|
600
|
-
if (separatorIndex === -1) {
|
|
601
|
-
return expandHomeAliasPath(text);
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
const hostPath = text.slice(0, separatorIndex);
|
|
605
|
-
const rest = text.slice(separatorIndex);
|
|
606
|
-
return `${expandHomeAliasPath(hostPath)}${rest}`;
|
|
607
|
-
}
|
|
608
|
-
|
|
609
569
|
function hasEnvKey(targetEnvs, key) {
|
|
610
570
|
for (let i = 0; i < targetEnvs.length; i += 2) {
|
|
611
571
|
if (targetEnvs[i] !== '--env') {
|
|
@@ -1004,11 +964,7 @@ function validateShellSuffixPassThroughArgs(command) {
|
|
|
1004
964
|
}
|
|
1005
965
|
}
|
|
1006
966
|
|
|
1007
|
-
function
|
|
1008
|
-
const includeRmOnExit = options.includeRmOnExit !== false;
|
|
1009
|
-
const includeServePreview = options.includeServePreview === true;
|
|
1010
|
-
const includeWebAuthOptions = options.includeWebAuthOptions === true;
|
|
1011
|
-
|
|
967
|
+
function applyContainerBaseOptions(command) {
|
|
1012
968
|
command
|
|
1013
969
|
.option('-r, --run <name>', '加载运行配置 (从 ~/.manyoyo/manyoyo.json 的 runs.<name> 读取)')
|
|
1014
970
|
.option('--hp, --host-path <path>', '设置宿主机工作目录 (默认: 当前路径)')
|
|
@@ -1023,35 +979,69 @@ function applyRunStyleOptions(command, options = {}) {
|
|
|
1023
979
|
appendArrayOption(command, '-v, --volume <volume>', '绑定挂载卷 XXX:YYY (可多次使用)');
|
|
1024
980
|
appendArrayOption(command, '-p, --port <port>', '设置端口映射 XXX:YYY (可多次使用)');
|
|
1025
981
|
|
|
982
|
+
return command;
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
function applyExecOptions(command) {
|
|
1026
986
|
command
|
|
1027
987
|
.option('--sp, --shell-prefix <command>', '主命令前缀 (常用于临时环境变量)')
|
|
1028
988
|
.option('-s, --shell <command>', '主命令')
|
|
1029
989
|
.option('--ss, --shell-suffix <command>', '主命令后缀 (追加到 -s 之后,等价于 -- <args>)')
|
|
1030
|
-
.option('--first-shell-prefix <command>', '首次预执行命令前缀 (仅新建容器生效; 容器已存在时忽略)')
|
|
1031
|
-
.option('--first-shell <command>', '首次预执行命令 (仅新建容器生效; 容器已存在时忽略)')
|
|
1032
|
-
.option('--first-shell-suffix <command>', '首次预执行命令后缀 (仅新建容器生效; 容器已存在时忽略)')
|
|
1033
990
|
.option('-x, --shell-full <command...>', '完整命令 (与 --sp/-s/--ss/-- 互斥)')
|
|
1034
991
|
.option('-y, --yolo <cli>', '使 AGENT 无需确认 (claude(c), gemini(gm), codex(cx), opencode(oc))');
|
|
992
|
+
|
|
993
|
+
return command;
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
function applyFirstExecOptions(command) {
|
|
997
|
+
command
|
|
998
|
+
.option('--first-shell-prefix <command>', '首次预执行命令前缀 (仅新建容器生效; 容器已存在时忽略)')
|
|
999
|
+
.option('--first-shell <command>', '首次预执行命令 (仅新建容器生效; 容器已存在时忽略)')
|
|
1000
|
+
.option('--first-shell-suffix <command>', '首次预执行命令后缀 (仅新建容器生效; 容器已存在时忽略)');
|
|
1035
1001
|
appendArrayOption(command, '--first-env <env>', '首次预执行环境变量 XXX=YYY (可多次使用)');
|
|
1036
1002
|
appendArrayOption(command, '--first-env-file <file>', '首次预执行环境变量文件 (仅支持绝对路径,如 /abs/path.env)');
|
|
1037
1003
|
|
|
1004
|
+
return command;
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
function applyQuietOptions(command) {
|
|
1008
|
+
appendArrayOption(command, '-q, --quiet <item>', '静默输出 (可多次使用: cnew, crm, tip, cmd, full)');
|
|
1009
|
+
return command;
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
function applyServeAuthOptions(command) {
|
|
1013
|
+
command
|
|
1014
|
+
.option('-U, --user <username>', '网页服务登录用户名 (默认 admin)')
|
|
1015
|
+
.option('-P, --pass <password>', '网页服务登录密码 (默认自动生成随机密码)');
|
|
1016
|
+
return command;
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
function applyRunStyleOptions(command, options = {}) {
|
|
1020
|
+
const includeRmOnExit = options.includeRmOnExit !== false;
|
|
1021
|
+
const includeServePreview = options.includeServePreview === true;
|
|
1022
|
+
const includeWebAuthOptions = options.includeWebAuthOptions === true;
|
|
1023
|
+
const includeFirstExecOptions = options.includeFirstExecOptions !== false;
|
|
1024
|
+
|
|
1025
|
+
applyContainerBaseOptions(command);
|
|
1026
|
+
applyExecOptions(command);
|
|
1027
|
+
if (includeFirstExecOptions) {
|
|
1028
|
+
applyFirstExecOptions(command);
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1038
1031
|
if (includeRmOnExit) {
|
|
1039
1032
|
command.option('--rm-on-exit', '退出后自动删除容器 (一次性模式)');
|
|
1040
1033
|
}
|
|
1041
1034
|
|
|
1042
|
-
|
|
1035
|
+
applyQuietOptions(command);
|
|
1043
1036
|
|
|
1044
1037
|
if (includeServePreview) {
|
|
1045
1038
|
command
|
|
1046
|
-
.option('--serve [listen]', '按 serve 模式解析配置 (仅支持 <ip:port>)')
|
|
1047
|
-
|
|
1048
|
-
.option('-P, --pass <password>', '网页服务登录密码 (默认自动生成随机密码)');
|
|
1039
|
+
.option('--serve [listen]', '按 serve 模式解析配置 (仅支持 <ip:port>)');
|
|
1040
|
+
applyServeAuthOptions(command);
|
|
1049
1041
|
}
|
|
1050
1042
|
|
|
1051
1043
|
if (includeWebAuthOptions) {
|
|
1052
|
-
command
|
|
1053
|
-
.option('-U, --user <username>', '网页服务登录用户名 (默认 admin)')
|
|
1054
|
-
.option('-P, --pass <password>', '网页服务登录密码 (默认自动生成随机密码)');
|
|
1044
|
+
applyServeAuthOptions(command);
|
|
1055
1045
|
}
|
|
1056
1046
|
|
|
1057
1047
|
return command;
|
|
@@ -1214,7 +1204,11 @@ Notes:
|
|
|
1214
1204
|
.action(() => selectAction('images', { imageList: true }));
|
|
1215
1205
|
|
|
1216
1206
|
const serveCommand = program.command('serve [listen]').description('启动网页交互服务 (默认 127.0.0.1:3000)');
|
|
1217
|
-
applyRunStyleOptions(serveCommand, {
|
|
1207
|
+
applyRunStyleOptions(serveCommand, {
|
|
1208
|
+
includeRmOnExit: false,
|
|
1209
|
+
includeWebAuthOptions: true,
|
|
1210
|
+
includeFirstExecOptions: false
|
|
1211
|
+
});
|
|
1218
1212
|
serveCommand.option('-d, --detach', '后台启动网页服务并立即返回');
|
|
1219
1213
|
serveCommand.option('--stop', '停止后台网页服务;必须显式传入 listen');
|
|
1220
1214
|
serveCommand.action((listen, options) => {
|
|
@@ -1260,7 +1254,10 @@ Notes:
|
|
|
1260
1254
|
});
|
|
1261
1255
|
|
|
1262
1256
|
const configRunCommand = configCommand.command('command').description('显示将执行的 docker run 命令并退出');
|
|
1263
|
-
applyRunStyleOptions(configRunCommand, {
|
|
1257
|
+
applyRunStyleOptions(configRunCommand, {
|
|
1258
|
+
includeRmOnExit: false,
|
|
1259
|
+
includeFirstExecOptions: false
|
|
1260
|
+
});
|
|
1264
1261
|
enableShellSuffixPassThrough(configRunCommand);
|
|
1265
1262
|
configRunCommand.action((options, command) => {
|
|
1266
1263
|
validateShellSuffixPassThroughArgs(command);
|
|
@@ -1367,145 +1364,96 @@ Notes:
|
|
|
1367
1364
|
const globalFirstConfig = normalizeFirstConfig(config.first, '全局配置');
|
|
1368
1365
|
const runFirstConfig = normalizeFirstConfig(runConfig.first, '运行配置');
|
|
1369
1366
|
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
if (mergedFirstShellSuffix) {
|
|
1409
|
-
FIRST_EXEC_COMMAND_SUFFIX = normalizeCommandSuffix(mergedFirstShellSuffix);
|
|
1410
|
-
}
|
|
1367
|
+
const resolvedRuntime = resolveRuntimeConfig({
|
|
1368
|
+
cliOptions: options,
|
|
1369
|
+
globalConfig: config,
|
|
1370
|
+
runConfig,
|
|
1371
|
+
globalFirstConfig,
|
|
1372
|
+
runFirstConfig,
|
|
1373
|
+
defaults: {
|
|
1374
|
+
hostPath: HOST_PATH,
|
|
1375
|
+
containerName: CONTAINER_NAME,
|
|
1376
|
+
containerPath: CONTAINER_PATH,
|
|
1377
|
+
imageName: IMAGE_NAME,
|
|
1378
|
+
imageVersion: IMAGE_VERSION
|
|
1379
|
+
},
|
|
1380
|
+
envVars: process.env,
|
|
1381
|
+
argv: process.argv,
|
|
1382
|
+
isServerMode,
|
|
1383
|
+
isServerStopMode,
|
|
1384
|
+
pickConfigValue,
|
|
1385
|
+
resolveContainerNameTemplate,
|
|
1386
|
+
normalizeCommandSuffix,
|
|
1387
|
+
normalizeJsonEnvMap,
|
|
1388
|
+
normalizeCliEnvMap,
|
|
1389
|
+
mergeArrayConfig,
|
|
1390
|
+
normalizeVolume,
|
|
1391
|
+
parseServerListen
|
|
1392
|
+
});
|
|
1393
|
+
|
|
1394
|
+
HOST_PATH = resolvedRuntime.hostPath;
|
|
1395
|
+
CONTAINER_NAME = resolvedRuntime.containerName;
|
|
1396
|
+
CONTAINER_PATH = resolvedRuntime.containerPath;
|
|
1397
|
+
IMAGE_NAME = resolvedRuntime.imageName;
|
|
1398
|
+
IMAGE_VERSION = resolvedRuntime.imageVersion;
|
|
1399
|
+
EXEC_COMMAND_PREFIX = resolvedRuntime.exec.prefix;
|
|
1400
|
+
EXEC_COMMAND = resolvedRuntime.exec.shell;
|
|
1401
|
+
EXEC_COMMAND_SUFFIX = resolvedRuntime.exec.suffix;
|
|
1402
|
+
FIRST_EXEC_COMMAND_PREFIX = resolvedRuntime.first.exec.prefix;
|
|
1403
|
+
FIRST_EXEC_COMMAND = resolvedRuntime.first.exec.shell;
|
|
1404
|
+
FIRST_EXEC_COMMAND_SUFFIX = resolvedRuntime.first.exec.suffix;
|
|
1411
1405
|
|
|
1412
1406
|
// Basic name validation to reduce injection risk
|
|
1413
1407
|
validateName('containerName', CONTAINER_NAME, SAFE_CONTAINER_NAME_PATTERN);
|
|
1414
1408
|
validateName('imageName', IMAGE_NAME, /^[A-Za-z0-9][A-Za-z0-9._/:-]*$/);
|
|
1415
1409
|
validateImageVersion(IMAGE_VERSION);
|
|
1416
1410
|
|
|
1417
|
-
|
|
1418
|
-
const toArray = (val) => Array.isArray(val) ? val : (val ? [val] : []);
|
|
1419
|
-
const envFileList = [
|
|
1420
|
-
...toArray(config.envFile),
|
|
1421
|
-
...toArray(runConfig.envFile),
|
|
1422
|
-
...(options.envFile || [])
|
|
1423
|
-
].filter(Boolean);
|
|
1411
|
+
const envFileList = resolvedRuntime.envFile;
|
|
1424
1412
|
envFileList.forEach(ef => addEnvFile(ef));
|
|
1425
1413
|
|
|
1426
1414
|
// env in JSON config uses map type, and is merged by key with CLI priority.
|
|
1427
|
-
const envMap =
|
|
1428
|
-
...normalizeJsonEnvMap(config.env, '全局配置'),
|
|
1429
|
-
...normalizeJsonEnvMap(runConfig.env, '运行配置'),
|
|
1430
|
-
...normalizeCliEnvMap(options.env)
|
|
1431
|
-
};
|
|
1415
|
+
const envMap = resolvedRuntime.env;
|
|
1432
1416
|
Object.entries(envMap).forEach(([key, value]) => addEnv(`${key}=${value}`));
|
|
1433
1417
|
|
|
1434
|
-
const firstEnvFileList =
|
|
1435
|
-
...toArray(globalFirstConfig.envFile),
|
|
1436
|
-
...toArray(runFirstConfig.envFile),
|
|
1437
|
-
...(options.firstEnvFile || [])
|
|
1438
|
-
].filter(Boolean);
|
|
1418
|
+
const firstEnvFileList = resolvedRuntime.first.envFile;
|
|
1439
1419
|
firstEnvFileList.forEach(ef => addEnvFileTo(FIRST_CONTAINER_ENVS, ef));
|
|
1440
1420
|
|
|
1441
|
-
const firstEnvMap =
|
|
1442
|
-
...normalizeJsonEnvMap(globalFirstConfig.env, '全局配置 first'),
|
|
1443
|
-
...normalizeJsonEnvMap(runFirstConfig.env, '运行配置 first'),
|
|
1444
|
-
...normalizeCliEnvMap(options.firstEnv)
|
|
1445
|
-
};
|
|
1421
|
+
const firstEnvMap = resolvedRuntime.first.env;
|
|
1446
1422
|
Object.entries(firstEnvMap).forEach(([key, value]) => addEnvTo(FIRST_CONTAINER_ENVS, `${key}=${value}`));
|
|
1447
1423
|
|
|
1448
1424
|
applyPlaywrightCliSessionIntegration(config, runConfig);
|
|
1449
1425
|
|
|
1450
|
-
const volumeList =
|
|
1451
|
-
.map(normalizeVolume);
|
|
1426
|
+
const volumeList = resolvedRuntime.volumes;
|
|
1452
1427
|
volumeList.forEach(v => addVolume(v));
|
|
1453
1428
|
|
|
1454
|
-
const portList =
|
|
1429
|
+
const portList = resolvedRuntime.ports;
|
|
1455
1430
|
portList.forEach(p => addPort(p));
|
|
1456
1431
|
|
|
1457
|
-
const buildArgList =
|
|
1432
|
+
const buildArgList = resolvedRuntime.imageBuildArgs;
|
|
1458
1433
|
buildArgList.forEach(arg => addImageBuildArg(arg));
|
|
1459
1434
|
|
|
1460
1435
|
// Override mode for special options
|
|
1461
|
-
const yoloValue =
|
|
1436
|
+
const yoloValue = resolvedRuntime.yolo;
|
|
1462
1437
|
if (yoloValue) setYolo(yoloValue);
|
|
1463
1438
|
|
|
1464
|
-
const contModeValue =
|
|
1439
|
+
const contModeValue = resolvedRuntime.containerMode;
|
|
1465
1440
|
if (contModeValue) setContMode(contModeValue);
|
|
1466
1441
|
|
|
1467
|
-
const quietValue =
|
|
1442
|
+
const quietValue = resolvedRuntime.quiet;
|
|
1468
1443
|
if (quietValue) setQuiet(quietValue);
|
|
1469
1444
|
|
|
1470
|
-
// Handle shell-full (variadic arguments)
|
|
1471
|
-
if (options.shellFull) {
|
|
1472
|
-
EXEC_COMMAND = options.shellFull.join(' ');
|
|
1473
|
-
EXEC_COMMAND_PREFIX = "";
|
|
1474
|
-
EXEC_COMMAND_SUFFIX = "";
|
|
1475
|
-
}
|
|
1476
|
-
|
|
1477
|
-
// Handle -- suffix arguments
|
|
1478
|
-
if (!options.shellFull) {
|
|
1479
|
-
const doubleDashIndex = process.argv.indexOf('--');
|
|
1480
|
-
if (doubleDashIndex !== -1 && doubleDashIndex < process.argv.length - 1) {
|
|
1481
|
-
EXEC_COMMAND_SUFFIX = normalizeCommandSuffix(process.argv.slice(doubleDashIndex + 1).join(' '));
|
|
1482
|
-
}
|
|
1483
|
-
}
|
|
1484
|
-
|
|
1485
1445
|
if (options.rmOnExit) {
|
|
1486
1446
|
RM_ON_EXIT = true;
|
|
1487
1447
|
}
|
|
1488
1448
|
|
|
1489
1449
|
if (isServerMode) {
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
SERVER_PORT = serverListen.port;
|
|
1450
|
+
SERVER_HOST = resolvedRuntime.serverHost;
|
|
1451
|
+
SERVER_PORT = resolvedRuntime.serverPort;
|
|
1493
1452
|
}
|
|
1494
1453
|
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
}
|
|
1499
|
-
|
|
1500
|
-
const serverPassValue = pickConfigValue(options.serverPass, runConfig.serverPass, config.serverPass, process.env.MANYOYO_SERVER_PASS);
|
|
1501
|
-
if (serverPassValue) {
|
|
1502
|
-
SERVER_AUTH_PASS = String(serverPassValue);
|
|
1503
|
-
SERVER_AUTH_PASS_AUTO = false;
|
|
1504
|
-
}
|
|
1505
|
-
|
|
1506
|
-
if (isServerMode && !isServerStopMode) {
|
|
1507
|
-
ensureWebServerAuthCredentials();
|
|
1508
|
-
}
|
|
1454
|
+
SERVER_AUTH_USER = resolvedRuntime.serverUser || '';
|
|
1455
|
+
SERVER_AUTH_PASS = resolvedRuntime.serverPass || '';
|
|
1456
|
+
SERVER_AUTH_PASS_AUTO = resolvedRuntime.serverPassAuto === true;
|
|
1509
1457
|
|
|
1510
1458
|
if (isShowConfigMode) {
|
|
1511
1459
|
const finalConfig = {
|
|
@@ -1525,9 +1473,9 @@ Notes:
|
|
|
1525
1473
|
shellSuffix: EXEC_COMMAND_SUFFIX || "",
|
|
1526
1474
|
yolo: yoloValue || "",
|
|
1527
1475
|
quiet: quietValue || [],
|
|
1528
|
-
server:
|
|
1529
|
-
serverHost:
|
|
1530
|
-
serverPort:
|
|
1476
|
+
server: resolvedRuntime.server,
|
|
1477
|
+
serverHost: resolvedRuntime.server ? SERVER_HOST : null,
|
|
1478
|
+
serverPort: resolvedRuntime.server ? SERVER_PORT : null,
|
|
1531
1479
|
serverUser: SERVER_AUTH_USER || "",
|
|
1532
1480
|
serverPass: SERVER_AUTH_PASS || "",
|
|
1533
1481
|
exec: {
|
package/lib/global-config.js
CHANGED
|
@@ -4,6 +4,7 @@ const fs = require('fs');
|
|
|
4
4
|
const os = require('os');
|
|
5
5
|
const path = require('path');
|
|
6
6
|
const JSON5 = require('json5');
|
|
7
|
+
const { findTopLevelPropertyValueRange } = require('./json5-text-edit');
|
|
7
8
|
|
|
8
9
|
function getManyoyoConfigPath(homeDir = os.homedir()) {
|
|
9
10
|
return path.join(homeDir, '.manyoyo', 'manyoyo.json');
|
|
@@ -36,204 +37,6 @@ function readManyoyoConfig(homeDir = os.homedir()) {
|
|
|
36
37
|
}
|
|
37
38
|
}
|
|
38
39
|
|
|
39
|
-
function readQuotedString(text, startIndex) {
|
|
40
|
-
const quote = text[startIndex];
|
|
41
|
-
let value = '';
|
|
42
|
-
|
|
43
|
-
for (let i = startIndex + 1; i < text.length; i += 1) {
|
|
44
|
-
const ch = text[i];
|
|
45
|
-
if (ch === '\\') {
|
|
46
|
-
value += ch;
|
|
47
|
-
if (i + 1 < text.length) {
|
|
48
|
-
value += text[i + 1];
|
|
49
|
-
i += 1;
|
|
50
|
-
}
|
|
51
|
-
continue;
|
|
52
|
-
}
|
|
53
|
-
if (ch === quote) {
|
|
54
|
-
return {
|
|
55
|
-
value,
|
|
56
|
-
end: i + 1
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
value += ch;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
return null;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function isIdentifierStart(ch) {
|
|
66
|
-
return /[A-Za-z_$]/.test(ch);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function isIdentifierPart(ch) {
|
|
70
|
-
return /[A-Za-z0-9_$]/.test(ch);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function skipWhitespace(text, index) {
|
|
74
|
-
let i = index;
|
|
75
|
-
while (i < text.length && /\s/.test(text[i])) {
|
|
76
|
-
i += 1;
|
|
77
|
-
}
|
|
78
|
-
return i;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function findTopLevelPropertyValueRange(text, propertyName) {
|
|
82
|
-
let depth = 0;
|
|
83
|
-
let inString = '';
|
|
84
|
-
let inLineComment = false;
|
|
85
|
-
let inBlockComment = false;
|
|
86
|
-
|
|
87
|
-
for (let i = 0; i < text.length; i += 1) {
|
|
88
|
-
const ch = text[i];
|
|
89
|
-
const next = text[i + 1];
|
|
90
|
-
|
|
91
|
-
if (inLineComment) {
|
|
92
|
-
if (ch === '\n') inLineComment = false;
|
|
93
|
-
continue;
|
|
94
|
-
}
|
|
95
|
-
if (inBlockComment) {
|
|
96
|
-
if (ch === '*' && next === '/') {
|
|
97
|
-
inBlockComment = false;
|
|
98
|
-
i += 1;
|
|
99
|
-
}
|
|
100
|
-
continue;
|
|
101
|
-
}
|
|
102
|
-
if (inString) {
|
|
103
|
-
if (ch === '\\') {
|
|
104
|
-
i += 1;
|
|
105
|
-
continue;
|
|
106
|
-
}
|
|
107
|
-
if (ch === inString) inString = '';
|
|
108
|
-
continue;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (ch === '/' && next === '/') {
|
|
112
|
-
inLineComment = true;
|
|
113
|
-
i += 1;
|
|
114
|
-
continue;
|
|
115
|
-
}
|
|
116
|
-
if (ch === '/' && next === '*') {
|
|
117
|
-
inBlockComment = true;
|
|
118
|
-
i += 1;
|
|
119
|
-
continue;
|
|
120
|
-
}
|
|
121
|
-
if (depth === 1 && !/\s|,/.test(ch)) {
|
|
122
|
-
let property = '';
|
|
123
|
-
let cursor = i;
|
|
124
|
-
if (ch === '"' || ch === '\'') {
|
|
125
|
-
const token = readQuotedString(text, i);
|
|
126
|
-
if (!token) {
|
|
127
|
-
return null;
|
|
128
|
-
}
|
|
129
|
-
property = token.value;
|
|
130
|
-
cursor = token.end;
|
|
131
|
-
} else if (isIdentifierStart(ch)) {
|
|
132
|
-
cursor = i + 1;
|
|
133
|
-
while (cursor < text.length && isIdentifierPart(text[cursor])) {
|
|
134
|
-
cursor += 1;
|
|
135
|
-
}
|
|
136
|
-
property = text.slice(i, cursor);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
if (property) {
|
|
140
|
-
const colonIndex = skipWhitespace(text, cursor);
|
|
141
|
-
if (text[colonIndex] === ':') {
|
|
142
|
-
if (property !== propertyName) {
|
|
143
|
-
i = colonIndex;
|
|
144
|
-
continue;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
let valueStart = skipWhitespace(text, colonIndex + 1);
|
|
148
|
-
let valueEnd = valueStart;
|
|
149
|
-
let valueString = '';
|
|
150
|
-
let valueLineComment = false;
|
|
151
|
-
let valueBlockComment = false;
|
|
152
|
-
let valueDepth = 0;
|
|
153
|
-
|
|
154
|
-
for (; valueEnd < text.length; valueEnd += 1) {
|
|
155
|
-
const valueCh = text[valueEnd];
|
|
156
|
-
const valueNext = text[valueEnd + 1];
|
|
157
|
-
|
|
158
|
-
if (valueLineComment) {
|
|
159
|
-
if (valueCh === '\n') valueLineComment = false;
|
|
160
|
-
continue;
|
|
161
|
-
}
|
|
162
|
-
if (valueBlockComment) {
|
|
163
|
-
if (valueCh === '*' && valueNext === '/') {
|
|
164
|
-
valueBlockComment = false;
|
|
165
|
-
valueEnd += 1;
|
|
166
|
-
}
|
|
167
|
-
continue;
|
|
168
|
-
}
|
|
169
|
-
if (valueString) {
|
|
170
|
-
if (valueCh === '\\') {
|
|
171
|
-
valueEnd += 1;
|
|
172
|
-
continue;
|
|
173
|
-
}
|
|
174
|
-
if (valueCh === valueString) valueString = '';
|
|
175
|
-
continue;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
if (valueCh === '/' && valueNext === '/') {
|
|
179
|
-
valueLineComment = true;
|
|
180
|
-
valueEnd += 1;
|
|
181
|
-
continue;
|
|
182
|
-
}
|
|
183
|
-
if (valueCh === '/' && valueNext === '*') {
|
|
184
|
-
valueBlockComment = true;
|
|
185
|
-
valueEnd += 1;
|
|
186
|
-
continue;
|
|
187
|
-
}
|
|
188
|
-
if (valueCh === '"' || valueCh === '\'') {
|
|
189
|
-
valueString = valueCh;
|
|
190
|
-
continue;
|
|
191
|
-
}
|
|
192
|
-
if (valueCh === '{' || valueCh === '[' || valueCh === '(') {
|
|
193
|
-
valueDepth += 1;
|
|
194
|
-
continue;
|
|
195
|
-
}
|
|
196
|
-
if (valueCh === '}' || valueCh === ']' || valueCh === ')') {
|
|
197
|
-
if (valueDepth === 0) {
|
|
198
|
-
break;
|
|
199
|
-
}
|
|
200
|
-
valueDepth -= 1;
|
|
201
|
-
continue;
|
|
202
|
-
}
|
|
203
|
-
if (valueDepth === 0 && valueCh === ',') {
|
|
204
|
-
break;
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
while (valueEnd > valueStart && /\s/.test(text[valueEnd - 1])) {
|
|
209
|
-
valueEnd -= 1;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
return {
|
|
213
|
-
start: valueStart,
|
|
214
|
-
end: valueEnd
|
|
215
|
-
};
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
if (ch === '"' || ch === '\'') {
|
|
221
|
-
inString = ch;
|
|
222
|
-
continue;
|
|
223
|
-
}
|
|
224
|
-
if (ch === '{' || ch === '[') {
|
|
225
|
-
depth += 1;
|
|
226
|
-
continue;
|
|
227
|
-
}
|
|
228
|
-
if (ch === '}' || ch === ']') {
|
|
229
|
-
depth -= 1;
|
|
230
|
-
continue;
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
return null;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
40
|
function insertTopLevelImageVersion(text, imageVersion) {
|
|
238
41
|
const openBraceIndex = text.indexOf('{');
|
|
239
42
|
if (openBraceIndex === -1) {
|
package/lib/image-build.js
CHANGED
|
@@ -230,15 +230,31 @@ function prepareGoplsBuildCache(ctx, cache, imageTool, arch) {
|
|
|
230
230
|
}
|
|
231
231
|
}
|
|
232
232
|
|
|
233
|
+
function createBuildCacheArtifacts(ctx, cache, imageTool, archInfo) {
|
|
234
|
+
return [
|
|
235
|
+
{
|
|
236
|
+
name: 'node',
|
|
237
|
+
prepare: () => prepareNodeBuildCache(ctx, cache, archInfo.archNode)
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
name: 'jdtls',
|
|
241
|
+
prepare: () => prepareJdtlsBuildCache(ctx, cache, imageTool)
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
name: 'gopls',
|
|
245
|
+
prepare: () => prepareGoplsBuildCache(ctx, cache, imageTool, archInfo.arch)
|
|
246
|
+
}
|
|
247
|
+
];
|
|
248
|
+
}
|
|
249
|
+
|
|
233
250
|
async function prepareBuildCache(ctx, imageTool) {
|
|
234
251
|
const { CYAN, GREEN, NC } = ctx.colors;
|
|
235
252
|
const cache = createBuildCacheContext(ctx);
|
|
236
|
-
const
|
|
253
|
+
const archInfo = resolveBuildCacheArch();
|
|
254
|
+
const artifacts = createBuildCacheArtifacts(ctx, cache, imageTool, archInfo);
|
|
237
255
|
|
|
238
256
|
ctx.log(`\n${CYAN}准备构建缓存...${NC}`);
|
|
239
|
-
|
|
240
|
-
prepareJdtlsBuildCache(ctx, cache, imageTool);
|
|
241
|
-
prepareGoplsBuildCache(ctx, cache, imageTool, arch);
|
|
257
|
+
artifacts.forEach(artifact => artifact.prepare());
|
|
242
258
|
saveBuildCacheTimestamps(cache.timestampFile, cache.timestamps);
|
|
243
259
|
ctx.log(`${GREEN}✅ 构建缓存准备完成${NC}\n`);
|
|
244
260
|
}
|