PLG-CAL-004: Implement ScheduledGatewayRestart handling in plugin

- Add state persistence (persistState/restoreState) for recovery after restart
- Add handleScheduledGatewayRestart method that:
  - Persists current scheduler state to disk
  - Sends final heartbeat to backend before shutdown
  - Stops the calendar scheduler (pauses scheduled tasks)
- Add isRestartPending flag to prevent new slot processing during restart
- Add isScheduledGatewayRestart helper to detect restart events
- Update scheduler to detect and handle ScheduledGatewayRestart events
- Add new tools: harborforge_restart_status, harborforge_calendar_pause/resume
- Export isRestartPending and getStateFilePath methods
- Bump plugin version to 0.3.1
This commit is contained in:
zhi
2026-04-01 09:41:02 +00:00
parent 24c4a7ad14
commit 3b0ea0ad12
6 changed files with 578 additions and 19 deletions

View File

@@ -5,7 +5,7 @@
* for the HarborForge Monitor bridge (via monitor_port).
*
* Also integrates with HarborForge Calendar system to wake agents
* for scheduled tasks (PLG-CAL-002).
* for scheduled tasks (PLG-CAL-002, PLG-CAL-004).
*
* Sidecar architecture has been removed. Telemetry data is now
* served directly by the plugin when Monitor queries via the
@@ -105,7 +105,7 @@ export default {
},
openclaw: {
version: api.version || 'unknown',
pluginVersion: '0.3.0',
pluginVersion: '0.3.1', // Bumped for PLG-CAL-004
},
timestamp: new Date().toISOString(),
};
@@ -128,7 +128,7 @@ export default {
const meta: OpenClawMeta = {
version: api.version || 'unknown',
plugin_version: '0.3.0',
plugin_version: '0.3.1',
agents: [], // TODO: populate from api agent list when available
};
@@ -363,6 +363,7 @@ export default {
running: calendarScheduler.isRunning(),
processing: calendarScheduler.isProcessing(),
currentSlot: calendarScheduler.getCurrentSlot(),
isRestartPending: calendarScheduler.isRestartPending(),
} : null;
return {
@@ -439,6 +440,8 @@ export default {
processing: calendarScheduler.isProcessing(),
currentSlot: calendarScheduler.getCurrentSlot(),
state: calendarScheduler.getState(),
isRestartPending: calendarScheduler.isRestartPending(),
stateFilePath: calendarScheduler.getStateFilePath(),
};
},
}));
@@ -490,6 +493,68 @@ export default {
},
}));
// Tool: pause current slot
api.registerTool(() => ({
name: 'harborforge_calendar_pause',
description: 'Pause the current calendar slot',
parameters: {
type: 'object',
properties: {},
},
async execute() {
if (!calendarScheduler) {
return { error: 'Calendar scheduler not running' };
}
await calendarScheduler.pauseCurrentSlot();
return { success: true, message: 'Slot paused' };
},
}));
// Tool: resume current slot
api.registerTool(() => ({
name: 'harborforge_calendar_resume',
description: 'Resume the paused calendar slot',
parameters: {
type: 'object',
properties: {},
},
async execute() {
if (!calendarScheduler) {
return { error: 'Calendar scheduler not running' };
}
await calendarScheduler.resumeCurrentSlot();
return { success: true, message: 'Slot resumed' };
},
}));
// Tool: check ScheduledGatewayRestart status
api.registerTool(() => ({
name: 'harborforge_restart_status',
description: 'Check if a gateway restart is pending (PLG-CAL-004)',
parameters: {
type: 'object',
properties: {},
},
async execute() {
if (!calendarScheduler) {
return { error: 'Calendar scheduler not running' };
}
const isPending = calendarScheduler.isRestartPending();
const stateFilePath = calendarScheduler.getStateFilePath();
return {
isRestartPending: isPending,
stateFilePath: stateFilePath,
message: isPending
? 'A gateway restart has been scheduled. The scheduler has been paused.'
: 'No gateway restart is pending.',
};
},
}));
logger.info('HarborForge plugin registered (id: harbor-forge)');
},
};