@xcanwin/manyoyo 5.8.6 → 5.8.10
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 +63 -157
- 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/runtime-normalizers.js +65 -0
- package/lib/runtime-resolver.js +195 -0
- package/lib/web/server.js +76 -315
- package/package.json +1 -1
package/bin/manyoyo.js
CHANGED
|
@@ -16,6 +16,11 @@ 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
|
+
normalizeVolume
|
|
23
|
+
} = require('../lib/runtime-normalizers');
|
|
19
24
|
const {
|
|
20
25
|
sanitizeSensitiveData,
|
|
21
26
|
sanitizeServeLogText,
|
|
@@ -454,23 +459,13 @@ async function askQuestion(prompt) {
|
|
|
454
459
|
* @param {string} env - 环境变量字符串 (KEY=VALUE)
|
|
455
460
|
*/
|
|
456
461
|
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}`);
|
|
462
|
+
try {
|
|
463
|
+
return parseEnvEntryOrThrow(env);
|
|
464
|
+
} catch (e) {
|
|
465
|
+
const message = e && e.message ? e.message : String(e);
|
|
466
|
+
console.error(`${RED}⚠️ 错误: ${message}${NC}`);
|
|
471
467
|
process.exit(1);
|
|
472
468
|
}
|
|
473
|
-
return { key, value };
|
|
474
469
|
}
|
|
475
470
|
|
|
476
471
|
function normalizeJsonEnvMap(envConfig, sourceLabel) {
|
|
@@ -570,42 +565,6 @@ function addEnvFile(envFile) {
|
|
|
570
565
|
return addEnvFileTo(CONTAINER_ENVS, envFile);
|
|
571
566
|
}
|
|
572
567
|
|
|
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
568
|
function hasEnvKey(targetEnvs, key) {
|
|
610
569
|
for (let i = 0; i < targetEnvs.length; i += 2) {
|
|
611
570
|
if (targetEnvs[i] !== '--env') {
|
|
@@ -1367,47 +1326,44 @@ Notes:
|
|
|
1367
1326
|
const globalFirstConfig = normalizeFirstConfig(config.first, '全局配置');
|
|
1368
1327
|
const runFirstConfig = normalizeFirstConfig(runConfig.first, '运行配置');
|
|
1369
1328
|
|
|
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
|
-
}
|
|
1329
|
+
const resolvedRuntime = resolveRuntimeConfig({
|
|
1330
|
+
cliOptions: options,
|
|
1331
|
+
globalConfig: config,
|
|
1332
|
+
runConfig,
|
|
1333
|
+
globalFirstConfig,
|
|
1334
|
+
runFirstConfig,
|
|
1335
|
+
defaults: {
|
|
1336
|
+
hostPath: HOST_PATH,
|
|
1337
|
+
containerName: CONTAINER_NAME,
|
|
1338
|
+
containerPath: CONTAINER_PATH,
|
|
1339
|
+
imageName: IMAGE_NAME,
|
|
1340
|
+
imageVersion: IMAGE_VERSION
|
|
1341
|
+
},
|
|
1342
|
+
envVars: process.env,
|
|
1343
|
+
argv: process.argv,
|
|
1344
|
+
isServerMode,
|
|
1345
|
+
isServerStopMode,
|
|
1346
|
+
pickConfigValue,
|
|
1347
|
+
resolveContainerNameTemplate,
|
|
1348
|
+
normalizeCommandSuffix,
|
|
1349
|
+
normalizeJsonEnvMap,
|
|
1350
|
+
normalizeCliEnvMap,
|
|
1351
|
+
mergeArrayConfig,
|
|
1352
|
+
normalizeVolume,
|
|
1353
|
+
parseServerListen
|
|
1354
|
+
});
|
|
1355
|
+
|
|
1356
|
+
HOST_PATH = resolvedRuntime.hostPath;
|
|
1357
|
+
CONTAINER_NAME = resolvedRuntime.containerName;
|
|
1358
|
+
CONTAINER_PATH = resolvedRuntime.containerPath;
|
|
1359
|
+
IMAGE_NAME = resolvedRuntime.imageName;
|
|
1360
|
+
IMAGE_VERSION = resolvedRuntime.imageVersion;
|
|
1361
|
+
EXEC_COMMAND_PREFIX = resolvedRuntime.exec.prefix;
|
|
1362
|
+
EXEC_COMMAND = resolvedRuntime.exec.shell;
|
|
1363
|
+
EXEC_COMMAND_SUFFIX = resolvedRuntime.exec.suffix;
|
|
1364
|
+
FIRST_EXEC_COMMAND_PREFIX = resolvedRuntime.first.exec.prefix;
|
|
1365
|
+
FIRST_EXEC_COMMAND = resolvedRuntime.first.exec.shell;
|
|
1366
|
+
FIRST_EXEC_COMMAND_SUFFIX = resolvedRuntime.first.exec.suffix;
|
|
1411
1367
|
|
|
1412
1368
|
// Basic name validation to reduce injection risk
|
|
1413
1369
|
validateName('containerName', CONTAINER_NAME, SAFE_CONTAINER_NAME_PATTERN);
|
|
@@ -1415,97 +1371,47 @@ Notes:
|
|
|
1415
1371
|
validateImageVersion(IMAGE_VERSION);
|
|
1416
1372
|
|
|
1417
1373
|
// Merge mode (array values): concatenate all sources
|
|
1418
|
-
const
|
|
1419
|
-
const envFileList = [
|
|
1420
|
-
...toArray(config.envFile),
|
|
1421
|
-
...toArray(runConfig.envFile),
|
|
1422
|
-
...(options.envFile || [])
|
|
1423
|
-
].filter(Boolean);
|
|
1374
|
+
const envFileList = resolvedRuntime.envFile;
|
|
1424
1375
|
envFileList.forEach(ef => addEnvFile(ef));
|
|
1425
1376
|
|
|
1426
|
-
|
|
1427
|
-
const envMap = {
|
|
1428
|
-
...normalizeJsonEnvMap(config.env, '全局配置'),
|
|
1429
|
-
...normalizeJsonEnvMap(runConfig.env, '运行配置'),
|
|
1430
|
-
...normalizeCliEnvMap(options.env)
|
|
1431
|
-
};
|
|
1377
|
+
const envMap = resolvedRuntime.env;
|
|
1432
1378
|
Object.entries(envMap).forEach(([key, value]) => addEnv(`${key}=${value}`));
|
|
1433
1379
|
|
|
1434
|
-
const firstEnvFileList =
|
|
1435
|
-
...toArray(globalFirstConfig.envFile),
|
|
1436
|
-
...toArray(runFirstConfig.envFile),
|
|
1437
|
-
...(options.firstEnvFile || [])
|
|
1438
|
-
].filter(Boolean);
|
|
1380
|
+
const firstEnvFileList = resolvedRuntime.first.envFile;
|
|
1439
1381
|
firstEnvFileList.forEach(ef => addEnvFileTo(FIRST_CONTAINER_ENVS, ef));
|
|
1440
1382
|
|
|
1441
|
-
const firstEnvMap =
|
|
1442
|
-
...normalizeJsonEnvMap(globalFirstConfig.env, '全局配置 first'),
|
|
1443
|
-
...normalizeJsonEnvMap(runFirstConfig.env, '运行配置 first'),
|
|
1444
|
-
...normalizeCliEnvMap(options.firstEnv)
|
|
1445
|
-
};
|
|
1383
|
+
const firstEnvMap = resolvedRuntime.first.env;
|
|
1446
1384
|
Object.entries(firstEnvMap).forEach(([key, value]) => addEnvTo(FIRST_CONTAINER_ENVS, `${key}=${value}`));
|
|
1447
1385
|
|
|
1448
1386
|
applyPlaywrightCliSessionIntegration(config, runConfig);
|
|
1449
1387
|
|
|
1450
|
-
const volumeList =
|
|
1451
|
-
.map(normalizeVolume);
|
|
1388
|
+
const volumeList = resolvedRuntime.volumes;
|
|
1452
1389
|
volumeList.forEach(v => addVolume(v));
|
|
1453
1390
|
|
|
1454
|
-
const portList =
|
|
1391
|
+
const portList = resolvedRuntime.ports;
|
|
1455
1392
|
portList.forEach(p => addPort(p));
|
|
1456
1393
|
|
|
1457
|
-
const buildArgList =
|
|
1394
|
+
const buildArgList = resolvedRuntime.imageBuildArgs;
|
|
1458
1395
|
buildArgList.forEach(arg => addImageBuildArg(arg));
|
|
1459
1396
|
|
|
1460
|
-
|
|
1461
|
-
const yoloValue = pickConfigValue(options.yolo, runConfig.yolo, config.yolo);
|
|
1397
|
+
const yoloValue = resolvedRuntime.yolo;
|
|
1462
1398
|
if (yoloValue) setYolo(yoloValue);
|
|
1463
1399
|
|
|
1464
|
-
const contModeValue =
|
|
1400
|
+
const contModeValue = resolvedRuntime.containerMode;
|
|
1465
1401
|
if (contModeValue) setContMode(contModeValue);
|
|
1466
1402
|
|
|
1467
|
-
const quietValue =
|
|
1403
|
+
const quietValue = resolvedRuntime.quiet;
|
|
1468
1404
|
if (quietValue) setQuiet(quietValue);
|
|
1469
1405
|
|
|
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
1406
|
if (options.rmOnExit) {
|
|
1486
1407
|
RM_ON_EXIT = true;
|
|
1487
1408
|
}
|
|
1488
1409
|
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
const serverUserValue = pickConfigValue(options.serverUser, runConfig.serverUser, config.serverUser, process.env.MANYOYO_SERVER_USER);
|
|
1496
|
-
if (serverUserValue) {
|
|
1497
|
-
SERVER_AUTH_USER = String(serverUserValue);
|
|
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
|
-
}
|
|
1410
|
+
SERVER_HOST = resolvedRuntime.serverHost || SERVER_HOST;
|
|
1411
|
+
SERVER_PORT = resolvedRuntime.serverPort || SERVER_PORT;
|
|
1412
|
+
SERVER_AUTH_USER = resolvedRuntime.serverUser || '';
|
|
1413
|
+
SERVER_AUTH_PASS = resolvedRuntime.serverPass || '';
|
|
1414
|
+
SERVER_AUTH_PASS_AUTO = Boolean(resolvedRuntime.serverPassAuto);
|
|
1509
1415
|
|
|
1510
1416
|
if (isShowConfigMode) {
|
|
1511
1417
|
const finalConfig = {
|
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
|
}
|
package/lib/init-config.js
CHANGED
|
@@ -251,6 +251,25 @@ function collectOpenCodeInitData(homeDir, ctx) {
|
|
|
251
251
|
return { keys, values, notes, volumes: dedupeList(volumes) };
|
|
252
252
|
}
|
|
253
253
|
|
|
254
|
+
const AGENT_INIT_SPECS = {
|
|
255
|
+
claude: {
|
|
256
|
+
yolo: 'c',
|
|
257
|
+
collect: (homeDir, ctx) => collectClaudeInitData(homeDir, ctx)
|
|
258
|
+
},
|
|
259
|
+
codex: {
|
|
260
|
+
yolo: 'cx',
|
|
261
|
+
collect: (homeDir, ctx) => collectCodexInitData(homeDir, ctx)
|
|
262
|
+
},
|
|
263
|
+
gemini: {
|
|
264
|
+
yolo: 'gm',
|
|
265
|
+
collect: (homeDir) => collectGeminiInitData(homeDir)
|
|
266
|
+
},
|
|
267
|
+
opencode: {
|
|
268
|
+
yolo: 'oc',
|
|
269
|
+
collect: (homeDir, ctx) => collectOpenCodeInitData(homeDir, ctx)
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
|
|
254
273
|
function buildInitRunEnv(keys, values) {
|
|
255
274
|
const envMap = {};
|
|
256
275
|
const missingKeys = [];
|
|
@@ -338,24 +357,17 @@ async function initAgentConfigs(rawAgents, options = {}) {
|
|
|
338
357
|
runsMap = { ...manyoyoConfig.runs };
|
|
339
358
|
}
|
|
340
359
|
|
|
341
|
-
const extractors = {
|
|
342
|
-
claude: homeDir => collectClaudeInitData(homeDir, ctx),
|
|
343
|
-
codex: homeDir => collectCodexInitData(homeDir, ctx),
|
|
344
|
-
gemini: collectGeminiInitData,
|
|
345
|
-
opencode: homeDir => collectOpenCodeInitData(homeDir, ctx)
|
|
346
|
-
};
|
|
347
|
-
const yoloMap = { claude: 'c', codex: 'cx', gemini: 'gm', opencode: 'oc' };
|
|
348
|
-
|
|
349
360
|
let hasConfigChanged = false;
|
|
350
361
|
ctx.log(`${CYAN}🧭 正在初始化 MANYOYO 配置: ${agents.join(', ')}${NC}`);
|
|
351
362
|
|
|
352
363
|
for (const agent of agents) {
|
|
353
|
-
const
|
|
364
|
+
const spec = AGENT_INIT_SPECS[agent];
|
|
365
|
+
const data = spec.collect(ctx.homeDir, ctx);
|
|
354
366
|
const shouldWriteRun = await shouldOverwriteInitRunEntry(agent, Object.prototype.hasOwnProperty.call(runsMap, agent), ctx);
|
|
355
367
|
|
|
356
368
|
let writeResult = { missingKeys: [], unsafeKeys: [] };
|
|
357
369
|
if (shouldWriteRun) {
|
|
358
|
-
const buildResult = buildInitRunProfile(agent,
|
|
370
|
+
const buildResult = buildInitRunProfile(agent, spec.yolo, data.volumes, data.keys, data.values);
|
|
359
371
|
runsMap[agent] = buildResult.runProfile;
|
|
360
372
|
writeResult = { missingKeys: buildResult.missingKeys, unsafeKeys: buildResult.unsafeKeys };
|
|
361
373
|
hasConfigChanged = true;
|