feat(automation): add daily briefing preset and cron backup scheduling
This commit is contained in:
@@ -3,3 +3,4 @@ export { WebhookHandler } from './webhooks.js';
|
||||
export { GmailWatcher } from './gmail.js';
|
||||
export { HeartbeatMonitor, parseInterval } from './heartbeat.js';
|
||||
export type { HeartbeatResult, HeartbeatDeps, CheckResult } from './heartbeat.js';
|
||||
export { buildPresetCronJobs } from './presets.js';
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import { configSchema } from '../config/schema.js';
|
||||
import { buildPresetCronJobs } from './presets.js';
|
||||
|
||||
describe('buildPresetCronJobs', () => {
|
||||
it('creates a daily briefing preset cron job when enabled with output', () => {
|
||||
const config = configSchema.parse({
|
||||
telegram: { bot_token: 'token', allowed_chat_ids: [1] },
|
||||
models: { default: { provider: 'anthropic', model: 'claude-sonnet' } },
|
||||
automation: {
|
||||
daily_briefing: {
|
||||
enabled: true,
|
||||
schedule: '0 7 * * *',
|
||||
timezone: 'America/New_York',
|
||||
output: { channel: 'telegram', peer: '1' },
|
||||
model_tier: 'fast',
|
||||
prompt: 'Daily briefing prompt',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const jobs = buildPresetCronJobs(config);
|
||||
expect(jobs).toHaveLength(1);
|
||||
expect(jobs[0]).toMatchObject({
|
||||
name: 'daily-briefing',
|
||||
schedule: '0 7 * * *',
|
||||
timezone: 'America/New_York',
|
||||
output: { channel: 'telegram', peer: '1' },
|
||||
model_tier: 'fast',
|
||||
message: 'Daily briefing prompt',
|
||||
});
|
||||
});
|
||||
|
||||
it('skips daily briefing job when output is missing', () => {
|
||||
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
||||
const config = configSchema.parse({
|
||||
telegram: { bot_token: 'token', allowed_chat_ids: [1] },
|
||||
models: { default: { provider: 'anthropic', model: 'claude-sonnet' } },
|
||||
automation: {
|
||||
daily_briefing: {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const jobs = buildPresetCronJobs(config);
|
||||
expect(jobs).toHaveLength(0);
|
||||
expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('output is missing'));
|
||||
warnSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('skips preset when daily briefing name conflicts with user cron job', () => {
|
||||
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
||||
const config = configSchema.parse({
|
||||
telegram: { bot_token: 'token', allowed_chat_ids: [1] },
|
||||
models: { default: { provider: 'anthropic', model: 'claude-sonnet' } },
|
||||
automation: {
|
||||
cron: [{
|
||||
name: 'daily-briefing',
|
||||
schedule: '0 9 * * *',
|
||||
message: 'manual job',
|
||||
output: { channel: 'telegram', peer: '1' },
|
||||
}],
|
||||
daily_briefing: {
|
||||
enabled: true,
|
||||
output: { channel: 'telegram', peer: '1' },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const jobs = buildPresetCronJobs(config);
|
||||
expect(jobs).toHaveLength(0);
|
||||
expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('conflicts with automation.cron'));
|
||||
warnSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,31 @@
|
||||
import type { Config, CronJobConfig } from '../config/schema.js';
|
||||
|
||||
/**
|
||||
* Builds config-derived cron jobs that are not manually listed under automation.cron.
|
||||
* This keeps opinionated automation features opt-in while reusing the existing CronScheduler.
|
||||
*/
|
||||
export function buildPresetCronJobs(config: Config): CronJobConfig[] {
|
||||
const jobs: CronJobConfig[] = [];
|
||||
const existingNames = new Set(config.automation.cron.map((job) => job.name));
|
||||
|
||||
const briefing = config.automation.daily_briefing;
|
||||
if (briefing.enabled) {
|
||||
if (!briefing.output) {
|
||||
console.warn('automation.daily_briefing.enabled=true but output is missing; skipping daily briefing job');
|
||||
} else if (existingNames.has(briefing.name)) {
|
||||
console.warn(`automation.daily_briefing name '${briefing.name}' conflicts with automation.cron; skipping preset job`);
|
||||
} else {
|
||||
jobs.push({
|
||||
name: briefing.name,
|
||||
schedule: briefing.schedule,
|
||||
message: briefing.prompt,
|
||||
output: briefing.output,
|
||||
enabled: true,
|
||||
timezone: briefing.timezone,
|
||||
model_tier: briefing.model_tier,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return jobs;
|
||||
}
|
||||
Reference in New Issue
Block a user