Add ops-chat planned reboot command

---
Build: pass | Tests: FAIL — 3 unrelated stale telegram TTS expectations in src/telegram-commands.test.ts

---
Build: pass | Tests: FAIL — Tests  3 failed | 2083 passed (2086)
This commit is contained in:
Operator & Codex 2026-05-01 12:38:13 +02:00
parent bc2fecab53
commit 53509d51bb
2 changed files with 40 additions and 0 deletions

View file

@ -58,6 +58,7 @@ import {
handleTokensCommand,
handleUsageCommand,
handleUpdatesCommand,
handleRebootCommand,
handleBudgetResetCommand,
handleClearCooldownCommand,
handleWhoamiCommand,
@ -104,6 +105,7 @@ const OPS_CHAT_COMMANDS: TelegramBotCommand[] = [
{ command: 'publishreport', description: 'Show tenant publish report' },
{ command: 'testreport', description: 'Show test and build report' },
{ command: 'updates', description: 'Show FreeBSD update status' },
{ command: 'reboot', description: 'Schedule safe planned reboot' },
{ command: 'budgetreset', description: 'Reset an agent token budget' },
{ command: 'clearcooldown', description: 'Clear provider cooldown' },
{ command: 'audit', description: 'Show platform ownership audit' },
@ -217,6 +219,7 @@ export class TelegramChannel implements Channel {
'/usage — Show token budget per agent\n' +
'/tokens — Show runtime token burn per agent\n' +
'/updates — Show FreeBSD base/ports updates\n' +
'/reboot — Schedule safe planned reboot\n' +
'/budgetreset — Reset agent token budget (admin)\n' +
'/clearcooldown — Clear provider fallback cooldown (admin)\n' +
'/tts — Control voice replies (on/off/status)\n' +
@ -401,6 +404,11 @@ export class TelegramChannel implements Channel {
await handleUpdatesCommand(ctx, chatJid);
});
this.bot.command('reboot', async (ctx) => {
const chatJid = `tg:${ctx.chat.id}`;
await handleRebootCommand(ctx, chatJid);
});
this.bot.command('budgetreset', async (ctx) => {
const chatJid = `tg:${ctx.chat.id}`;
await handleBudgetResetCommand(ctx, chatJid);

View file

@ -1353,6 +1353,38 @@ export async function handleUpdatesCommand(
}
}
export async function handleRebootCommand(
ctxArg: any,
chatJid: string,
): Promise<void> {
if (!(await requireAdmin(ctxArg))) return;
if (!(await requireOpsChat(ctxArg, chatJid))) return;
const preflight = getRebootPreflight();
const kb = new InlineKeyboard()
.text('Confirm reboot (+1m)', 'cmd:updates:run:reboot')
.row()
.text('Force reboot (+1m)', 'cmd:updates:run:reboot:force')
.row()
.text('Cancel', 'cmd:updates:open');
const lines = [
'Planned maintenance reboot',
...preflight.details,
`Status: ${preflight.status}`,
'',
'This uses the safe maintenance path:',
'- stop Mevy',
'- checkpoint and stop PostgreSQL',
'- snapshot pgdata + pgwal',
'- reboot the host in 1 minute',
'',
'Confirm planned reboot?',
];
await ctxArg.reply(lines.join('\n'), { reply_markup: kb });
}
export async function handleUpdatesCallback(
ctxArg: any,
chatJid: string,