openclawsetup 2.8.1 → 2.8.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/bin/cli.mjs +109 -23
- package/package.json +1 -1
package/bin/cli.mjs
CHANGED
|
@@ -83,6 +83,9 @@ const log = {
|
|
|
83
83
|
|
|
84
84
|
const DEFAULT_GATEWAY_PORT = 18789;
|
|
85
85
|
const STRONG_FIX_MAX_PASSES = 3;
|
|
86
|
+
const STATUS_CMD_TIMEOUT_MS = platform() === 'win32' ? 8000 : 5000;
|
|
87
|
+
const PORT_CHECK_TIMEOUT_MS = platform() === 'win32' ? 6000 : 4000;
|
|
88
|
+
const MODEL_CHAT_TIMEOUT_MS = platform() === 'win32' ? 25000 : 18000;
|
|
86
89
|
const STRONG_PORT_CANDIDATES = [
|
|
87
90
|
18789,
|
|
88
91
|
18790,
|
|
@@ -855,26 +858,81 @@ function getDashboardToken(config) {
|
|
|
855
858
|
|
|
856
859
|
function parseStatusOutput(statusOutput) {
|
|
857
860
|
const text = (statusOutput || '').toLowerCase();
|
|
858
|
-
const
|
|
859
|
-
|
|
861
|
+
const stoppedRegex = [
|
|
862
|
+
/\bnot\s+running\b/,
|
|
863
|
+
/\bstopped\b/,
|
|
864
|
+
/\binactive\b/,
|
|
865
|
+
/\boffline\b/,
|
|
866
|
+
];
|
|
867
|
+
const runningRegex = [
|
|
868
|
+
/\brunning\b/,
|
|
869
|
+
/\bactive\b/,
|
|
870
|
+
/\bonline\b/,
|
|
871
|
+
];
|
|
872
|
+
const stoppedKeywords = ['未运行', '已停止', '停止', '离线'];
|
|
873
|
+
const runningKeywords = ['已运行', '运行中', '在线'];
|
|
860
874
|
|
|
861
|
-
if (
|
|
875
|
+
if (stoppedRegex.some((pattern) => pattern.test(text))) return 'stopped';
|
|
862
876
|
if (stoppedKeywords.some((word) => text.includes(word))) return 'stopped';
|
|
877
|
+
if (runningRegex.some((pattern) => pattern.test(text))) return 'running';
|
|
878
|
+
if (runningKeywords.some((word) => text.includes(word))) return 'running';
|
|
863
879
|
return 'unknown';
|
|
864
880
|
}
|
|
865
881
|
|
|
866
|
-
function
|
|
882
|
+
function isCommandTimeout(result) {
|
|
883
|
+
if (!result || result.ok) return false;
|
|
884
|
+
const raw = `${result.error || ''} ${result.stderr || ''}`.toLowerCase();
|
|
885
|
+
return raw.includes('timed out') || raw.includes('etimedout') || raw.includes('sigterm') || raw.includes('killed');
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
function probeGatewayStatus(cliName, timeout = STATUS_CMD_TIMEOUT_MS) {
|
|
889
|
+
const attempts = [
|
|
890
|
+
`${cliName} status`,
|
|
891
|
+
`${cliName} gateway status`,
|
|
892
|
+
];
|
|
893
|
+
|
|
894
|
+
let state = 'unknown';
|
|
895
|
+
let output = '';
|
|
896
|
+
let timedOut = false;
|
|
897
|
+
let error = '';
|
|
898
|
+
|
|
899
|
+
for (const cmd of attempts) {
|
|
900
|
+
const result = safeExec(cmd, { timeout });
|
|
901
|
+
if (isCommandTimeout(result)) {
|
|
902
|
+
timedOut = true;
|
|
903
|
+
continue;
|
|
904
|
+
}
|
|
905
|
+
if (!result.ok) {
|
|
906
|
+
if (!error) error = result.stderr || result.error || '';
|
|
907
|
+
continue;
|
|
908
|
+
}
|
|
909
|
+
if (!result.output) continue;
|
|
910
|
+
|
|
911
|
+
output = result.output;
|
|
912
|
+
const parsed = parseStatusOutput(result.output);
|
|
913
|
+
if (parsed === 'running') {
|
|
914
|
+
return { state: 'running', output, timedOut, error };
|
|
915
|
+
}
|
|
916
|
+
if (parsed === 'stopped') {
|
|
917
|
+
state = 'stopped';
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
return { state, output, timedOut, error };
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
function getPortCheckOutput(port, timeout = PORT_CHECK_TIMEOUT_MS) {
|
|
867
925
|
const cmd = platform() === 'win32'
|
|
868
926
|
? `netstat -ano | findstr :${port}`
|
|
869
927
|
: `lsof -nP -iTCP:${port} -sTCP:LISTEN 2>/dev/null || netstat -tlnp 2>/dev/null | grep :${port}`;
|
|
870
|
-
return safeExec(cmd);
|
|
928
|
+
return safeExec(cmd, { timeout });
|
|
871
929
|
}
|
|
872
930
|
|
|
873
|
-
function getPortConflictDetail(port) {
|
|
931
|
+
function getPortConflictDetail(port, timeout = PORT_CHECK_TIMEOUT_MS) {
|
|
874
932
|
const cmd = platform() === 'win32'
|
|
875
933
|
? `netstat -ano | findstr :${port} | findstr LISTENING`
|
|
876
934
|
: `lsof -nP -iTCP:${port} -sTCP:LISTEN 2>/dev/null | head -8`;
|
|
877
|
-
return safeExec(cmd);
|
|
935
|
+
return safeExec(cmd, { timeout });
|
|
878
936
|
}
|
|
879
937
|
|
|
880
938
|
function isLikelyOpenClawProcess(output = '') {
|
|
@@ -1372,12 +1430,18 @@ function getOnboardHelp(cliName) {
|
|
|
1372
1430
|
return '';
|
|
1373
1431
|
}
|
|
1374
1432
|
|
|
1433
|
+
function extractOnboardFlags(helpText = '') {
|
|
1434
|
+
const matches = helpText.toLowerCase().match(/--[a-z0-9][a-z0-9-]*/g) || [];
|
|
1435
|
+
return new Set(matches);
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1375
1438
|
function buildOnboardArgsFromHelp(helpText, options) {
|
|
1376
1439
|
const help = helpText.toLowerCase();
|
|
1440
|
+
const flagSet = extractOnboardFlags(helpText);
|
|
1377
1441
|
const args = [];
|
|
1378
1442
|
const enabled = [];
|
|
1379
1443
|
|
|
1380
|
-
const pickFlag = (flags) => flags.find((flag) =>
|
|
1444
|
+
const pickFlag = (flags) => flags.find((flag) => flagSet.has(flag));
|
|
1381
1445
|
|
|
1382
1446
|
const yesFlag = pickFlag(['--yes', '--assume-yes', '--accept', '--agree']);
|
|
1383
1447
|
if (yesFlag) {
|
|
@@ -1390,10 +1454,10 @@ function buildOnboardArgsFromHelp(helpText, options) {
|
|
|
1390
1454
|
args.push(installDaemonFlag);
|
|
1391
1455
|
}
|
|
1392
1456
|
|
|
1393
|
-
if (
|
|
1457
|
+
if (flagSet.has('--quickstart')) {
|
|
1394
1458
|
args.push('--quickstart');
|
|
1395
1459
|
enabled.push('quickstart');
|
|
1396
|
-
} else if (
|
|
1460
|
+
} else if (flagSet.has('--mode') && (help.includes('quickstart') || help.includes('quick start'))) {
|
|
1397
1461
|
args.push('--mode', 'quickstart');
|
|
1398
1462
|
enabled.push('quickstart');
|
|
1399
1463
|
}
|
|
@@ -1414,10 +1478,10 @@ function buildOnboardArgsFromHelp(helpText, options) {
|
|
|
1414
1478
|
}
|
|
1415
1479
|
}
|
|
1416
1480
|
|
|
1417
|
-
if (
|
|
1481
|
+
if (flagSet.has('--ui') && (help.includes('web') || help.includes('dashboard'))) {
|
|
1418
1482
|
args.push('--ui', 'web');
|
|
1419
1483
|
enabled.push('ui-web');
|
|
1420
|
-
} else if (
|
|
1484
|
+
} else if (flagSet.has('--web')) {
|
|
1421
1485
|
args.push('--web');
|
|
1422
1486
|
enabled.push('ui-web');
|
|
1423
1487
|
}
|
|
@@ -2023,16 +2087,26 @@ async function runHealthCheck(cliName, autoFix = false, strongMode = false) {
|
|
|
2023
2087
|
|
|
2024
2088
|
// 3. 检查 Gateway 进程
|
|
2025
2089
|
console.log(colors.cyan('检查 Gateway 进程...'));
|
|
2026
|
-
const
|
|
2027
|
-
const statusState =
|
|
2090
|
+
const statusProbe = probeGatewayStatus(activeCli, STATUS_CMD_TIMEOUT_MS);
|
|
2091
|
+
const statusState = statusProbe.state;
|
|
2092
|
+
const portForStatus = selectHealthPort(config, strongMode);
|
|
2093
|
+
const statusPortResult = getPortCheckOutput(portForStatus, PORT_CHECK_TIMEOUT_MS);
|
|
2094
|
+
const statusPortListening = Boolean(statusPortResult.ok && statusPortResult.output);
|
|
2028
2095
|
|
|
2029
|
-
if (
|
|
2096
|
+
if (statusState === 'running' || (statusState !== 'running' && statusPortListening)) {
|
|
2030
2097
|
log.success('Gateway 进程正在运行');
|
|
2098
|
+
if (statusProbe.timedOut) {
|
|
2099
|
+
log.hint('状态命令超时,已按端口监听判定为运行中');
|
|
2100
|
+
} else if (statusState !== 'running' && statusPortListening) {
|
|
2101
|
+
log.hint('状态命令返回非运行,但端口可访问(兼容判定)');
|
|
2102
|
+
}
|
|
2031
2103
|
} else {
|
|
2032
2104
|
const issue = summarizeIssue(
|
|
2033
2105
|
'error',
|
|
2034
2106
|
'Gateway 未运行',
|
|
2035
|
-
|
|
2107
|
+
statusProbe.output
|
|
2108
|
+
? 'Gateway 状态为停止/未知'
|
|
2109
|
+
: (statusProbe.error || '状态检查失败'),
|
|
2036
2110
|
`运行 ${activeCli} gateway start 启动服务`,
|
|
2037
2111
|
`${activeCli} gateway start`,
|
|
2038
2112
|
);
|
|
@@ -2288,10 +2362,12 @@ async function runHealthCheck(cliName, autoFix = false, strongMode = false) {
|
|
|
2288
2362
|
|
|
2289
2363
|
// ============ 交互式菜单 ============
|
|
2290
2364
|
|
|
2291
|
-
function testModelChat(cliName) {
|
|
2365
|
+
function testModelChat(cliName, timeoutMs = MODEL_CHAT_TIMEOUT_MS) {
|
|
2292
2366
|
return new Promise((resolve) => {
|
|
2293
|
-
const
|
|
2294
|
-
|
|
2367
|
+
const agentTimeoutSec = Math.max(12, Math.floor(timeoutMs / 1000));
|
|
2368
|
+
const execTimeoutMs = Math.max(15000, timeoutMs + 5000);
|
|
2369
|
+
const cmd = `${cliName} agent --session-id openclawsetup-test --message "请用一句话回复你的模型名称" --json --timeout ${agentTimeoutSec}`;
|
|
2370
|
+
exec(cmd, { timeout: execTimeoutMs }, (error, stdout, stderr) => {
|
|
2295
2371
|
if (error) {
|
|
2296
2372
|
const errMsg = (stderr || stdout || error.message || '').trim();
|
|
2297
2373
|
// 提取关键错误信息
|
|
@@ -2337,6 +2413,7 @@ async function showStatusInfo(cliName) {
|
|
|
2337
2413
|
const dashboardUrl = `http://127.0.0.1:${port}/?token=${token}`;
|
|
2338
2414
|
|
|
2339
2415
|
console.log(colors.bold(colors.cyan('\n📊 OpenClaw 状态信息\n')));
|
|
2416
|
+
console.log(colors.gray(' … 正在检查服务状态(超时自动跳过)...'));
|
|
2340
2417
|
|
|
2341
2418
|
// 服务状态(兼容不同 CLI 版本,避免 Windows 下误判)
|
|
2342
2419
|
const statusAttempts = [
|
|
@@ -2345,9 +2422,14 @@ async function showStatusInfo(cliName) {
|
|
|
2345
2422
|
];
|
|
2346
2423
|
let statusState = 'unknown';
|
|
2347
2424
|
let statusOutput = '';
|
|
2425
|
+
let statusTimedOut = false;
|
|
2348
2426
|
|
|
2349
2427
|
for (const attempt of statusAttempts) {
|
|
2350
|
-
const statusResult = safeExec(attempt.cmd);
|
|
2428
|
+
const statusResult = safeExec(attempt.cmd, { timeout: STATUS_CMD_TIMEOUT_MS });
|
|
2429
|
+
if (!statusResult.ok && /timed out|etimedout|SIGTERM|killed/i.test(`${statusResult.error || ''} ${statusResult.stderr || ''}`)) {
|
|
2430
|
+
statusTimedOut = true;
|
|
2431
|
+
continue;
|
|
2432
|
+
}
|
|
2351
2433
|
if (!statusResult.ok || !statusResult.output) continue;
|
|
2352
2434
|
statusOutput = statusResult.output;
|
|
2353
2435
|
const parsed = parseStatusOutput(statusResult.output);
|
|
@@ -2361,9 +2443,13 @@ async function showStatusInfo(cliName) {
|
|
|
2361
2443
|
}
|
|
2362
2444
|
|
|
2363
2445
|
// 端口检查(作为状态命令兜底)
|
|
2364
|
-
const portResult = getPortCheckOutput(port);
|
|
2446
|
+
const portResult = getPortCheckOutput(port, PORT_CHECK_TIMEOUT_MS);
|
|
2365
2447
|
const portListening = Boolean(portResult.ok && portResult.output);
|
|
2366
2448
|
|
|
2449
|
+
if (statusTimedOut) {
|
|
2450
|
+
console.log(colors.yellow(' ⚠ 状态命令超时,已自动切换端口探测模式'));
|
|
2451
|
+
}
|
|
2452
|
+
|
|
2367
2453
|
if (statusState === 'running') {
|
|
2368
2454
|
console.log(colors.green(' ✓ Gateway 服务正在运行'));
|
|
2369
2455
|
} else if (portListening) {
|
|
@@ -2413,8 +2499,8 @@ async function showStatusInfo(cliName) {
|
|
|
2413
2499
|
|
|
2414
2500
|
// 模型对话测试
|
|
2415
2501
|
if (primaryModel) {
|
|
2416
|
-
console.log(colors.gray(
|
|
2417
|
-
const testResult = await testModelChat(cliName);
|
|
2502
|
+
console.log(colors.gray(` … 正在测试模型对话(最长 ${Math.floor(MODEL_CHAT_TIMEOUT_MS / 1000)} 秒)...`));
|
|
2503
|
+
const testResult = await testModelChat(cliName, MODEL_CHAT_TIMEOUT_MS);
|
|
2418
2504
|
if (testResult.success) {
|
|
2419
2505
|
console.log(colors.green(' ✓ 模型对话正常'));
|
|
2420
2506
|
if (testResult.message) {
|