diff --git a/assistant/src/routes/cnb-board/live/live-content.ts b/assistant/src/routes/cnb-board/live/live-content.ts index e609e0e..650f787 100644 --- a/assistant/src/routes/cnb-board/live/live-content.ts +++ b/assistant/src/routes/cnb-board/live/live-content.ts @@ -1,6 +1,7 @@ import { useKey } from "@kevisual/context" import os from 'node:os'; +import { execSync } from 'node:child_process'; import dayjs from 'dayjs'; export const getLiveMdContent = (opts?: { more?: boolean }) => { @@ -78,6 +79,11 @@ export const getLiveMdContent = (opts?: { more?: boolean }) => { value: token, description: 'CNB 临时 Token,保持和环境变量 CNB_TOKEN 一致' }, + { + title: 'openWebUrl', + value: openWebUrl, + description: 'OpenWebUI 的访问地址,可以通过该地址访问 OpenWebUI 服务' + }, { title: 'openclawUrl', value: openclawUrl, @@ -112,7 +118,7 @@ export const getLiveMdContent = (opts?: { more?: boolean }) => { const createOSInfo = (more = false) => { const labels: Array<{ title: string; value: string; description: string }> = [] - const startTimer = useKey('CNB_BUILD_START_TIME') || 0 + const startTimer = useKey('CNB_BUILD_START_TIME') || '' // CPU 使用率 const cpus = os.cpus() @@ -126,11 +132,27 @@ const createOSInfo = (more = false) => { }) const cpuUsage = ((1 - totalIdle / totalTick) * 100).toFixed(2) - // 内存使用情况 - const totalMem = os.totalmem() - const freeMem = os.freemem() - const usedMem = totalMem - freeMem - const memUsage = ((usedMem / totalMem) * 100).toFixed(2) + // 内存使用情况 (使用 free 命令) + let memUsed = 0 + let memTotal = 0 + let memFree = 0 + try { + const freeOutput = execSync('free -b', { encoding: 'utf-8' }) + const lines = freeOutput.trim().split('\n') + const memLine = lines.find(line => line.startsWith('Mem:')) + if (memLine) { + const parts = memLine.split(/\s+/) + memTotal = parseInt(parts[1]) + memUsed = parseInt(parts[2]) + memFree = parseInt(parts[3]) + } + } catch (e) { + // 如果 free 命令失败,使用 os 模块 + memTotal = os.totalmem() + memFree = os.freemem() + memUsed = memTotal - memFree + } + const memUsage = memTotal > 0 ? ((memUsed / memTotal) * 100).toFixed(2) : '0.00' // 格式化字节为人类可读格式 const formatBytes = (bytes: number) => { @@ -142,8 +164,6 @@ const createOSInfo = (more = false) => { // 启动时间 const bootTime = os.uptime() - const bootTimeDate = new Date(Date.now() - bootTime * 1000) - const bootTimeStr = dayjs(bootTimeDate).format('YYYY-MM-DD HH:mm:ss') // 运行时间格式化 const formatUptime = (seconds: number) => { @@ -154,10 +174,14 @@ const createOSInfo = (more = false) => { return `${days}天 ${hours}小时 ${minutes}分钟 ${secs}秒` } - // 磁盘大小 (假设获取 / 目录) - // 注意: Node.js 原生不提供磁盘大小,需要通过 child_process 或假设值 - // 这里使用内存作为参考,实际磁盘需要额外处理 - const diskInfo = '可通过 df -h 命令获取' + // 磁盘使用情况 (使用 du 命令,获取当前目录) + let diskUsage = '' + try { + const duOutput = execSync('du -sh .', { encoding: 'utf-8' }) + diskUsage = duOutput.trim().split('\t')[0] + } catch (e) { + diskUsage = '获取失败' + } labels.push( { @@ -172,28 +196,28 @@ const createOSInfo = (more = false) => { }, { title: 'memoryUsed', - value: formatBytes(usedMem), + value: formatBytes(memUsed), description: '已使用内存' }, { title: 'memoryTotal', - value: formatBytes(totalMem), + value: formatBytes(memTotal), description: '总内存' }, + { + title: 'memoryFree', + value: formatBytes(memFree), + description: '空闲内存' + }, { title: 'memoryUsage', value: `${memUsage}%`, description: '内存使用率' }, { - title: 'diskInfo', - value: diskInfo, - description: '磁盘信息 (请使用 df -h 命令查看)' - }, - { - title: 'bootTime', - value: bootTimeStr, - description: '系统启动时间' + title: 'diskUsage', + value: diskUsage, + description: '当前目录磁盘使用情况' }, { title: 'uptime', @@ -204,9 +228,13 @@ const createOSInfo = (more = false) => { // 如果有 CNB_BUILD_START_TIME,添加构建启动时间 if (startTimer) { - const buildStartTime = dayjs(parseInt(startTimer as string) * 1000).format('YYYY-MM-DD HH:mm:ss') - const buildUptime = Date.now() - parseInt(startTimer as string) * 1000 + // startTimer 是日期字符串格式 + const buildStartTime = dayjs(startTimer as string).format('YYYY-MM-DD HH:mm:ss') + const buildStartTimestamp = dayjs(startTimer as string).valueOf() + const buildUptime = Date.now() - buildStartTimestamp const buildUptimeStr = formatUptime(Math.floor(buildUptime / 1000)) + const maxRunTime = useKey('CNB_PIPELINE_MAX_RUN_TIME') || 0 // 毫秒 + labels.push( { title: 'buildStartTime', @@ -219,6 +247,28 @@ const createOSInfo = (more = false) => { description: '构建已运行时间' } ) + if (maxRunTime > 0) { + // 计算到达4点的倒计时 + const now = dayjs() + const today4am = now.hour(4).minute(0).second(0).millisecond(0) + let timeTo4 = today4am.valueOf() - now.valueOf() + if (timeTo4 < 0) { + // 如果已经过了4点,计算到明天4点 + timeTo4 = today4am.add(1, 'day').valueOf() - now.valueOf() + } + const timeTo4Str = `[距离晚上4点重启时间: ${formatUptime(Math.floor(timeTo4 / 1000))}]` + + labels.push({ + title: 'buildMaxRunTime', + value: formatUptime(Math.floor(maxRunTime / 1000)), + description: '构建最大运行时间(限制时间)' + }) + labels.unshift({ + title: '剩余时间', + value: formatUptime(Math.floor((maxRunTime - buildUptime) / 1000)) + ' ' + timeTo4Str, + description: '构建剩余时间' + }) + } } // more 为 true 时添加更多系统信息 diff --git a/package.json b/package.json index 07a6fe1..15309b1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@kevisual/cli", - "version": "0.1.10", + "version": "0.1.11", "description": "envision 命令行工具", "type": "module", "basename": "/root/cli",