const { Client } = require('ssh2'); const fs = require('fs'); const path = require('path'); const SSH_CONFIG = { host: '119.45.10.34', port: 22, username: 'root', password: '#xyzh%CS#2512@28' }; const REMOTE_DIR = '/www/wwwroot/demo.tensorgrove.com.cn/server/services'; const LOCAL_DIR = path.join(__dirname, '..', 'test2', 'server', 'services'); const FILES = [ 'assistantProfileConfig.js', 'nativeVoiceGateway.js', 'contextKeywordTracker.js', 'toolExecutor.js', 'realtimeDialogRouting.js', ]; function sshExec(conn, cmd) { return new Promise((resolve, reject) => { conn.exec(cmd, (err, s) => { if (err) return reject(err); let o = ''; s.on('data', d => o += d); s.stderr.on('data', d => o += d); s.on('close', () => resolve(o)); }); }); } function sftpUpload(conn, localPath, remotePath) { return new Promise((resolve, reject) => { conn.sftp((err, sftp) => { if (err) return reject(err); const rs = fs.createReadStream(localPath); const ws = sftp.createWriteStream(remotePath); ws.on('close', () => resolve()); ws.on('error', reject); rs.pipe(ws); }); }); } async function main() { const conn = new Client(); await new Promise((resolve, reject) => { conn.on('ready', resolve).on('error', reject).connect(SSH_CONFIG); }); console.log('SSH connected\n'); // 1. Backup all files console.log('=== Step 1: Backup ==='); for (const f of FILES) { const r = await sshExec(conn, `cp ${REMOTE_DIR}/${f} ${REMOTE_DIR}/${f}.bak.$(date +%Y%m%d_%H%M%S) && echo " Backed up ${f}"`); console.log(r); } // 2. Upload all files console.log('\n=== Step 2: Upload ==='); for (const f of FILES) { await sftpUpload(conn, path.join(LOCAL_DIR, f), `${REMOTE_DIR}/${f}`); console.log(` Uploaded ${f}`); } // 3. Syntax check each file console.log('\n=== Step 3: Syntax check ==='); for (const f of FILES) { const result = await sshExec(conn, `node -c ${REMOTE_DIR}/${f} 2>&1 && echo "SYNTAX_OK ${f}" || echo "SYNTAX_FAIL ${f}"`); console.log(result); if (result.includes('SYNTAX_FAIL')) { console.error(`SYNTAX CHECK FAILED for ${f}! Rolling back ALL...`); for (const rf of FILES) { await sshExec(conn, `ls -t ${REMOTE_DIR}/${rf}.bak.* 2>/dev/null | head -1 | xargs -I{} cp {} ${REMOTE_DIR}/${rf}`); } console.log('Rolled back.'); conn.end(); process.exit(1); } } // 4. Restart PM2 console.log('\n=== Step 4: Restart PM2 ==='); console.log(await sshExec(conn, 'cd /www/wwwroot/demo.tensorgrove.com.cn/server && pm2 restart bigwo-server --update-env')); await new Promise(r => setTimeout(r, 3000)); // 5. Check PM2 status console.log('\n=== Step 5: PM2 status ==='); console.log(await sshExec(conn, 'pm2 status bigwo-server')); // 6. Check recent logs for errors console.log('\n=== Step 6: Recent logs ==='); console.log(await sshExec(conn, 'tail -15 /var/log/bigwo/server-out.log')); // 7. Check error log console.log('\n=== Step 7: Error log ==='); console.log(await sshExec(conn, 'tail -5 /var/log/bigwo/server-error.log')); conn.end(); console.log('\n=== Deploy KB Protection Window Complete! ==='); console.log('Changes: KB topic memory + 60s protection window + honest fallback + session passthrough'); } main().catch(e => { console.error('DEPLOY FAILED:', e.message); process.exit(1); });