8 Commits

Author SHA1 Message Date
8579d58890 fix: 筛选按钮改为顶部横向带文字显示 2026-04-23 16:56:57 +08:00
f8a32ab6be fix: 更新版本号v2.3 2026-04-23 16:52:27 +08:00
56975f9b0d feat: 筛选按钮改为右侧固定导航按钮 2026-04-23 16:50:41 +08:00
b2900febf9 fix: 更新版本号标题为v2.2 2026-04-23 12:57:07 +08:00
17e946ae56 feat: 自定义新增外部web服务功能 2026-04-23 12:55:56 +08:00
c320066b63 feat: Web服务卡片添加自定义链接入口功能
- 端口号/后台后添加绿色+按钮
- 点击弹出模态框输入自定义链接
- 支持端口+路径生成URL或完整URL
- 链接保存到localStorage,支持删除
- 使用网页统一设置的IP生成链接
2026-04-23 11:33:11 +08:00
854b6b6f82 feat: 添加语音对话网页服务(19019) 2026-04-21 21:20:26 +08:00
6d8410ac28 feat: 添加图片编辑器服务 2026-04-21 12:06:00 +08:00
10 changed files with 477 additions and 113 deletions

368
app.py
View File

@@ -1,8 +1,9 @@
#!/usr/bin/env python3
"""
项目服务管理面板 v2.1.0
项目服务管理面板 v2.2.0
端口: 19013
修复: 后台链接使用 externalIp 替代 localhost
新增: Web服务卡片添加自定义链接入口+按钮)
新增: 自定义新增外部web服务功能新增服务按钮
"""
import os
@@ -934,7 +935,7 @@ HTML_TEMPLATE = '''<!DOCTYPE html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>项目服务管理面板 v2.1</title>
<title>项目服务管理面板 v2.4</title>
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>📊</text></svg>">
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://cdn.jsdelivr.net/npm/remixicon@3.5.0/fonts/remixicon.css" rel="stylesheet">
@@ -958,6 +959,12 @@ HTML_TEMPLATE = '''<!DOCTYPE html>
.nav-btn:hover { background: #475569; transform: scale(1.1); }
.nav-btn i { font-size: 20px; color: #94a3b8; }
.nav-btn:hover i { color: #f1f5f9; }
.filter-bar { position: sticky; top: 0; z-index: 50; display: flex; gap: 8px; padding: 12px 0; background: #0f172a; margin-bottom: 16px; flex-wrap: wrap; }
.filter-bar-btn { display: flex; align-items: center; gap: 6px; padding: 8px 16px; border-radius: 8px; background: #1e293b; border: 1px solid #334155; color: #94a3b8; font-size: 14px; cursor: pointer; transition: all 0.2s; }
.filter-bar-btn:hover { background: #334155; color: #f1f5f9; }
.filter-bar-btn.active { background: #3b82f6; border-color: #3b82f6; color: #fff; }
.filter-bar-btn.add-btn { background: #22c55e; border-color: #22c55e; color: #fff; }
.filter-bar-btn.add-btn:hover { background: #16a34a; }
.tab-btn { border-bottom: 2px solid transparent; }
.tab-btn.active { border-bottom-color: #3b82f6; }
.cron-card { transition: all 0.2s; }
@@ -1056,12 +1063,14 @@ HTML_TEMPLATE = '''<!DOCTYPE html>
</div>
</div>
<!-- 筛选器 -->
<div class="flex gap-2 mb-4">
<button onclick="filterType('all')" class="filter-btn px-3 py-1 rounded-lg bg-gray-700 hover:bg-gray-600 text-sm" data-type="all">全部</button>
<button onclick="filterType('web')" class="filter-btn px-3 py-1 rounded-lg bg-gray-700 hover:bg-gray-600 text-sm" data-type="web">Web服务</button>
<button onclick="filterType('cli')" class="filter-btn px-3 py-1 rounded-lg bg-gray-700 hover:bg-gray-600 text-sm" data-type="cli">CLI工具</button>
<button onclick="filterType('extension')" class="filter-btn px-3 py-1 rounded-lg bg-gray-700 hover:bg-gray-600 text-sm" data-type="extension">插件</button>
<!-- 筛选器(顶部固定) -->
<div class="filter-bar">
<button onclick="filterType('all')" class="filter-bar-btn active" data-type="all"><i class="ri-apps-line"></i> 全部</button>
<button onclick="filterType('web')" class="filter-bar-btn" data-type="web"><i class="ri-server-line"></i> Web服务</button>
<button onclick="filterType('custom')" class="filter-bar-btn" data-type="custom"><i class="ri-global-line"></i> 自定义</button>
<button onclick="filterType('cli')" class="filter-bar-btn" data-type="cli"><i class="ri-terminal-box-line"></i> CLI工具</button>
<button onclick="filterType('extension')" class="filter-bar-btn" data-type="extension"><i class="ri-chrome-line"></i> 插件</button>
<button onclick="showAddServiceModal()" class="filter-bar-btn add-btn"><i class="ri-add-circle-line"></i> 新增服务</button>
</div>
<!-- 项目列表 -->
@@ -1218,6 +1227,96 @@ HTML_TEMPLATE = '''<!DOCTYPE html>
</div>
</div>
<!-- 自定义链接模态框 -->
<div id="linkModal" class="modal-overlay hidden">
<div class="card rounded-xl modal-content p-6">
<div class="flex items-center justify-between mb-4">
<h3 id="linkModalTitle" class="font-bold text-lg">添加自定义链接</h3>
<button onclick="closeLinkModal()" class="text-gray-400 hover:text-white"><i class="ri-close-line text-xl"></i></button>
</div>
<form id="linkForm" onsubmit="saveCustomLink(event)">
<input type="hidden" id="linkProjectId">
<div class="mb-3">
<label class="block text-gray-400 text-sm mb-1">链接名称 *</label>
<input type="text" id="linkName" required class="w-full bg-gray-700 text-gray-200 px-3 py-2 rounded-lg" placeholder="例如API文档、管理后台">
</div>
<div class="mb-3">
<label class="block text-gray-400 text-sm mb-1">端口使用统一IP</label>
<input type="text" id="linkPort" class="w-full bg-gray-700 text-gray-200 px-3 py-2 rounded-lg" placeholder="例如19001" onchange="updateLinkPreview()">
</div>
<div class="mb-3">
<label class="block text-gray-400 text-sm mb-1">路径(可选)</label>
<input type="text" id="linkPath" class="w-full bg-gray-700 text-gray-200 px-3 py-2 rounded-lg" placeholder="例如:/admin /api/docs" onchange="updateLinkPreview()">
<p id="linkPreview" class="text-sm text-cyan-400 mt-1"></p>
</div>
<div class="mb-4">
<label class="block text-gray-400 text-sm mb-1">完整URL不填则用端口生成</label>
<input type="text" id="linkFullUrl" class="w-full bg-gray-700 text-gray-200 px-3 py-2 rounded-lg" placeholder="例如http://192.168.2.17:19000/docs">
</div>
<div id="existingLinks" class="mb-4"></div>
<div class="flex justify-end gap-2">
<button type="button" onclick="closeLinkModal()" class="btn bg-gray-600 hover:bg-gray-700 px-4 py-2 rounded-lg">取消</button>
<button type="submit" class="btn bg-green-600 hover:bg-green-700 px-4 py-2 rounded-lg">添加</button>
</div>
</form>
</div>
</div>
<!-- 新增自定义服务模态框 -->
<div id="addServiceModal" class="modal-overlay hidden">
<div class="card rounded-xl modal-content p-6">
<div class="flex items-center justify-between mb-4">
<h3 class="font-bold text-lg">新增自定义服务</h3>
<button onclick="closeAddServiceModal()" class="text-gray-400 hover:text-white"><i class="ri-close-line text-xl"></i></button>
</div>
<form id="addServiceForm" onsubmit="saveCustomService(event)">
<div class="mb-3">
<label class="block text-gray-400 text-sm mb-1">服务名称 *</label>
<input type="text" id="serviceName" required class="w-full bg-gray-700 text-gray-200 px-3 py-2 rounded-lg" placeholder="例如外部API、监控面板">
</div>
<div class="mb-3">
<label class="block text-gray-400 text-sm mb-1">端口使用统一IP</label>
<input type="text" id="servicePort" class="w-full bg-gray-700 text-gray-200 px-3 py-2 rounded-lg" placeholder="例如8080" onchange="updateServicePreview()">
</div>
<div class="mb-3">
<label class="block text-gray-400 text-sm mb-1">路径(可选)</label>
<input type="text" id="servicePath" class="w-full bg-gray-700 text-gray-200 px-3 py-2 rounded-lg" placeholder="例如:/admin" onchange="updateServicePreview()">
<p id="servicePreview" class="text-sm text-cyan-400 mt-1"></p>
</div>
<div class="mb-3">
<label class="block text-gray-400 text-sm mb-1">完整URL不填则用端口生成</label>
<input type="text" id="serviceFullUrl" class="w-full bg-gray-700 text-gray-200 px-3 py-2 rounded-lg" placeholder="例如http://external.com:8080">
</div>
<div class="mb-3">
<label class="block text-gray-400 text-sm mb-1">后台URL可选</label>
<input type="text" id="serviceAdminUrl" class="w-full bg-gray-700 text-gray-200 px-3 py-2 rounded-lg" placeholder="例如http://external.com:8080/admin">
</div>
<div class="mb-3">
<label class="block text-gray-400 text-sm mb-1">描述</label>
<input type="text" id="serviceDesc" class="w-full bg-gray-700 text-gray-200 px-3 py-2 rounded-lg" placeholder="简要描述">
</div>
<div class="flex justify-end gap-2">
<button type="button" onclick="closeAddServiceModal()" class="btn bg-gray-600 hover:bg-gray-700 px-4 py-2 rounded-lg">取消</button>
<button type="submit" class="btn bg-green-600 hover:bg-green-700 px-4 py-2 rounded-lg">添加</button>
</div>
</form>
</div>
</div>
<!-- 导航按钮 -->
<div class="nav-buttons">
<button onclick="scrollToTop()" class="nav-btn" title="回到顶部"><i class="ri-arrow-up-line"></i></button>
@@ -1286,16 +1385,20 @@ HTML_TEMPLATE = '''<!DOCTYPE html>
function filterType(type) {
currentFilter = type;
document.querySelectorAll('.filter-btn').forEach(btn => {
btn.classList.toggle('bg-blue-600', btn.dataset.type === type);
btn.classList.toggle('bg-gray-700', btn.dataset.type !== type);
document.querySelectorAll('.filter-bar-btn').forEach(btn => {
btn.classList.toggle('active', btn.dataset.type === type);
});
renderProjects();
}
function renderProjects() {
const list = document.getElementById('projectsList');
const filtered = currentFilter === 'all' ? projects : projects.filter(p => p.type === currentFilter);
// 合合自定义服务到项目列表
const customServices = getCustomServices();
const allProjects = [...projects, ...customServices];
const filtered = currentFilter === 'all' ? allProjects : allProjects.filter(p => p.type === currentFilter || (currentFilter === 'web' && p.isCustom));
if (filtered.length === 0) {
list.innerHTML = '<div class="text-center py-12 text-gray-400"><i class="ri-folder-open-line text-4xl"></i><p class="mt-2">暂无项目</p></div>';
@@ -1305,13 +1408,15 @@ HTML_TEMPLATE = '''<!DOCTYPE html>
const activeStatus = getActiveStatus();
const grouped = {};
filtered.forEach(p => {
if (!grouped[p.type]) grouped[p.type] = [];
grouped[p.type].push(p);
const groupType = p.isCustom ? 'custom' : p.type;
if (!grouped[groupType]) grouped[groupType] = [];
grouped[groupType].push(p);
});
let html = '';
const typeNames = {
'web': { name: 'Web服务', icon: 'ri-server-line', color: 'blue' },
'custom': { name: '自定义服务', icon: 'ri-global-line', color: 'cyan' },
'cron': { name: 'Cron任务', icon: 'ri-time-line', color: 'yellow' },
'cli': { name: 'CLI工具', icon: 'ri-terminal-box-line', color: 'purple' },
'extension': { name: '浏览器插件', icon: 'ri-chrome-line', color: 'pink' }
@@ -1357,6 +1462,27 @@ HTML_TEMPLATE = '''<!DOCTYPE html>
}
function renderProjectCard(p, isActive = true) {
// 自定义服务特殊处理
if (p.isCustom) {
return `
<div class="card rounded-lg p-3 hover:border-cyan-500 transition-colors border-cyan-500/30">
<div class="flex items-center justify-between mb-2">
<div class="flex items-center gap-2">
<div class="status-dot bg-cyan-400" style="box-shadow: 0 0 8px #22d3ee;" title="外部服务"></div>
<h3 class="font-semibold text-sm truncate">${p.name}</h3>
</div>
<span class="text-xs text-cyan-400">外部</span>
</div>
<p class="text-gray-400 text-xs mb-2 truncate">${p.description || ''}</p>
<div class="flex items-center gap-1 text-xs flex-wrap">
<a href="${p.url}" target="_blank" class="px-2 py-0.5 rounded bg-cyan-500/20 text-cyan-400 hover:bg-cyan-500/30">访问</a>
${p.admin_url ? `<a href="${p.admin_url}" target="_blank" class="text-yellow-400 hover:text-yellow-300 ml-1">后台</a>` : ''}
<button onclick="deleteCustomService('${p.id}')" class="text-red-400 hover:text-red-300 ml-2" title="删除服务"><i class="ri-delete-bin-line"></i></button>
</div>
</div>
`;
}
const statusInfo = getStatusInfo(p.status?.status);
// 处理后台链接:将 localhost 替换为 externalIp
@@ -1365,6 +1491,9 @@ HTML_TEMPLATE = '''<!DOCTYPE html>
adminUrl = adminUrl.replace(/localhost/g, externalIp);
}
// 获取自定义链接
const customLinks = getCustomLinks(p.id);
return `
<div class="card rounded-lg p-3 hover:border-gray-500 transition-colors">
<div class="flex items-center justify-between mb-2">
@@ -1375,13 +1504,15 @@ HTML_TEMPLATE = '''<!DOCTYPE html>
<span class="text-xs ${statusInfo.textColor}">${statusInfo.text}</span>
</div>
${p.ports && p.ports.length > 0 ? `
<div class="flex items-center gap-1 text-xs mb-2">
<div class="flex items-center gap-1 text-xs mb-2 flex-wrap">
${p.ports.map(port => {
const portStatus = p.status?.ports?.[port];
const isRunning = portStatus?.running;
return `<a href="http://${externalIp}:${port}" target="_blank" class="px-2 py-0.5 rounded ${isRunning ? 'bg-green-500/20 text-green-400 hover:bg-green-500/30' : 'bg-red-500/20 text-red-400'}">${port}</a>`;
}).join('')}
${adminUrl ? `<a href="${adminUrl}" target="_blank" class="text-yellow-400 hover:text-yellow-300 ml-1">后台</a>` : ''}
${customLinks.map(link => `<a href="${link.url}" target="_blank" class="text-cyan-400 hover:text-cyan-300 ml-1">${link.name}</a>`).join('')}
<button onclick="showAddLinkModal('${p.id}', '${p.name}')" class="text-green-400 hover:text-green-300 ml-1" title="添加自定义链接"><i class="ri-add-line"></i></button>
</div>
` : ''}
${p.type === 'web' ? `
@@ -1401,6 +1532,211 @@ HTML_TEMPLATE = '''<!DOCTYPE html>
</div>
`;
}
// ==================== 自定义链接管理 ====================
function getCustomLinks(projectId) {
const stored = localStorage.getItem('customLinks_' + projectId);
return stored ? JSON.parse(stored) : [];
}
function saveCustomLinks(projectId, links) {
localStorage.setItem('customLinks_' + projectId, JSON.stringify(links));
}
function showAddLinkModal(projectId, projectName) {
document.getElementById('linkModalTitle').textContent = `${projectName} - 添加链接`;
document.getElementById('linkProjectId').value = projectId;
document.getElementById('linkName').value = '';
document.getElementById('linkPort').value = '';
document.getElementById('linkPath').value = '';
document.getElementById('linkFullUrl').value = '';
document.getElementById('linkModal').classList.remove('hidden');
// 显示已有的自定义链接(供删除)
const existingLinks = getCustomLinks(projectId);
const existingList = document.getElementById('existingLinks');
if (existingLinks.length > 0) {
existingList.innerHTML = '<div class="text-gray-400 text-sm mb-2">已有链接:</div>' +
existingLinks.map(link => `
<div class="flex items-center justify-between bg-gray-700 px-2 py-1 rounded mb-1">
<span class="text-cyan-400 text-sm">${link.name}: ${link.url}</span>
<button onclick="deleteCustomLink('${projectId}', '${link.id}')" class="text-red-400 hover:text-red-300"><i class="ri-delete-bin-line"></i></button>
</div>
`).join('');
} else {
existingList.innerHTML = '';
}
}
function closeLinkModal() {
document.getElementById('linkModal').classList.add('hidden');
}
function saveCustomLink(event) {
event.preventDefault();
const projectId = document.getElementById('linkProjectId').value;
const name = document.getElementById('linkName').value.trim();
const port = document.getElementById('linkPort').value.trim();
const path = document.getElementById('linkPath').value.trim();
const fullUrl = document.getElementById('linkFullUrl').value.trim();
if (!name) {
alert('请输入链接名称');
return;
}
let url;
if (fullUrl) {
url = fullUrl;
} else if (port) {
url = `http://${externalIp}:${port}${path || ''}`;
} else {
alert('请输入端口或完整URL');
return;
}
const links = getCustomLinks(projectId);
const newLink = {
id: Date.now().toString(),
name: name,
url: url
};
links.push(newLink);
saveCustomLinks(projectId, links);
closeLinkModal();
renderProjects();
}
function deleteCustomLink(projectId, linkId) {
if (!confirm('确定删除此链接?')) return;
const links = getCustomLinks(projectId);
const filtered = links.filter(l => l.id !== linkId);
saveCustomLinks(projectId, filtered);
// 更新模态框中的显示
const existingLinks = getCustomLinks(projectId);
const existingList = document.getElementById('existingLinks');
if (existingLinks.length > 0) {
existingList.innerHTML = '<div class="text-gray-400 text-sm mb-2">已有链接:</div>' +
existingLinks.map(link => `
<div class="flex items-center justify-between bg-gray-700 px-2 py-1 rounded mb-1">
<span class="text-cyan-400 text-sm">${link.name}: ${link.url}</span>
<button onclick="deleteCustomLink('${projectId}', '${link.id}')" class="text-red-400 hover:text-red-300"><i class="ri-delete-bin-line"></i></button>
</div>
`).join('');
} else {
existingList.innerHTML = '<div class="text-gray-500 text-sm">暂无自定义链接</div>';
}
renderProjects();
}
// 自动生成URL预览
function updateLinkPreview() {
const port = document.getElementById('linkPort').value.trim();
const path = document.getElementById('linkPath').value.trim();
const preview = document.getElementById('linkPreview');
if (port) {
preview.textContent = `预览: http://${externalIp}:${port}${path || ''}`;
} else {
preview.textContent = '';
}
}
// ==================== 自定义服务管理 ====================
function getCustomServices() {
const stored = localStorage.getItem('customServices');
return stored ? JSON.parse(stored) : [];
}
function saveCustomServices(services) {
localStorage.setItem('customServices', JSON.stringify(services));
}
function showAddServiceModal() {
document.getElementById('serviceName').value = '';
document.getElementById('servicePort').value = '';
document.getElementById('servicePath').value = '';
document.getElementById('serviceFullUrl').value = '';
document.getElementById('serviceAdminUrl').value = '';
document.getElementById('serviceDesc').value = '';
document.getElementById('servicePreview').textContent = '';
document.getElementById('addServiceModal').classList.remove('hidden');
}
function closeAddServiceModal() {
document.getElementById('addServiceModal').classList.add('hidden');
}
function updateServicePreview() {
const port = document.getElementById('servicePort').value.trim();
const path = document.getElementById('servicePath').value.trim();
const preview = document.getElementById('servicePreview');
if (port) {
preview.textContent = `预览: http://${externalIp}:${port}${path || ''}`;
} else {
preview.textContent = '';
}
}
function saveCustomService(event) {
event.preventDefault();
const name = document.getElementById('serviceName').value.trim();
const port = document.getElementById('servicePort').value.trim();
const path = document.getElementById('servicePath').value.trim();
const fullUrl = document.getElementById('serviceFullUrl').value.trim();
const adminUrl = document.getElementById('serviceAdminUrl').value.trim();
const desc = document.getElementById('serviceDesc').value.trim();
if (!name) {
alert('请输入服务名称');
return;
}
let mainUrl;
if (fullUrl) {
mainUrl = fullUrl;
} else if (port) {
mainUrl = `http://${externalIp}:${port}${path || ''}`;
} else {
alert('请输入端口或完整URL');
return;
}
const services = getCustomServices();
const newService = {
id: 'custom_' + Date.now(),
name: name,
url: mainUrl,
admin_url: adminUrl || null,
description: desc || '自定义外部服务',
type: 'custom',
isCustom: true
};
services.push(newService);
saveCustomServices(services);
closeAddServiceModal();
loadProjects();
}
function deleteCustomService(serviceId) {
if (!confirm('确定删除此自定义服务?')) return;
const services = getCustomServices();
const filtered = services.filter(s => s.id !== serviceId);
saveCustomServices(filtered);
loadProjects();
}
function getStatusInfo(status) {
const map = {

View File

@@ -0,0 +1,12 @@
*/30 * * * * openclaw agent --agent zuitoushang --message '执行下心跳任务' >> /home/xian/.openclaw/workspace-zuitoushang/works/heartbeat_logs/$(date +\%Y-\%m-\%d_\%H:\%M:\%S).log 2>&1
0 * * * * /usr/bin/python3 /home/xian/.copaw/workspaces/default/active_skills/system-monitor/scripts/monitor.py --alert >> /home/xian/.copaw/workspaces/default/works/system-monitor.log 2>&1
0 * * * * /home/xian/.openclaw/workspace-zuitoushang/scripts/cleanup-chrome-zombies.sh
0 3 * * * /home/xian/.nvm/versions/node/v24.14.0/bin/node /home/xian/.openclaw/workspace-zuitoushang/scripts/daily-summary.js >> /home/xian/.openclaw/workspace-zuitoushang/works/daily-summary.log 2>&1
0 * * * * /usr/bin/python3 /home/xian/.openclaw/workspace-zuitoushang/scripts/cpu-monitor.py >> /home/xian/.openclaw/workspace-zuitoushang/works/cpu-monitor.log 2>&1
0 8-22 * * * /usr/bin/python3 /home/xian/.openclaw/workspace-zuitoushang/scripts/disk-monitor.py >> /home/xian/.openclaw/workspace-zuitoushang/works/disk-monitor.log 2>&1
*/20 * * * * /usr/bin/python3 /home/xian/.openclaw/workspace-coder/works/service-monitor/monitor.py >> /home/xian/.openclaw/workspace-coder/works/service-monitor/monitor.log 2>&1
0 17 * * 1-5 /usr/bin/python3 /home/xian/.openclaw/workspace-coder/works/board-monitor/board_monitor.py report >> /home/xian/.openclaw/workspace-coder/works/board-monitor/report.log 2>&1
0 17 * * 1-5 /usr/bin/python3 /home/xian/.openclaw/common/stock_system/cron_daily_fetch.py >> /home/xian/.openclaw/common/stock_system/logs/daily_fetch.log 2>&1
0 4 * * * /home/xian/.openclaw/workspace-coder/works/xian-favor/scripts/auto_backup.py >> /tmp/xian-favor-backup.log 2>&1
0 12 * * * cd /home/xian/.openclaw/workspace-laoli && /home/xian/.openclaw/workspace-laoli/works/send_coder_stats.sh
10 3 * * * openclaw agent --agent zuitoushang --message '总结并记忆昨天和你的会话内容' >> /home/xian/.openclaw/workspace-zuitoushang/works/daily-memory/$(date +\%Y-\%m-\%d).log 2>&1

View File

@@ -0,0 +1,12 @@
*/30 * * * * openclaw agent --agent zuitoushang --message '执行下心跳任务' >> /home/xian/.openclaw/workspace-zuitoushang/works/heartbeat_logs/$(date +\%Y-\%m-\%d_\%H:\%M:\%S).log 2>&1
0 * * * * /usr/bin/python3 /home/xian/.copaw/workspaces/default/active_skills/system-monitor/scripts/monitor.py --alert >> /home/xian/.copaw/workspaces/default/works/system-monitor.log 2>&1
0 * * * * /home/xian/.openclaw/workspace-zuitoushang/scripts/cleanup-chrome-zombies.sh
0 3 * * * /home/xian/.nvm/versions/node/v24.14.0/bin/node /home/xian/.openclaw/workspace-zuitoushang/scripts/daily-summary.js >> /home/xian/.openclaw/workspace-zuitoushang/works/daily-summary.log 2>&1
0 * * * * /usr/bin/python3 /home/xian/.openclaw/workspace-zuitoushang/scripts/cpu-monitor.py >> /home/xian/.openclaw/workspace-zuitoushang/works/cpu-monitor.log 2>&1
0 8-22 * * * /usr/bin/python3 /home/xian/.openclaw/workspace-zuitoushang/scripts/disk-monitor.py >> /home/xian/.openclaw/workspace-zuitoushang/works/disk-monitor.log 2>&1
*/20 * * * * /usr/bin/python3 /home/xian/.openclaw/workspace-coder/works/service-monitor/monitor.py >> /home/xian/.openclaw/workspace-coder/works/service-monitor/monitor.log 2>&1
0 17 * * 1-5 /usr/bin/python3 /home/xian/.openclaw/workspace-coder/works/board-monitor/board_monitor.py report >> /home/xian/.openclaw/workspace-coder/works/board-monitor/report.log 2>&1
0 17 * * 1-5 /usr/bin/python3 /home/xian/.openclaw/common/stock_system/cron_daily_fetch.py >> /home/xian/.openclaw/common/stock_system/logs/daily_fetch.log 2>&1
0 4 * * * /home/xian/.openclaw/workspace-coder/works/xian-favor/scripts/auto_backup.py >> /tmp/xian-favor-backup.log 2>&1
0 12 * * * cd /home/xian/.openclaw/workspace-laoli && /home/xian/.openclaw/workspace-laoli/works/send_coder_stats.sh
10 3 * * * openclaw agent --agent zuitoushang --message '总结并记忆昨天和你的会话内容' >> /home/xian/.openclaw/workspace-zuitoushang/works/daily-memory/$(date +\%Y-\%m-\%d).log 2>&1

BIN
cron_manager.db Normal file

Binary file not shown.

View File

@@ -1,8 +1,8 @@
[2026-04-20 12:09:27] ==================================================
[2026-04-20 12:09:27] 项目服务管理面板启动
[2026-04-20 12:09:27] 访问地址: http://localhost:19013
[2026-04-20 12:09:27] 进程PID: 3701577
[2026-04-20 12:09:27] ==================================================
[2026-04-23 16:56:14] ==================================================
[2026-04-23 16:56:14] 项目服务管理面板 v2.0.0 启动
[2026-04-23 16:56:14] 访问地址: http://localhost:19013
[2026-04-23 16:56:14] 进程PID: 1118301
[2026-04-23 16:56:14] ==================================================
* Serving Flask app 'app'
* Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
@@ -10,95 +10,27 @@ WARNING: This is a development server. Do not use it in a production deployment.
* Running on http://127.0.0.1:19013
* Running on http://192.168.2.17:19013
Press CTRL+C to quit
127.0.0.1 - - [20/Apr/2026 12:09:30] "GET / HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 12:09:32] "GET /api/projects HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 12:09:35] "GET / HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 12:09:36] "GET /api/projects HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 12:09:37] "GET / HTTP/1.1" 200 -
192.168.2.8 - - [20/Apr/2026 12:09:38] "GET /api/projects HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 12:09:46] "GET / HTTP/1.1" 200 -
192.168.2.8 - - [20/Apr/2026 12:09:48] "GET /api/projects HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 12:09:53] "GET / HTTP/1.1" 200 -
192.168.2.8 - - [20/Apr/2026 12:09:55] "GET /api/projects HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 12:09:56] "GET / HTTP/1.1" 200 -
192.168.2.8 - - [20/Apr/2026 12:09:58] "GET /api/projects HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 12:10:06] "GET / HTTP/1.1" 200 -
192.168.2.8 - - [20/Apr/2026 12:10:08] "GET /api/projects HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 12:10:16] "GET / HTTP/1.1" 200 -
192.168.2.8 - - [20/Apr/2026 12:10:18] "GET /api/projects HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 12:10:23] "GET / HTTP/1.1" 200 -
192.168.2.8 - - [20/Apr/2026 12:10:25] "GET /api/projects HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 12:10:26] "GET / HTTP/1.1" 200 -
192.168.2.8 - - [20/Apr/2026 12:10:28] "GET /api/projects HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 12:10:30] "GET / HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 12:10:32] "GET /api/projects HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 12:10:35] "GET / HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 12:10:36] "GET /api/projects HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 12:10:37] "GET / HTTP/1.1" 200 -
192.168.2.8 - - [20/Apr/2026 12:10:38] "GET /api/projects HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 12:10:47] "GET / HTTP/1.1" 200 -
192.168.2.8 - - [20/Apr/2026 12:10:47] "GET / HTTP/1.1" 200 -
192.168.2.8 - - [20/Apr/2026 12:10:48] "GET /api/crons HTTP/1.1" 200 -
192.168.2.8 - - [20/Apr/2026 12:10:48] "GET /api/projects HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 12:10:51] "GET / HTTP/1.1" 200 -
192.168.2.8 - - [20/Apr/2026 12:10:52] "GET /api/projects HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 12:10:54] "GET / HTTP/1.1" 200 -
192.168.2.8 - - [20/Apr/2026 12:10:55] "GET /api/projects HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 12:10:57] "GET / HTTP/1.1" 200 -
192.168.2.8 - - [20/Apr/2026 12:10:58] "GET /api/projects HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 12:11:01] "GET / HTTP/1.1" 200 -
192.168.2.8 - - [20/Apr/2026 12:11:02] "GET /api/projects HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 12:11:07] "GET / HTTP/1.1" 200 -
192.168.2.8 - - [20/Apr/2026 12:11:08] "GET /api/projects HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 12:11:11] "GET / HTTP/1.1" 200 -
192.168.2.8 - - [20/Apr/2026 12:11:12] "GET /api/projects HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 12:11:16] "GET / HTTP/1.1" 200 -
192.168.2.8 - - [20/Apr/2026 12:11:18] "GET /api/projects HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 12:11:21] "GET / HTTP/1.1" 200 -
192.168.2.8 - - [20/Apr/2026 12:11:22] "GET /api/projects HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 12:11:24] "GET / HTTP/1.1" 200 -
192.168.2.8 - - [20/Apr/2026 12:11:25] "GET /api/projects HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 12:11:25] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 12:11:27] "GET / HTTP/1.1" 200 -
192.168.2.8 - - [20/Apr/2026 12:11:27] "GET /api/projects HTTP/1.1" 200 -
192.168.2.8 - - [20/Apr/2026 12:11:28] "GET /api/projects HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 12:11:30] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 12:11:31] "GET / HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 12:11:32] "GET /api/projects HTTP/1.1" 200 -
192.168.2.8 - - [20/Apr/2026 12:11:32] "GET /api/projects HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 12:11:35] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 12:11:37] "GET / HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 12:11:37] "GET /api/projects HTTP/1.1" 200 -
192.168.2.8 - - [20/Apr/2026 12:11:38] "GET /api/projects HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 12:11:41] "GET / HTTP/1.1" 200 -
192.168.2.8 - - [20/Apr/2026 12:11:42] "GET /api/projects HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 12:11:47] "GET / HTTP/1.1" 200 -
192.168.2.8 - - [20/Apr/2026 12:11:48] "GET /api/projects HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 12:11:51] "GET / HTTP/1.1" 200 -
192.168.2.8 - - [20/Apr/2026 12:11:52] "GET /api/projects HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 12:11:54] "GET / HTTP/1.1" 200 -
192.168.2.8 - - [20/Apr/2026 12:11:55] "GET /api/projects HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 12:11:55] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 12:11:57] "GET / HTTP/1.1" 200 -
192.168.2.8 - - [20/Apr/2026 12:11:57] "GET /api/projects HTTP/1.1" 200 -
192.168.2.8 - - [20/Apr/2026 12:11:58] "GET /api/projects HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 12:12:01] "GET / HTTP/1.1" 200 -
192.168.2.8 - - [20/Apr/2026 12:12:02] "GET /api/projects HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 12:12:07] "GET / HTTP/1.1" 200 -
192.168.2.8 - - [20/Apr/2026 12:12:08] "GET /api/projects HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 12:12:11] "GET / HTTP/1.1" 200 -
192.168.2.8 - - [20/Apr/2026 12:12:12] "GET /api/projects HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 12:12:16] "GET / HTTP/1.1" 200 -
192.168.2.8 - - [20/Apr/2026 12:12:18] "GET /api/projects HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 12:12:21] "GET / HTTP/1.1" 200 -
192.168.2.8 - - [20/Apr/2026 12:12:22] "GET /api/projects HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 12:12:23] "GET / HTTP/1.1" 200 -
192.168.2.8 - - [20/Apr/2026 12:12:25] "GET /api/projects HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 12:12:26] "GET / HTTP/1.1" 200 -
192.168.2.8 - - [20/Apr/2026 12:12:28] "GET /api/projects HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 12:12:31] "GET / HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 12:12:32] "GET /api/projects HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 12:12:35] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [20/Apr/2026 12:12:36] "GET / HTTP/1.1" 200 -
192.168.2.10 - - [20/Apr/2026 12:12:37] "GET /api/projects HTTP/1.1" 200 -
192.168.2.8 - - [20/Apr/2026 12:12:38] "GET /api/projects HTTP/1.1" 200 -
127.0.0.1 - - [23/Apr/2026 16:56:17] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [23/Apr/2026 16:56:17] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [23/Apr/2026 16:56:18] "GET / HTTP/1.1" 200 -
192.168.2.14 - - [23/Apr/2026 16:56:19] "GET /api/projects HTTP/1.1" 200 -
192.168.2.8 - - [23/Apr/2026 16:56:20] "GET /api/projects HTTP/1.1" 200 -
127.0.0.1 - - [23/Apr/2026 16:56:21] "GET / HTTP/1.1" 200 -
192.168.2.14 - - [23/Apr/2026 16:56:22] "GET /api/projects HTTP/1.1" 200 -
127.0.0.1 - - [23/Apr/2026 16:56:27] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [23/Apr/2026 16:56:28] "GET / HTTP/1.1" 200 -
192.168.2.14 - - [23/Apr/2026 16:56:29] "GET /api/projects HTTP/1.1" 200 -
192.168.2.8 - - [23/Apr/2026 16:56:30] "GET /api/projects HTTP/1.1" 200 -
127.0.0.1 - - [23/Apr/2026 16:56:36] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [23/Apr/2026 16:56:37] "GET / HTTP/1.1" 200 -
192.168.2.8 - - [23/Apr/2026 16:56:38] "GET /api/projects HTTP/1.1" 200 -
127.0.0.1 - - [23/Apr/2026 16:56:38] "GET / HTTP/1.1" 200 -
192.168.2.14 - - [23/Apr/2026 16:56:39] "GET /api/projects HTTP/1.1" 200 -
192.168.2.8 - - [23/Apr/2026 16:56:40] "GET /api/projects HTTP/1.1" 200 -
127.0.0.1 - - [23/Apr/2026 16:56:47] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [23/Apr/2026 16:56:48] "GET / HTTP/1.1" 200 -
192.168.2.14 - - [23/Apr/2026 16:56:49] "GET /api/projects HTTP/1.1" 200 -
192.168.2.8 - - [23/Apr/2026 16:56:50] "GET /api/projects HTTP/1.1" 200 -
127.0.0.1 - - [23/Apr/2026 16:56:50] "GET / HTTP/1.1" 200 -
192.168.2.14 - - [23/Apr/2026 16:56:52] "GET /api/projects HTTP/1.1" 200 -
127.0.0.1 - - [23/Apr/2026 16:56:57] "GET / HTTP/1.1" 200 -

View File

@@ -4268,3 +4268,15 @@ Directory: /home/xian/.openclaw/common/projects/llm-proxy
Command: mkdir -p logs && nohup python3 app.py > logs/app.log 2>&1 & disown
Directory: /home/xian/.openclaw/common/projects/llm-proxy
/bin/sh: 1: disown: not found
==================================================
[2026-04-20T23:12:09.500932] start
Command: mkdir -p logs && nohup python3 app.py > logs/app.log 2>&1 & disown
Directory: /home/xian/.openclaw/common/projects/llm-proxy
/bin/sh: 1: disown: not found
==================================================
[2026-04-21T15:45:11.327216] start
Command: mkdir -p logs && nohup python3 app.py > logs/app.log 2>&1 & disown
Directory: /home/xian/.openclaw/common/projects/llm-proxy
/bin/sh: 1: disown: not found

18
logs/param-hub.log Normal file
View File

@@ -0,0 +1,18 @@
==================================================
[2026-04-20T18:18:18.605305] start
Command: mkdir -p logs && nohup python3 app.py > logs/app.log 2>&1 & disown
Directory: /home/xian/.openclaw/workspace-coder/works/param-hub-python
/bin/sh: 1: disown: not found
==================================================
[2026-04-20T23:12:13.392896] start
Command: mkdir -p logs && nohup python3 app.py > logs/app.log 2>&1 & disown
Directory: /home/xian/.openclaw/workspace-coder/works/param-hub-python
/bin/sh: 1: disown: not found
==================================================
[2026-04-21T15:45:15.735387] start
Command: mkdir -p logs && nohup python3 app.py > logs/app.log 2>&1 & disown
Directory: /home/xian/.openclaw/workspace-coder/works/param-hub-python
/bin/sh: 1: disown: not found

6
logs/voice-chat-web.log Normal file
View File

@@ -0,0 +1,6 @@
==================================================
[2026-04-22T12:29:26.685865] start
Command: mkdir -p logs && MODEL_SERVICE_URL=http://192.168.2.5:12001 nohup python3 main.py > logs/server.log 2>&1 & disown
Directory: /home/xian/.openclaw/workspace-coder/works/voice-chat-web
/bin/sh: 1: disown: not found

View File

@@ -73,3 +73,15 @@ Directory: /home/xian/.openclaw/workspace-coder/works/xian-favor
Command: mkdir -p logs && nohup xian_favor serve --port 19014 > logs/app.log 2>&1 & disown
Directory: /home/xian/.openclaw/workspace-coder/works/xian-favor
/bin/sh: 1: disown: not found
==================================================
[2026-04-20T23:12:18.766216] start
Command: mkdir -p logs && nohup python3 -c "from xian_favor.api import start_server; start_server(port=19014)" > logs/app.log 2>&1 & disown
Directory: /home/xian/.openclaw/workspace-coder/works/xian-favor
/bin/sh: 1: disown: not found
==================================================
[2026-04-23T00:57:27.686904] start
Command: mkdir -p logs && nohup python3 -c "from xian_favor.api import start_server; start_server(port=19014)" > logs/app.log 2>&1 & disown
Directory: /home/xian/.openclaw/workspace-coder/works/xian-favor
/bin/sh: 1: disown: not found

View File

@@ -192,6 +192,30 @@
"admin_url": "http://localhost:19004/admin",
"git_repo": "http://192.168.2.8:12007/coder/tech-forum",
"version": "v1.2.0"
},
{
"id": "image-editor",
"name": "图片编辑器",
"type": "web",
"ports": [19018],
"directory": "works/image-editor",
"start_cmd": "mkdir -p logs && nohup python3 app.py > logs/app.log 2>&1 & disown",
"health_url": "http://localhost:19018/api/health",
"description": "前端图片处理:合并、分割、挖孔、圆形切图、文字图片",
"git_repo": "http://192.168.2.8:12007/coder/image-editor",
"version": "v1.2.1"
},
{
"id": "voice-chat-web",
"name": "语音对话网页",
"type": "web",
"ports": [19019],
"directory": "works/voice-chat-web",
"start_cmd": "mkdir -p logs && MODEL_SERVICE_URL=http://192.168.2.5:12001 nohup python3 main.py > logs/server.log 2>&1 & disown",
"health_url": "http://localhost:19019/api/status",
"description": "基于Qwen2-Audio的语音交互网页支持录音和文字对话",
"git_repo": "http://192.168.2.8:12007/coder/voice-chat-web",
"version": "v1.2.0"
}
]
}