feat(setup): skip redundant operator-pack prompts and add strict-mode docs/tests
This commit is contained in:
@@ -6,6 +6,16 @@ All notable changes to Flynn are documented in this file.
|
||||
|
||||
### Added
|
||||
|
||||
- **Operator Setup Pack + Strict Doctor Mode** -- `flynn setup` Automation now
|
||||
supports an Operator Pack that preconfigures backup scheduling/notifications,
|
||||
heartbeat alerts, daily briefing, and MinIO sync with configurable output
|
||||
channel/peer routing. `flynn doctor --strict` now treats warnings as failures
|
||||
for CI and preflight gating workflows.
|
||||
|
||||
- **Heartbeat Notification Cooldown Config** -- `automation.heartbeat.notify_cooldown`
|
||||
adds throttling for repeated failure/recovery alerts (default `30m`) to reduce
|
||||
noisy notifications during unstable periods.
|
||||
|
||||
- **Native Audio Support** -- Smart routing for voice messages: audio-capable models
|
||||
(Gemini, OpenAI, GitHub) receive raw audio directly via `AudioSource` content parts;
|
||||
non-audio models (Anthropic, Bedrock, Ollama, llama.cpp) get Whisper transcription
|
||||
@@ -130,6 +140,9 @@ All notable changes to Flynn are documented in this file.
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Heartbeat Cooldown Parse Resilience** -- Invalid `automation.heartbeat.notify_cooldown`
|
||||
values no longer throw during alerting; monitor falls back to `30m` and logs a warning.
|
||||
|
||||
- **Voice Message Failure Handling** -- Telegram voice/audio messages now send user feedback on
|
||||
download failures instead of silently dropping. When audio transcription is not configured for
|
||||
non-audio-capable models, a graceful error message is sent to the user instead of
|
||||
|
||||
@@ -771,6 +771,35 @@ Repeated failure/recovery notifications are throttled by `notify_cooldown`.
|
||||
|
||||
`flynn setup` now includes an Operator Pack option in Automation that preconfigures scheduled backups, heartbeat alerts, a daily briefing, and a default MinIO sync task, with prompts for output channel/peer routing.
|
||||
|
||||
Example Operator Pack output routing:
|
||||
|
||||
```yaml
|
||||
backup:
|
||||
enabled: true
|
||||
schedule: "0 2 * * *"
|
||||
notify:
|
||||
channel: telegram
|
||||
peer: "123456789"
|
||||
|
||||
automation:
|
||||
heartbeat:
|
||||
enabled: true
|
||||
notify:
|
||||
channel: telegram
|
||||
peer: "123456789"
|
||||
notify_cooldown: "30m"
|
||||
daily_briefing:
|
||||
enabled: true
|
||||
output:
|
||||
channel: telegram
|
||||
peer: "123456789"
|
||||
minio_sync:
|
||||
enabled: true
|
||||
notify:
|
||||
channel: telegram
|
||||
peer: "123456789"
|
||||
```
|
||||
|
||||
## Gmail Pub/Sub Watcher
|
||||
|
||||
Monitor a Gmail inbox and forward new messages into the agent pipeline.
|
||||
|
||||
@@ -193,8 +193,9 @@
|
||||
"status": "completed",
|
||||
"date": "2026-02-16",
|
||||
"updated": "2026-02-16",
|
||||
"summary": "Implemented operator-focused hardening and onboarding polish: added setup Operator Pack flows in both Automation menu and first-run wizard to preconfigure scheduled backups, heartbeat alerts, daily briefing, and default MinIO sync; added operator-pack prompts for output channel/peer routing; added heartbeat notification throttling via `automation.heartbeat.notify_cooldown` (with invalid-value fallback handling); and added `flynn doctor --strict` to treat warnings as failures. Updated docs/default config examples and onboarding tests accordingly.",
|
||||
"summary": "Implemented operator-focused hardening and onboarding polish: added setup Operator Pack flows in both Automation menu and first-run wizard to preconfigure scheduled backups, heartbeat alerts, daily briefing, and default MinIO sync; added operator-pack prompts for output channel/peer routing with redundant prompt skip when already configured; added heartbeat notification throttling via `automation.heartbeat.notify_cooldown` (with invalid-value fallback handling); and added `flynn doctor --strict` to treat warnings as failures. Added docs/changelog updates and a consolidated regression run record.",
|
||||
"files_modified": [
|
||||
"CHANGELOG.md",
|
||||
"src/cli/setup/config.ts",
|
||||
"src/cli/setup/config.test.ts",
|
||||
"src/cli/setup/automation.test.ts",
|
||||
@@ -203,6 +204,7 @@
|
||||
"src/cli/setup/integration.test.ts",
|
||||
"src/cli/setup/summary.ts",
|
||||
"src/cli/setup/summary.test.ts",
|
||||
"src/cli/index.test.ts",
|
||||
"src/automation/heartbeat.ts",
|
||||
"src/automation/heartbeat.test.ts",
|
||||
"src/config/schema.ts",
|
||||
@@ -213,7 +215,7 @@
|
||||
"README.md",
|
||||
"docs/plans/state.json"
|
||||
],
|
||||
"test_status": "pnpm test:run src/cli/setup/summary.test.ts src/cli/setup/integration.test.ts src/cli/setup/automation.test.ts src/cli/setup/config.test.ts src/automation/heartbeat.test.ts src/config/schema.test.ts src/cli/doctor.test.ts + pnpm typecheck passing"
|
||||
"test_status": "pnpm test:run src/cli/index.test.ts src/cli/setup/automation.test.ts src/cli/setup/integration.test.ts src/cli/setup/summary.test.ts src/automation/heartbeat.test.ts src/config/schema.test.ts src/cli/doctor.test.ts + pnpm typecheck passing"
|
||||
},
|
||||
"backup-session-summary-audit-trail": {
|
||||
"status": "completed",
|
||||
@@ -3499,7 +3501,7 @@
|
||||
}
|
||||
},
|
||||
"overall_progress": {
|
||||
"total_test_count": 1870,
|
||||
"total_test_count": 1872,
|
||||
"all_tests_passing": true,
|
||||
"p0_completion": "3/3 (100%)",
|
||||
"p1_completion": "4/4 (100%)",
|
||||
|
||||
@@ -21,6 +21,13 @@ describe('CLI program', () => {
|
||||
expect(commandNames).toContain('zai-auth');
|
||||
});
|
||||
|
||||
it('registers doctor strict flag on doctor command', () => {
|
||||
const program = createProgram();
|
||||
const doctor = program.commands.find((c) => c.name() === 'doctor');
|
||||
const strictOption = doctor?.options.find((o) => o.long === '--strict');
|
||||
expect(strictOption).toBeDefined();
|
||||
});
|
||||
|
||||
it('has version info', () => {
|
||||
const program = createProgram();
|
||||
expect(program.version()).toBeDefined();
|
||||
|
||||
@@ -127,4 +127,34 @@ describe('setupAutomation', () => {
|
||||
expect(automation?.heartbeat).toBeUndefined();
|
||||
expect(automation?.minio_sync).toBeUndefined();
|
||||
});
|
||||
|
||||
it('skips operator-pack reconfiguration prompts when already configured and not reconfigured', async () => {
|
||||
const builder = new ConfigBuilder();
|
||||
builder.applyOperatorPack({
|
||||
outputChannel: 'telegram',
|
||||
outputPeer: '123',
|
||||
backupSchedule: '0 2 * * *',
|
||||
dailyBriefingSchedule: '0 8 * * *',
|
||||
enableMinioSync: true,
|
||||
});
|
||||
|
||||
const p = makePrompter({
|
||||
confirms: [
|
||||
false, // reconfigure operator pack?
|
||||
false, // enable cron scheduler
|
||||
false, // enable webhook receiver
|
||||
false, // configure google services
|
||||
],
|
||||
});
|
||||
|
||||
await setupAutomation(p, builder);
|
||||
|
||||
const config = builder.build() as Record<string, unknown>;
|
||||
const backup = config.backup as Record<string, unknown>;
|
||||
const automation = config.automation as Record<string, unknown>;
|
||||
const briefing = automation.daily_briefing as Record<string, unknown>;
|
||||
|
||||
expect(backup.schedule).toBe('0 2 * * *');
|
||||
expect(briefing.schedule).toBe('0 8 * * *');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -57,12 +57,26 @@ const GOOGLE_SERVICES: GoogleService[] = [
|
||||
];
|
||||
|
||||
export async function setupAutomation(p: Prompter, builder: ConfigBuilder): Promise<void> {
|
||||
const enableOperatorPack = await p.confirm(
|
||||
'Enable operator automation pack (scheduled backups + heartbeat alerts + daily briefing + MinIO sync)?',
|
||||
false,
|
||||
const config = builder.build();
|
||||
const automation = (config.automation ?? {}) as Record<string, unknown>;
|
||||
const backup = (config.backup ?? {}) as Record<string, unknown>;
|
||||
const operatorPackConfigured = Boolean(
|
||||
backup.enabled === true
|
||||
&& (automation.heartbeat as { enabled?: boolean } | undefined)?.enabled
|
||||
&& (automation.daily_briefing as { enabled?: boolean } | undefined)?.enabled,
|
||||
);
|
||||
if (enableOperatorPack) {
|
||||
const config = builder.build();
|
||||
|
||||
const configureOperatorPack = operatorPackConfigured
|
||||
? await p.confirm(
|
||||
'Operator automation pack appears enabled. Reconfigure operator pack settings now?',
|
||||
false,
|
||||
)
|
||||
: await p.confirm(
|
||||
'Enable operator automation pack (scheduled backups + heartbeat alerts + daily briefing + MinIO sync)?',
|
||||
false,
|
||||
);
|
||||
|
||||
if (configureOperatorPack) {
|
||||
const telegramPeer = config.telegram?.allowed_chat_ids?.[0];
|
||||
const defaultOutputChannel = telegramPeer ? 'telegram' : 'webchat';
|
||||
const defaultOutputPeer = telegramPeer ? String(telegramPeer) : 'operator';
|
||||
|
||||
Reference in New Issue
Block a user