feat(KB-026): add ntfy.sh push notification support
- Add ntfy settings (ntfyEnabled, ntfyTopic) to core types and config storage - Create NtfyNotifier service with event-based notifications for task completion/failure - Add Notifications section to SettingsModal for configuring ntfy topic - Wire up NtfyNotifier in engine to send notifications on task state changes - Include ntfy topic in task templates for per-task notification override - Add comprehensive tests for NtfyNotifier service
This commit is contained in:
@@ -2,7 +2,7 @@ import { EventEmitter } from "node:events";
|
||||
import { execSync } from "node:child_process";
|
||||
import { appendFile, mkdir, readFile, writeFile, readdir, rename, unlink } from "node:fs/promises";
|
||||
import { join, sep } from "node:path";
|
||||
import { existsSync, watch, type FSWatcher } from "node:fs";
|
||||
import { existsSync, watch, type FSWatcher, readFileSync } from "node:fs";
|
||||
import type { Task, TaskDetail, TaskCreateInput, TaskAttachment, AgentLogEntry, BoardConfig, Column, MergeResult, Settings } from "./types.js";
|
||||
import { VALID_TRANSITIONS, DEFAULT_SETTINGS } from "./types.js";
|
||||
|
||||
@@ -1174,6 +1174,13 @@ export class TaskStore extends EventEmitter<TaskStoreEvents> {
|
||||
? task.dependencies.map((d) => `- **Task:** ${d}`).join("\n")
|
||||
: "- **None**";
|
||||
|
||||
// Get current settings to check for ntfy configuration
|
||||
const settings = this.getSettingsSync();
|
||||
const notificationsSection =
|
||||
settings.ntfyEnabled && settings.ntfyTopic
|
||||
? `\n## Notifications\n\nntfy topic: \`${settings.ntfyTopic}\`\n`
|
||||
: "";
|
||||
|
||||
const heading = task.title ? `${task.id}: ${task.title}` : task.id;
|
||||
return `# ${heading}
|
||||
|
||||
@@ -1209,6 +1216,23 @@ ${deps}
|
||||
|
||||
- [ ] All steps complete
|
||||
- [ ] All tests passing
|
||||
`;
|
||||
${notificationsSection}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronous version of getSettings for internal use.
|
||||
* Returns cached settings or default settings if not loaded.
|
||||
*/
|
||||
private getSettingsSync(): Settings {
|
||||
// Since we can't easily make generateSpecifiedPrompt async,
|
||||
// we read settings synchronously from the file.
|
||||
// The settings file is read during init and on each update,
|
||||
// so this should be reasonably up-to-date for prompt generation.
|
||||
try {
|
||||
const config = JSON.parse(readFileSync(this.configPath, "utf-8"));
|
||||
return { ...DEFAULT_SETTINGS, ...config.settings };
|
||||
} catch {
|
||||
return DEFAULT_SETTINGS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,6 +209,12 @@ export interface Settings {
|
||||
* remain in triage with status "awaiting-approval" until a user approves
|
||||
* or rejects the plan. Default: false. */
|
||||
requirePlanApproval?: boolean;
|
||||
/** ntfy.sh topic name for push notifications. When set along with ntfyEnabled,
|
||||
* notifications are sent to https://ntfy.sh/{topic} when tasks complete or fail. */
|
||||
ntfyTopic?: string;
|
||||
/** When true, enables ntfy.sh push notifications for task completion and failures.
|
||||
* Requires ntfyTopic to be set. Default: false. */
|
||||
ntfyEnabled?: boolean;
|
||||
}
|
||||
|
||||
export const DEFAULT_SETTINGS: Settings = {
|
||||
@@ -229,6 +235,8 @@ export const DEFAULT_SETTINGS: Settings = {
|
||||
autoResolveConflicts: true,
|
||||
smartConflictResolution: true,
|
||||
requirePlanApproval: false,
|
||||
ntfyEnabled: false,
|
||||
ntfyTopic: undefined,
|
||||
};
|
||||
|
||||
export interface BoardConfig {
|
||||
|
||||
Reference in New Issue
Block a user