feat(portable): bug 一键上报 + 自动崩溃上报 + openclaw 升级 2026.6.6
Bug 上报(方便服务器侧排查便携版问题): - 新增 lib/report-bug.mjs:算设备指纹→sk-uc apiKey,收集 openclaw 版本/ 系统信息/最近日志(gzip+base64),POST 到 api.u-claw.org/recharge/bug/submit。 静默失败,绝不影响主流程。复用 fingerprint.mjs + xiapan-client.mjs。 - config-server 加 POST /api/report-bug:本地补齐信息后转发,配置中心加 「🐛 报 Bug」表单(标题/描述/是否附日志)。 - Windows-Start.bat / Mac-Start.command:gateway 端口耗尽或异常退出时 自动后台上报(Ctrl+C 正常停止不上报)。 openclaw 升级: - OPENCLAW_VERSION 2026.4.29 → 2026.6.6(X 盘真实 USB 实测 gateway 启动/ready、Dashboard 200、旧配置兼容、虾盘云 status 均通过)。 便携性改进(借鉴 OpenClaw-Windows-Portable): - setup.sh / 启动脚本的 npm install 设 npm_config_cache 指向盘内 app/.npm-cache,避免污染系统缓存,拔盘不留痕。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1 +1 @@
|
|||||||
2026.4.29
|
2026.6.6
|
||||||
|
|||||||
@@ -103,6 +103,8 @@ if [ ! -d "$CORE_DIR/node_modules" ]; then
|
|||||||
echo " Falling back to npm install (USB drives may take 20+ min)."
|
echo " Falling back to npm install (USB drives may take 20+ min)."
|
||||||
echo " TIP: re-download u-claw-portable-*.zip with bundled deps."
|
echo " TIP: re-download u-claw-portable-*.zip with bundled deps."
|
||||||
cd "$CORE_DIR"
|
cd "$CORE_DIR"
|
||||||
|
# 把 npm 缓存留在盘内,避免污染系统 ~/.npm(拔盘不留痕)
|
||||||
|
npm_config_cache="$APP_DIR/.npm-cache" \
|
||||||
"$NODE_BIN" "$NODE_DIR/bin/npm" install --registry=https://registry.npmmirror.com --ignore-scripts --no-audit --no-fund --omit=dev 2>&1
|
"$NODE_BIN" "$NODE_DIR/bin/npm" install --registry=https://registry.npmmirror.com --ignore-scripts --no-audit --no-fund --omit=dev 2>&1
|
||||||
echo -e " ${GREEN}Dependencies installed${NC}"
|
echo -e " ${GREEN}Dependencies installed${NC}"
|
||||||
echo ""
|
echo ""
|
||||||
@@ -130,6 +132,9 @@ while lsof -i :$PORT >/dev/null 2>&1; do
|
|||||||
PORT=$((PORT + 1))
|
PORT=$((PORT + 1))
|
||||||
if [ $PORT -gt 18799 ]; then
|
if [ $PORT -gt 18799 ]; then
|
||||||
echo -e " ${RED}No available port (18789-18799)${NC}"
|
echo -e " ${RED}No available port (18789-18799)${NC}"
|
||||||
|
# 自动上报:端口全占,gateway 无法启动(后台、静默、失败不影响)
|
||||||
|
UCLAW_APP_ROOT="$UCLAW_DIR" "$NODE_BIN" "$UCLAW_DIR/lib/report-bug.mjs" \
|
||||||
|
--auto --title "gateway-no-free-port" --desc "Ports 18789-18799 all in use" --root "$UCLAW_DIR" >/dev/null 2>&1 &
|
||||||
read -p " Press Enter to exit..."
|
read -p " Press Enter to exit..."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
@@ -183,3 +188,15 @@ cleanup() {
|
|||||||
trap cleanup INT TERM
|
trap cleanup INT TERM
|
||||||
|
|
||||||
wait $GW_PID
|
wait $GW_PID
|
||||||
|
GW_EXIT=$?
|
||||||
|
|
||||||
|
# 自动上报:gateway 异常退出。Ctrl+C 走 trap cleanup(exit 0)不会到这;
|
||||||
|
# 走到这里说明 gateway 自己退了。退出码非 0 才上报,避免噪音。后台、静默、失败不影响。
|
||||||
|
if [ "$GW_EXIT" -ne 0 ]; then
|
||||||
|
echo -e " ${YELLOW}OpenClaw exited unexpectedly (code $GW_EXIT), reporting...${NC}"
|
||||||
|
UCLAW_APP_ROOT="$UCLAW_DIR" "$NODE_BIN" "$UCLAW_DIR/lib/report-bug.mjs" \
|
||||||
|
--auto --title "gateway-exited-code-$GW_EXIT" --desc "Gateway exited with code $GW_EXIT on port $PORT" --root "$UCLAW_DIR" >/dev/null 2>&1 &
|
||||||
|
fi
|
||||||
|
kill $CONFIG_PID 2>/dev/null
|
||||||
|
echo ""
|
||||||
|
echo -e " 🦞 U-Claw stopped."
|
||||||
|
|||||||
@@ -78,6 +78,8 @@ if not exist "%CORE_DIR%\node_modules" (
|
|||||||
echo File system: NTFS recommended. exFAT/FAT32 will be very slow.
|
echo File system: NTFS recommended. exFAT/FAT32 will be very slow.
|
||||||
echo.
|
echo.
|
||||||
cd /d "%CORE_DIR%"
|
cd /d "%CORE_DIR%"
|
||||||
|
REM 把 npm 缓存留在盘内,避免污染系统 %APPDATA%\npm-cache(拔盘不留痕)
|
||||||
|
set "npm_config_cache=%APP_DIR%\.npm-cache"
|
||||||
call "%NPM_BIN%" install --registry=https://registry.npmmirror.com --ignore-scripts --no-audit --no-fund --omit=dev
|
call "%NPM_BIN%" install --registry=https://registry.npmmirror.com --ignore-scripts --no-audit --no-fund --omit=dev
|
||||||
echo.
|
echo.
|
||||||
echo Dependencies installed!
|
echo Dependencies installed!
|
||||||
@@ -123,6 +125,8 @@ if %errorlevel%==0 (
|
|||||||
set /a PORT+=1
|
set /a PORT+=1
|
||||||
if %PORT% gtr 18799 (
|
if %PORT% gtr 18799 (
|
||||||
echo No available port 18789-18799
|
echo No available port 18789-18799
|
||||||
|
REM 自动上报:端口全被占,gateway 无法启动(detach、静默、失败不影响)
|
||||||
|
start /B "" "%NODE_BIN%" "%UCLAW_DIR%lib\report-bug.mjs" --auto --title "gateway-no-free-port" --desc "Ports 18789-18799 all in use" --root "%UCLAW_DIR%." >nul 2>&1
|
||||||
pause
|
pause
|
||||||
exit /b 1
|
exit /b 1
|
||||||
)
|
)
|
||||||
@@ -157,7 +161,14 @@ echo.
|
|||||||
cd /d "%CORE_DIR%"
|
cd /d "%CORE_DIR%"
|
||||||
set "OPENCLAW_MJS=%CORE_DIR%\node_modules\openclaw\openclaw.mjs"
|
set "OPENCLAW_MJS=%CORE_DIR%\node_modules\openclaw\openclaw.mjs"
|
||||||
"%NODE_BIN%" "%OPENCLAW_MJS%" gateway run --allow-unconfigured --force --port %PORT%
|
"%NODE_BIN%" "%OPENCLAW_MJS%" gateway run --allow-unconfigured --force --port %PORT%
|
||||||
|
set "GW_EXIT=%errorlevel%"
|
||||||
|
|
||||||
echo.
|
echo.
|
||||||
|
REM 自动上报:gateway 异常退出(退出码非 0 且非 Ctrl+C/0xC000013A=-1073741510)
|
||||||
|
REM 用户正常 Ctrl+C 停止不上报,避免噪音。detach、静默、失败不影响。
|
||||||
|
if not "%GW_EXIT%"=="0" if not "%GW_EXIT%"=="-1073741510" (
|
||||||
|
echo OpenClaw exited unexpectedly (code %GW_EXIT%), reporting...
|
||||||
|
start /B "" "%NODE_BIN%" "%UCLAW_DIR%lib\report-bug.mjs" --auto --title "gateway-exited-code-%GW_EXIT%" --desc "Gateway exited with code %GW_EXIT% on port %PORT%" --root "%UCLAW_DIR%." >nul 2>&1
|
||||||
|
)
|
||||||
echo OpenClaw stopped.
|
echo OpenClaw stopped.
|
||||||
pause
|
pause
|
||||||
|
|||||||
@@ -410,6 +410,26 @@ input:focus { border-color: #ff6b35; }
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="section active skills-section" style="display:block; margin-top: 8px;">
|
||||||
|
<h2>🐛 反馈 / 报 Bug</h2>
|
||||||
|
<p class="desc">遇到问题?一键反馈给我们,会自动附带版本号和最近日志(不含你的 API Key),方便我们排查。</p>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>问题标题 <span style="color:#888;font-weight:normal">(必填,简短描述)</span></label>
|
||||||
|
<input type="text" id="bugTitle" placeholder="例如:点击打开 Dashboard 没反应" maxlength="200">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>详细描述 <span style="color:#888;font-weight:normal">(可选)</span></label>
|
||||||
|
<textarea id="bugDesc" rows="4" placeholder="发生了什么?做了哪些操作?" style="width:100%;padding:12px;border-radius:8px;background:#1a1a1a;border:1px solid #333;color:#eee;font-family:inherit;font-size:0.95em;box-sizing:border-box;resize:vertical"></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="form-group" style="display:flex;align-items:center;gap:8px">
|
||||||
|
<input type="checkbox" id="bugLogs" checked style="width:auto;margin:0">
|
||||||
|
<label for="bugLogs" style="margin:0;font-weight:normal">附带最近运行日志(推荐,帮助定位问题)</label>
|
||||||
|
</div>
|
||||||
|
<div class="btn-row">
|
||||||
|
<button class="btn btn-primary" id="bugSubmitBtn" onclick="submitBug()">提交反馈</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="toast" id="toast"></div>
|
<div class="toast" id="toast"></div>
|
||||||
@@ -706,6 +726,32 @@ async function viewConfig() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Bug report ---
|
||||||
|
async function submitBug() {
|
||||||
|
var title = document.getElementById('bugTitle').value.trim();
|
||||||
|
if (title.length < 3) { showToast('请填写问题标题(至少 3 个字)', true); return; }
|
||||||
|
var desc = document.getElementById('bugDesc').value.trim();
|
||||||
|
var includeLogs = document.getElementById('bugLogs').checked;
|
||||||
|
var btn = document.getElementById('bugSubmitBtn');
|
||||||
|
btn.disabled = true; btn.textContent = '提交中…';
|
||||||
|
try {
|
||||||
|
var res = await fetch('/api/report-bug', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ title: title, description: desc, includeLogs: includeLogs })
|
||||||
|
});
|
||||||
|
var data = await res.json();
|
||||||
|
if (!res.ok || !data.ok) throw new Error(data.reason || data.error || '提交失败');
|
||||||
|
showToast('反馈已提交,编号 #' + data.id + ',谢谢!');
|
||||||
|
document.getElementById('bugTitle').value = '';
|
||||||
|
document.getElementById('bugDesc').value = '';
|
||||||
|
} catch (e) {
|
||||||
|
showToast('提交失败:' + e.message, true);
|
||||||
|
} finally {
|
||||||
|
btn.disabled = false; btn.textContent = '提交反馈';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// --- Toast ---
|
// --- Toast ---
|
||||||
function showToast(msg, isError) {
|
function showToast(msg, isError) {
|
||||||
var t = document.getElementById('toast');
|
var t = document.getElementById('toast');
|
||||||
|
|||||||
@@ -524,6 +524,37 @@ const server = http.createServer((req, res) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// API: Report a bug — user fills title/description in the web UI; this endpoint
|
||||||
|
// attaches fingerprint/version/logs locally and forwards to api.u-claw.org.
|
||||||
|
if (req.url === '/api/report-bug' && req.method === 'POST') {
|
||||||
|
let body = '';
|
||||||
|
req.on('data', chunk => {
|
||||||
|
body += chunk;
|
||||||
|
if (body.length > 2 * 1024 * 1024) req.destroy(); // 2MB cap on user input
|
||||||
|
});
|
||||||
|
req.on('end', () => {
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
const data = body ? JSON.parse(body) : {};
|
||||||
|
const mod = await import('../lib/report-bug.mjs');
|
||||||
|
const portableRoot = path.join(__dirname, '..');
|
||||||
|
const result = await mod.submitBugReport({
|
||||||
|
title: data.title,
|
||||||
|
description: data.description,
|
||||||
|
appRoot: portableRoot,
|
||||||
|
includeLogs: data.includeLogs !== false, // 默认带日志,用户可勾掉
|
||||||
|
});
|
||||||
|
res.writeHead(result.ok ? 200 : 400, { 'Content-Type': 'application/json' });
|
||||||
|
res.end(JSON.stringify(result));
|
||||||
|
} catch (err) {
|
||||||
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
||||||
|
res.end(JSON.stringify({ ok: false, error: err.message }));
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Serve static files
|
// Serve static files
|
||||||
const filePath = req.url === '/'
|
const filePath = req.url === '/'
|
||||||
? path.join(__dirname, 'public/index.html')
|
? path.join(__dirname, 'public/index.html')
|
||||||
|
|||||||
207
portable/lib/report-bug.mjs
Normal file
207
portable/lib/report-bug.mjs
Normal file
@@ -0,0 +1,207 @@
|
|||||||
|
// Bug 上报客户端(便携版)
|
||||||
|
//
|
||||||
|
// 流程:
|
||||||
|
// 1. 算设备指纹 -> sk-uc- apiKey(用来在服务器侧关联同一台设备的多条上报)
|
||||||
|
// 2. 收集 openclaw 版本号、设备类型、系统信息、最近日志(gzip+base64)
|
||||||
|
// 3. POST 到 https://api.u-claw.org/recharge/bug/submit
|
||||||
|
//
|
||||||
|
// 设计原则(对齐 check-update.mjs):
|
||||||
|
// - 静默失败:上报失败只 console.error,绝不影响 OpenClaw 主流程
|
||||||
|
// - 异步:调用方应 detach 跑(Windows-Start.bat 用 start /B),不阻塞启动
|
||||||
|
// - 无第三方依赖:fetch + node:zlib 都是内置
|
||||||
|
//
|
||||||
|
// 两种用法:
|
||||||
|
// 手动(网页一键,由 config-server 转发):import { submitBugReport } from './report-bug.mjs'
|
||||||
|
// 自动(崩溃):node lib/report-bug.mjs --auto --title "gateway-start-failed" [--log <文件>]
|
||||||
|
|
||||||
|
import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
|
||||||
|
import { gzipSync } from 'node:zlib';
|
||||||
|
import { resolve, join } from 'node:path';
|
||||||
|
import { platform, release, arch, tmpdir } from 'node:os';
|
||||||
|
import { pathToFileURL } from 'node:url';
|
||||||
|
|
||||||
|
import { getFingerprint } from './fingerprint.mjs';
|
||||||
|
import { buildApiKey } from './xiapan-client.mjs';
|
||||||
|
|
||||||
|
const DEFAULT_API_BASE = 'https://api.u-claw.org/v1';
|
||||||
|
const REQUEST_TIMEOUT_MS = 10_000;
|
||||||
|
const MAX_LOG_BYTES = 256 * 1024; // 只取日志尾部 256KB,避免上报体过大
|
||||||
|
|
||||||
|
function log(level, msg) {
|
||||||
|
const stream = level === 'error' ? process.stderr : process.stdout;
|
||||||
|
stream.write(`[report-bug] ${msg}\n`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// API base 默认带 /v1(虾盘云 API 中转),bug 上报走独立的 /bug 路径,所以去掉尾部 /v1。
|
||||||
|
// 例:https://api.u-claw.org/v1 -> https://api.u-claw.org/bug/submit
|
||||||
|
function getSubmitUrl() {
|
||||||
|
const base = (process.env.UCLAW_CLOUD_API_BASE || DEFAULT_API_BASE).replace(/\/+$/, '');
|
||||||
|
const root = base.replace(/\/v1$/, '');
|
||||||
|
return `${root}/bug/submit`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchWithTimeout(url, init) {
|
||||||
|
const controller = new AbortController();
|
||||||
|
const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
|
||||||
|
try {
|
||||||
|
return await fetch(url, { ...init, signal: controller.signal });
|
||||||
|
} finally {
|
||||||
|
clearTimeout(timer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读 OPENCLAW_VERSION:优先环境变量,再找盘内文件
|
||||||
|
function readOpenclawVersion(appRoot) {
|
||||||
|
if (process.env.OPENCLAW_VERSION) return process.env.OPENCLAW_VERSION.trim();
|
||||||
|
const candidates = [
|
||||||
|
process.env.UCLAW_VERSION_FILE,
|
||||||
|
appRoot && resolve(appRoot, 'OPENCLAW_VERSION'),
|
||||||
|
appRoot && resolve(appRoot, '..', 'OPENCLAW_VERSION'),
|
||||||
|
].filter(Boolean);
|
||||||
|
for (const p of candidates) {
|
||||||
|
try {
|
||||||
|
if (existsSync(p)) {
|
||||||
|
const v = readFileSync(p, 'utf8').trim();
|
||||||
|
if (v) return v;
|
||||||
|
}
|
||||||
|
} catch { /* 静默 */ }
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function collectSystemInfo() {
|
||||||
|
return JSON.stringify({
|
||||||
|
platform: platform(),
|
||||||
|
arch: arch(),
|
||||||
|
osRelease: release(),
|
||||||
|
node: process.version,
|
||||||
|
time: new Date().toISOString(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读最近一份 openclaw 日志的尾部,gzip+base64。失败返回 null。
|
||||||
|
// 日志默认在 %LOCALAPPDATA%\Temp\openclaw\ 或 $TMPDIR/openclaw/,文件名形如 openclaw-{date}.log
|
||||||
|
function collectLogsB64(explicitLogPath) {
|
||||||
|
try {
|
||||||
|
let logFile = null;
|
||||||
|
if (explicitLogPath && existsSync(explicitLogPath)) {
|
||||||
|
logFile = explicitLogPath;
|
||||||
|
} else {
|
||||||
|
const logDir = process.env.OPENCLAW_LOG_DIR || join(tmpdir(), 'openclaw');
|
||||||
|
if (existsSync(logDir)) {
|
||||||
|
const logs = readdirSync(logDir)
|
||||||
|
.filter((f) => /\.log$/i.test(f))
|
||||||
|
.map((f) => join(logDir, f))
|
||||||
|
.map((p) => ({ p, m: safeMtime(p) }))
|
||||||
|
.filter((x) => x.m > 0)
|
||||||
|
.sort((a, b) => b.m - a.m);
|
||||||
|
if (logs.length) logFile = logs[0].p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!logFile) return null;
|
||||||
|
|
||||||
|
let raw = readFileSync(logFile);
|
||||||
|
if (raw.length > MAX_LOG_BYTES) raw = raw.subarray(raw.length - MAX_LOG_BYTES);
|
||||||
|
return gzipSync(raw).toString('base64');
|
||||||
|
} catch (err) {
|
||||||
|
log('error', `collect logs failed: ${err.message}`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function safeMtime(p) {
|
||||||
|
try { return statSync(p).mtimeMs; } catch { return 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上报一个 bug。所有现场信息(指纹/版本/系统/日志)由本函数自动补齐,
|
||||||
|
* 调用方只需给 title(必填)和 description(可选)。
|
||||||
|
*
|
||||||
|
* @param {object} opts
|
||||||
|
* @param {string} opts.title 必填,bug 标题
|
||||||
|
* @param {string} [opts.description] 描述(用户填写或堆栈)
|
||||||
|
* @param {string} [opts.appRoot] 便携版根目录,用于算指纹和找版本文件,默认 cwd
|
||||||
|
* @param {string} [opts.logPath] 指定日志文件;不给则自动找最近的 openclaw 日志
|
||||||
|
* @param {boolean}[opts.includeLogs] 是否附带日志,默认 true
|
||||||
|
* @returns {Promise<{ok:boolean, id?:number, reason?:string}>}
|
||||||
|
*/
|
||||||
|
export async function submitBugReport(opts = {}) {
|
||||||
|
const { title, description, appRoot, logPath, includeLogs = true } = opts;
|
||||||
|
if (!title || typeof title !== 'string' || title.trim().length < 3) {
|
||||||
|
return { ok: false, reason: 'title-required' };
|
||||||
|
}
|
||||||
|
const root = appRoot || process.cwd();
|
||||||
|
|
||||||
|
// 指纹失败不阻止上报,匿名提交即可
|
||||||
|
let api_key = null;
|
||||||
|
let device_source = null;
|
||||||
|
try {
|
||||||
|
const fp = await getFingerprint(root);
|
||||||
|
device_source = fp.source;
|
||||||
|
api_key = buildApiKey(fp.fingerprint);
|
||||||
|
} catch (err) {
|
||||||
|
log('error', `fingerprint failed (上报为匿名): ${err.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const payload = {
|
||||||
|
api_key,
|
||||||
|
device_source,
|
||||||
|
openclaw_version: readOpenclawVersion(root),
|
||||||
|
title: title.trim(),
|
||||||
|
description: description || null,
|
||||||
|
logs_b64: includeLogs ? collectLogsB64(logPath) : null,
|
||||||
|
system_info: collectSystemInfo(),
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await fetchWithTimeout(getSubmitUrl(), {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(payload),
|
||||||
|
});
|
||||||
|
const data = await res.json().catch(() => ({}));
|
||||||
|
if (!res.ok) {
|
||||||
|
log('error', `submit failed: HTTP ${res.status} ${data.error || ''}`);
|
||||||
|
return { ok: false, reason: data.error || `http-${res.status}` };
|
||||||
|
}
|
||||||
|
log('info', `submitted bug #${data.id}`);
|
||||||
|
return { ok: true, id: data.id };
|
||||||
|
} catch (err) {
|
||||||
|
log('error', `submit failed: ${err.message}`);
|
||||||
|
return { ok: false, reason: err.message };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CLI 入口(自动崩溃上报用):
|
||||||
|
// node report-bug.mjs --auto --title "gateway-start-failed" [--desc "..."] [--log <文件>] [--root <便携版根>]
|
||||||
|
const isMain = (() => {
|
||||||
|
try {
|
||||||
|
if (!process.argv[1]) return false;
|
||||||
|
return import.meta.url === pathToFileURL(process.argv[1]).href;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
if (isMain) {
|
||||||
|
const args = process.argv.slice(2);
|
||||||
|
const getArg = (name) => {
|
||||||
|
const i = args.indexOf(name);
|
||||||
|
return i >= 0 && i + 1 < args.length ? args[i + 1] : null;
|
||||||
|
};
|
||||||
|
const title = getArg('--title') || 'auto-report';
|
||||||
|
const description = getArg('--desc');
|
||||||
|
const logPath = getArg('--log');
|
||||||
|
const appRoot = getArg('--root') || process.env.UCLAW_APP_ROOT || process.cwd();
|
||||||
|
|
||||||
|
submitBugReport({ title, description, appRoot, logPath })
|
||||||
|
.then((res) => {
|
||||||
|
process.stdout.write(`${JSON.stringify(res)}\n`);
|
||||||
|
process.exit(res.ok ? 0 : 1);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
// 即便崩了也别让上报本身把启动脚本拖挂
|
||||||
|
process.stderr.write(`report-bug fatal: ${err && err.message ? err.message : err}\n`);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -130,10 +130,10 @@ else
|
|||||||
PKGJSON
|
PKGJSON
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Install with China mirror
|
# Install with China mirror(缓存留盘内,拔盘不留痕)
|
||||||
NODE_BIN="$NODE_TARGET/bin/node"
|
NODE_BIN="$NODE_TARGET/bin/node"
|
||||||
NPM_BIN="$NODE_TARGET/bin/npm"
|
NPM_BIN="$NODE_TARGET/bin/npm"
|
||||||
"$NODE_BIN" "$NPM_BIN" install --prefix "$CORE_DIR" --registry="$MIRROR"
|
npm_config_cache="$APP_DIR/.npm-cache" "$NODE_BIN" "$NPM_BIN" install --prefix "$CORE_DIR" --registry="$MIRROR"
|
||||||
|
|
||||||
echo -e " ${GREEN}✓${NC} OpenClaw 安装完成"
|
echo -e " ${GREEN}✓${NC} OpenClaw 安装完成"
|
||||||
fi
|
fi
|
||||||
@@ -145,7 +145,7 @@ else
|
|||||||
echo -e " ${CYAN}↓${NC} 安装 QQ 插件..."
|
echo -e " ${CYAN}↓${NC} 安装 QQ 插件..."
|
||||||
NODE_BIN="$NODE_TARGET/bin/node"
|
NODE_BIN="$NODE_TARGET/bin/node"
|
||||||
NPM_BIN="$NODE_TARGET/bin/npm"
|
NPM_BIN="$NODE_TARGET/bin/npm"
|
||||||
"$NODE_BIN" "$NPM_BIN" install @sliverp/qqbot@latest --prefix "$CORE_DIR" --registry="$MIRROR" 2>/dev/null || true
|
npm_config_cache="$APP_DIR/.npm-cache" "$NODE_BIN" "$NPM_BIN" install @sliverp/qqbot@latest --prefix "$CORE_DIR" --registry="$MIRROR" 2>/dev/null || true
|
||||||
echo -e " ${GREEN}✓${NC} QQ 插件安装完成"
|
echo -e " ${GREEN}✓${NC} QQ 插件安装完成"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user