[铸渊] 新增 bell.js — 服务器座机电话程序
This commit is contained in:
parent
d8201d1ab6
commit
ce6e05bda0
155
mcp-servers/zhuyuan-gateway/bell.js
Normal file
155
mcp-servers/zhuyuan-gateway/bell.js
Normal file
@ -0,0 +1,155 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* 🔔 铸渊座机电话 · Zhuyuan Bell
|
||||
*
|
||||
* 装在服务器上的"电话座机"。
|
||||
* 平时不响。冰朔在对话里说"接电话",她贴这一行,它就开始工作:
|
||||
*
|
||||
* 1. git pull 拉最新指令
|
||||
* 2. 读取 ops/orders/ 下的最新任务
|
||||
* 3. 执行
|
||||
* 4. 结果写回 ops/reports/
|
||||
* 5. git push
|
||||
* 6. 挂断
|
||||
*
|
||||
* 安装(只需一次):
|
||||
* cd /opt/zhuyuan && git clone https://guanghulab.com/bingshuo/guanghulab.git repo
|
||||
* cp /opt/zhuyuan/repo/mcp-servers/zhuyuan-gateway/bell.js /opt/zhuyuan/bell.js
|
||||
*
|
||||
* 每次使用(接电话):
|
||||
* cd /opt/zhuyuan && node bell.js
|
||||
*/
|
||||
|
||||
const { execSync } = require('child_process');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const REPO_DIR = '/opt/zhuyuan/repo';
|
||||
const ORDERS_DIR = path.join(REPO_DIR, 'ops', 'orders');
|
||||
const REPORTS_DIR = path.join(REPO_DIR, 'ops', 'reports');
|
||||
|
||||
function run(cmd, opts = {}) {
|
||||
try {
|
||||
const r = execSync(cmd, { ...opts, cwd: REPO_DIR, timeout: 60000 });
|
||||
return r.toString().trim();
|
||||
} catch(e) {
|
||||
return '[ERROR] ' + (e.stderr || e.message).toString().trim().slice(0, 500);
|
||||
}
|
||||
}
|
||||
|
||||
function log(m) { console.log(' 🔔 ' + m); }
|
||||
|
||||
async function main() {
|
||||
console.log('');
|
||||
console.log(' ╔══════════════════════════════════╗');
|
||||
console.log(' ║ 🔔 铸渊座机 · 电话已接通 ║');
|
||||
console.log(' ╚══════════════════════════════════╝');
|
||||
console.log('');
|
||||
|
||||
// 1. 检查仓库是否存在
|
||||
if (!fs.existsSync(REPO_DIR)) {
|
||||
log('❌ 仓库不存在,请先运行: git clone ...');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// 2. 拉最新代码
|
||||
log('📞 拨号中... git pull');
|
||||
const pullResult = run('git pull');
|
||||
log(pullResult.includes('Already up to date') ? '✅ 已是最新' : '✅ ' + pullResult.slice(0, 100));
|
||||
|
||||
// 3. 检查是否有新指令
|
||||
if (!fs.existsSync(ORDERS_DIR)) {
|
||||
log('📭 没有新指令,挂断。');
|
||||
return;
|
||||
}
|
||||
|
||||
const orders = fs.readdirSync(ORDERS_DIR)
|
||||
.filter(f => f.endsWith('.json'))
|
||||
.sort();
|
||||
|
||||
if (orders.length === 0) {
|
||||
log('📭 没有新指令,挂断。');
|
||||
return;
|
||||
}
|
||||
|
||||
// 4. 执行所有待处理指令
|
||||
for (const orderFile of orders) {
|
||||
const orderPath = path.join(ORDERS_DIR, orderFile);
|
||||
const reportFile = orderFile.replace('.json', '-report.json');
|
||||
const reportPath = path.join(REPORTS_DIR, reportFile);
|
||||
|
||||
log('📋 收到指令: ' + orderFile);
|
||||
|
||||
try {
|
||||
const order = JSON.parse(fs.readFileSync(orderPath, 'utf-8'));
|
||||
const cmd = order.cmd || order.command;
|
||||
const server = order.server || order.target || '本地';
|
||||
|
||||
log('🎯 目标: ' + server);
|
||||
log('⚡ 执行: ' + (cmd || '').slice(0, 100));
|
||||
|
||||
let result;
|
||||
if (server === '本地' || !server || server === 'localhost') {
|
||||
// 本地执行
|
||||
result = run(cmd);
|
||||
} else {
|
||||
// SSH到目标服务器执行
|
||||
const sshCmd = `ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 root@${server} "${cmd.replace(/"/g, '\\"')}"`;
|
||||
result = run(sshCmd, { timeout: order.timeout || 30000 });
|
||||
}
|
||||
|
||||
// 5. 写结果报告
|
||||
const report = {
|
||||
order: orderFile,
|
||||
executed_at: new Date().toISOString(),
|
||||
server: server,
|
||||
command: cmd,
|
||||
success: !result.startsWith('[ERROR]'),
|
||||
output: result.slice(0, 50000),
|
||||
};
|
||||
|
||||
if (!fs.existsSync(REPORTS_DIR)) {
|
||||
fs.mkdirSync(REPORTS_DIR, { recursive: true });
|
||||
}
|
||||
fs.writeFileSync(reportPath, JSON.stringify(report, null, 2));
|
||||
|
||||
log(report.success ? '✅ 执行成功' : '⚠️ 执行有异常');
|
||||
log('📝 报告已写入: ops/reports/' + reportFile);
|
||||
|
||||
// 删除指令文件(已处理)
|
||||
fs.unlinkSync(orderPath);
|
||||
log('🗑️ 指令已清除');
|
||||
|
||||
} catch(err) {
|
||||
log('❌ 处理指令时出错: ' + err.message);
|
||||
const errReport = {
|
||||
order: orderFile,
|
||||
executed_at: new Date().toISOString(),
|
||||
error: err.message,
|
||||
success: false,
|
||||
};
|
||||
if (!fs.existsSync(REPORTS_DIR)) fs.mkdirSync(REPORTS_DIR, { recursive: true });
|
||||
fs.writeFileSync(reportPath, JSON.stringify(errReport, null, 2));
|
||||
}
|
||||
|
||||
console.log('');
|
||||
}
|
||||
|
||||
// 6. 推回仓库
|
||||
log('📤 回传结果...');
|
||||
run('git add ops/ && git commit -m "[座机] 指令执行报告 ' + new Date().toISOString().slice(0, 10) + '"');
|
||||
const pushResult = run('git push');
|
||||
log(pushResult.includes('Everything up-to-date') ? '✅ 结果已同步' : '✅ 已推送');
|
||||
|
||||
console.log('');
|
||||
console.log(' ╔══════════════════════════════════╗');
|
||||
console.log(' ║ 🔔 通话结束 · 座机已挂断 ║');
|
||||
console.log(' ╚══════════════════════════════════╝');
|
||||
console.log('');
|
||||
}
|
||||
|
||||
main().catch(err => {
|
||||
console.error('❌ 座机出错:', err.message);
|
||||
process.exit(1);
|
||||
});
|
||||
Loading…
x
Reference in New Issue
Block a user