openclawsetup 2.8.2 → 2.8.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/bin/cli.mjs +134 -17
- package/package.json +1 -1
package/bin/cli.mjs
CHANGED
|
@@ -856,16 +856,78 @@ function getDashboardToken(config) {
|
|
|
856
856
|
return extractAuthToken(config?.raw || '') || '';
|
|
857
857
|
}
|
|
858
858
|
|
|
859
|
+
function maskToken(token = '') {
|
|
860
|
+
if (!token || token === '<未配置>' || token === '<你的token>') return token;
|
|
861
|
+
const text = String(token);
|
|
862
|
+
if (text.length <= 8) return '***';
|
|
863
|
+
return `${text.slice(0, 4)}...${text.slice(-4)}`;
|
|
864
|
+
}
|
|
865
|
+
|
|
859
866
|
function parseStatusOutput(statusOutput) {
|
|
860
867
|
const text = (statusOutput || '').toLowerCase();
|
|
861
|
-
const
|
|
862
|
-
|
|
868
|
+
const stoppedRegex = [
|
|
869
|
+
/\bnot\s+running\b/,
|
|
870
|
+
/\bstopped\b/,
|
|
871
|
+
/\binactive\b/,
|
|
872
|
+
/\boffline\b/,
|
|
873
|
+
];
|
|
874
|
+
const runningRegex = [
|
|
875
|
+
/\brunning\b/,
|
|
876
|
+
/\bactive\b/,
|
|
877
|
+
/\bonline\b/,
|
|
878
|
+
];
|
|
879
|
+
const stoppedKeywords = ['未运行', '已停止', '停止', '离线'];
|
|
880
|
+
const runningKeywords = ['已运行', '运行中', '在线'];
|
|
863
881
|
|
|
864
|
-
if (
|
|
882
|
+
if (stoppedRegex.some((pattern) => pattern.test(text))) return 'stopped';
|
|
865
883
|
if (stoppedKeywords.some((word) => text.includes(word))) return 'stopped';
|
|
884
|
+
if (runningRegex.some((pattern) => pattern.test(text))) return 'running';
|
|
885
|
+
if (runningKeywords.some((word) => text.includes(word))) return 'running';
|
|
866
886
|
return 'unknown';
|
|
867
887
|
}
|
|
868
888
|
|
|
889
|
+
function isCommandTimeout(result) {
|
|
890
|
+
if (!result || result.ok) return false;
|
|
891
|
+
const raw = `${result.error || ''} ${result.stderr || ''}`.toLowerCase();
|
|
892
|
+
return raw.includes('timed out') || raw.includes('etimedout') || raw.includes('sigterm') || raw.includes('killed');
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
function probeGatewayStatus(cliName, timeout = STATUS_CMD_TIMEOUT_MS) {
|
|
896
|
+
const attempts = [
|
|
897
|
+
`${cliName} status`,
|
|
898
|
+
`${cliName} gateway status`,
|
|
899
|
+
];
|
|
900
|
+
|
|
901
|
+
let state = 'unknown';
|
|
902
|
+
let output = '';
|
|
903
|
+
let timedOut = false;
|
|
904
|
+
let error = '';
|
|
905
|
+
|
|
906
|
+
for (const cmd of attempts) {
|
|
907
|
+
const result = safeExec(cmd, { timeout });
|
|
908
|
+
if (isCommandTimeout(result)) {
|
|
909
|
+
timedOut = true;
|
|
910
|
+
continue;
|
|
911
|
+
}
|
|
912
|
+
if (!result.ok) {
|
|
913
|
+
if (!error) error = result.stderr || result.error || '';
|
|
914
|
+
continue;
|
|
915
|
+
}
|
|
916
|
+
if (!result.output) continue;
|
|
917
|
+
|
|
918
|
+
output = result.output;
|
|
919
|
+
const parsed = parseStatusOutput(result.output);
|
|
920
|
+
if (parsed === 'running') {
|
|
921
|
+
return { state: 'running', output, timedOut, error };
|
|
922
|
+
}
|
|
923
|
+
if (parsed === 'stopped') {
|
|
924
|
+
state = 'stopped';
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
return { state, output, timedOut, error };
|
|
929
|
+
}
|
|
930
|
+
|
|
869
931
|
function getPortCheckOutput(port, timeout = PORT_CHECK_TIMEOUT_MS) {
|
|
870
932
|
const cmd = platform() === 'win32'
|
|
871
933
|
? `netstat -ano | findstr :${port}`
|
|
@@ -1094,6 +1156,39 @@ function selectHealthPort(config, strongMode = false) {
|
|
|
1094
1156
|
return strongMode ? STRONG_PORT_CANDIDATES[0] : DEFAULT_GATEWAY_PORT;
|
|
1095
1157
|
}
|
|
1096
1158
|
|
|
1159
|
+
function parsePortFromText(raw = '') {
|
|
1160
|
+
const matches = String(raw).match(/\b(\d{2,5})\b/g) || [];
|
|
1161
|
+
for (const item of matches) {
|
|
1162
|
+
const port = Number(item);
|
|
1163
|
+
if (Number.isInteger(port) && port > 0 && port <= 65535) {
|
|
1164
|
+
return port;
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
return null;
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
function getRuntimeGatewayPort(cliName, timeout = STATUS_CMD_TIMEOUT_MS) {
|
|
1171
|
+
if (!cliName) return null;
|
|
1172
|
+
const result = safeExec(`${cliName} config get gateway.port`, { timeout });
|
|
1173
|
+
if (!result.ok || !result.output) return null;
|
|
1174
|
+
return parsePortFromText(result.output);
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
function resolveHealthPort(cliName, config, strongMode = false) {
|
|
1178
|
+
const runtimePort = getRuntimeGatewayPort(cliName);
|
|
1179
|
+
if (runtimePort) return { port: runtimePort, source: 'runtime' };
|
|
1180
|
+
|
|
1181
|
+
const candidate = Number(config?.port || DEFAULT_GATEWAY_PORT);
|
|
1182
|
+
if (Number.isInteger(candidate) && candidate > 0) {
|
|
1183
|
+
return { port: candidate, source: 'config' };
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
return {
|
|
1187
|
+
port: strongMode ? STRONG_PORT_CANDIDATES[0] : DEFAULT_GATEWAY_PORT,
|
|
1188
|
+
source: 'default',
|
|
1189
|
+
};
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1097
1192
|
async function tryFixPortConflict(cliName, currentPort) {
|
|
1098
1193
|
const availablePort = await findAvailablePort(currentPort);
|
|
1099
1194
|
if (!availablePort || availablePort === currentPort) {
|
|
@@ -1375,12 +1470,18 @@ function getOnboardHelp(cliName) {
|
|
|
1375
1470
|
return '';
|
|
1376
1471
|
}
|
|
1377
1472
|
|
|
1473
|
+
function extractOnboardFlags(helpText = '') {
|
|
1474
|
+
const matches = helpText.toLowerCase().match(/--[a-z0-9][a-z0-9-]*/g) || [];
|
|
1475
|
+
return new Set(matches);
|
|
1476
|
+
}
|
|
1477
|
+
|
|
1378
1478
|
function buildOnboardArgsFromHelp(helpText, options) {
|
|
1379
1479
|
const help = helpText.toLowerCase();
|
|
1480
|
+
const flagSet = extractOnboardFlags(helpText);
|
|
1380
1481
|
const args = [];
|
|
1381
1482
|
const enabled = [];
|
|
1382
1483
|
|
|
1383
|
-
const pickFlag = (flags) => flags.find((flag) =>
|
|
1484
|
+
const pickFlag = (flags) => flags.find((flag) => flagSet.has(flag));
|
|
1384
1485
|
|
|
1385
1486
|
const yesFlag = pickFlag(['--yes', '--assume-yes', '--accept', '--agree']);
|
|
1386
1487
|
if (yesFlag) {
|
|
@@ -1393,10 +1494,10 @@ function buildOnboardArgsFromHelp(helpText, options) {
|
|
|
1393
1494
|
args.push(installDaemonFlag);
|
|
1394
1495
|
}
|
|
1395
1496
|
|
|
1396
|
-
if (
|
|
1497
|
+
if (flagSet.has('--quickstart')) {
|
|
1397
1498
|
args.push('--quickstart');
|
|
1398
1499
|
enabled.push('quickstart');
|
|
1399
|
-
} else if (
|
|
1500
|
+
} else if (flagSet.has('--mode') && (help.includes('quickstart') || help.includes('quick start'))) {
|
|
1400
1501
|
args.push('--mode', 'quickstart');
|
|
1401
1502
|
enabled.push('quickstart');
|
|
1402
1503
|
}
|
|
@@ -1417,10 +1518,10 @@ function buildOnboardArgsFromHelp(helpText, options) {
|
|
|
1417
1518
|
}
|
|
1418
1519
|
}
|
|
1419
1520
|
|
|
1420
|
-
if (
|
|
1521
|
+
if (flagSet.has('--ui') && (help.includes('web') || help.includes('dashboard'))) {
|
|
1421
1522
|
args.push('--ui', 'web');
|
|
1422
1523
|
enabled.push('ui-web');
|
|
1423
|
-
} else if (
|
|
1524
|
+
} else if (flagSet.has('--web')) {
|
|
1424
1525
|
args.push('--web');
|
|
1425
1526
|
enabled.push('ui-web');
|
|
1426
1527
|
}
|
|
@@ -1848,8 +1949,9 @@ function showCompletionInfo(cliName) {
|
|
|
1848
1949
|
function showDashboardAccessInfo() {
|
|
1849
1950
|
const config = getConfigInfo();
|
|
1850
1951
|
const port = config.port || 18789;
|
|
1851
|
-
const
|
|
1852
|
-
const
|
|
1952
|
+
const tokenRaw = getDashboardToken(config) || '<你的token>';
|
|
1953
|
+
const tokenMasked = maskToken(tokenRaw);
|
|
1954
|
+
const dashboardUrl = `http://127.0.0.1:${port}/?token=${tokenMasked}`;
|
|
1853
1955
|
|
|
1854
1956
|
if (detectVps()) {
|
|
1855
1957
|
const serverIp = getServerIp() || '<服务器IP>';
|
|
@@ -2026,16 +2128,27 @@ async function runHealthCheck(cliName, autoFix = false, strongMode = false) {
|
|
|
2026
2128
|
|
|
2027
2129
|
// 3. 检查 Gateway 进程
|
|
2028
2130
|
console.log(colors.cyan('检查 Gateway 进程...'));
|
|
2029
|
-
const
|
|
2030
|
-
const statusState =
|
|
2031
|
-
|
|
2032
|
-
|
|
2131
|
+
const statusProbe = probeGatewayStatus(activeCli, STATUS_CMD_TIMEOUT_MS);
|
|
2132
|
+
const statusState = statusProbe.state;
|
|
2133
|
+
const statusPortResolved = resolveHealthPort(activeCli, config, strongMode);
|
|
2134
|
+
const portForStatus = statusPortResolved.port;
|
|
2135
|
+
const statusPortResult = getPortCheckOutput(portForStatus, PORT_CHECK_TIMEOUT_MS);
|
|
2136
|
+
const statusPortListening = Boolean(statusPortResult.ok && statusPortResult.output);
|
|
2137
|
+
|
|
2138
|
+
if (statusState === 'running' || (statusState !== 'running' && statusPortListening)) {
|
|
2033
2139
|
log.success('Gateway 进程正在运行');
|
|
2140
|
+
if (statusProbe.timedOut) {
|
|
2141
|
+
log.hint('状态命令超时,已按端口监听判定为运行中');
|
|
2142
|
+
} else if (statusState !== 'running' && statusPortListening) {
|
|
2143
|
+
log.hint('状态命令返回非运行,但端口可访问(兼容判定)');
|
|
2144
|
+
}
|
|
2034
2145
|
} else {
|
|
2035
2146
|
const issue = summarizeIssue(
|
|
2036
2147
|
'error',
|
|
2037
2148
|
'Gateway 未运行',
|
|
2038
|
-
|
|
2149
|
+
statusProbe.output
|
|
2150
|
+
? 'Gateway 状态为停止/未知'
|
|
2151
|
+
: (statusProbe.error || '状态检查失败'),
|
|
2039
2152
|
`运行 ${activeCli} gateway start 启动服务`,
|
|
2040
2153
|
`${activeCli} gateway start`,
|
|
2041
2154
|
);
|
|
@@ -2056,7 +2169,7 @@ async function runHealthCheck(cliName, autoFix = false, strongMode = false) {
|
|
|
2056
2169
|
|
|
2057
2170
|
// 4. 检查端口监听
|
|
2058
2171
|
config = getConfigInfo();
|
|
2059
|
-
let port =
|
|
2172
|
+
let port = resolveHealthPort(activeCli, config, strongMode).port;
|
|
2060
2173
|
console.log(colors.cyan(`检查端口监听 (${port})...`));
|
|
2061
2174
|
let portHealthy = false;
|
|
2062
2175
|
|
|
@@ -2337,7 +2450,8 @@ function testModelChat(cliName, timeoutMs = MODEL_CHAT_TIMEOUT_MS) {
|
|
|
2337
2450
|
|
|
2338
2451
|
async function showStatusInfo(cliName) {
|
|
2339
2452
|
const config = getConfigInfo();
|
|
2340
|
-
const
|
|
2453
|
+
const portResolved = resolveHealthPort(cliName, config, false);
|
|
2454
|
+
const port = portResolved.port;
|
|
2341
2455
|
const token = getDashboardToken(config) || '<未配置>';
|
|
2342
2456
|
const dashboardUrl = `http://127.0.0.1:${port}/?token=${token}`;
|
|
2343
2457
|
|
|
@@ -2378,6 +2492,9 @@ async function showStatusInfo(cliName) {
|
|
|
2378
2492
|
if (statusTimedOut) {
|
|
2379
2493
|
console.log(colors.yellow(' ⚠ 状态命令超时,已自动切换端口探测模式'));
|
|
2380
2494
|
}
|
|
2495
|
+
if (portResolved.source === 'runtime' && Number(config?.port) !== Number(port)) {
|
|
2496
|
+
console.log(colors.gray(` … 运行时端口为 ${port}(与本地配置文件不一致)`));
|
|
2497
|
+
}
|
|
2381
2498
|
|
|
2382
2499
|
if (statusState === 'running') {
|
|
2383
2500
|
console.log(colors.green(' ✓ Gateway 服务正在运行'));
|