@pi-unipi/notify 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +103 -0
- package/commands.ts +210 -0
- package/events.ts +192 -0
- package/index.ts +57 -0
- package/package.json +53 -0
- package/platforms/gotify.ts +36 -0
- package/platforms/native.ts +47 -0
- package/platforms/node-notifier.d.ts +20 -0
- package/platforms/telegram.ts +77 -0
- package/settings.ts +115 -0
- package/skills/configure-notify/SKILL.md +134 -0
- package/skills/notify/SKILL.md +56 -0
- package/tools.ts +96 -0
- package/tui/gotify-setup.ts +528 -0
- package/tui/settings-overlay.ts +251 -0
- package/tui/telegram-setup.ts +301 -0
- package/types.ts +88 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
declare module "node-notifier" {
|
|
2
|
+
interface Notification {
|
|
3
|
+
title?: string;
|
|
4
|
+
message?: string;
|
|
5
|
+
appID?: string;
|
|
6
|
+
sound?: boolean | string;
|
|
7
|
+
icon?: string;
|
|
8
|
+
wait?: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface Notifier {
|
|
12
|
+
notify(
|
|
13
|
+
notification: Notification,
|
|
14
|
+
callback: (err: Error | null, data: any) => void
|
|
15
|
+
): void;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const notifier: Notifier;
|
|
19
|
+
export default notifier;
|
|
20
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @pi-unipi/notify — Telegram notification platform
|
|
3
|
+
*
|
|
4
|
+
* Sends notifications via Telegram Bot API.
|
|
5
|
+
* Supports auto-detection of chat ID via polling getUpdates.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/** Send a notification via Telegram Bot API */
|
|
9
|
+
export async function sendTelegramNotification(
|
|
10
|
+
botToken: string,
|
|
11
|
+
chatId: string,
|
|
12
|
+
title: string,
|
|
13
|
+
message: string
|
|
14
|
+
): Promise<void> {
|
|
15
|
+
const text = `*${escapeMarkdown(title)}*\n${escapeMarkdown(message)}`;
|
|
16
|
+
const url = `https://api.telegram.org/bot${botToken}/sendMessage`;
|
|
17
|
+
|
|
18
|
+
const response = await fetch(url, {
|
|
19
|
+
method: "POST",
|
|
20
|
+
headers: { "Content-Type": "application/json" },
|
|
21
|
+
body: JSON.stringify({
|
|
22
|
+
chat_id: chatId,
|
|
23
|
+
text,
|
|
24
|
+
parse_mode: "MarkdownV2",
|
|
25
|
+
}),
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
if (!response.ok) {
|
|
29
|
+
const body = await response.text().catch(() => "<no body>");
|
|
30
|
+
throw new Error(
|
|
31
|
+
`Telegram API error ${response.status}: ${body}`
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Poll Telegram getUpdates to detect the chat ID from a user message.
|
|
38
|
+
* Returns the chat ID string, or null if no message found.
|
|
39
|
+
*/
|
|
40
|
+
export async function pollForChatId(
|
|
41
|
+
botToken: string,
|
|
42
|
+
signal?: AbortSignal
|
|
43
|
+
): Promise<string | null> {
|
|
44
|
+
const url = `https://api.telegram.org/bot${botToken}/getUpdates?allowed_updates=["message"]`;
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
const response = await fetch(url, { signal });
|
|
48
|
+
const data = (await response.json()) as {
|
|
49
|
+
ok: boolean;
|
|
50
|
+
result: Array<{
|
|
51
|
+
message?: { chat?: { id?: number } };
|
|
52
|
+
callback_query?: { message?: { chat?: { id?: number } } };
|
|
53
|
+
}>;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
if (data.ok && data.result.length > 0) {
|
|
57
|
+
const lastUpdate = data.result[data.result.length - 1];
|
|
58
|
+
const chatId =
|
|
59
|
+
lastUpdate.message?.chat?.id ||
|
|
60
|
+
lastUpdate.callback_query?.message?.chat?.id;
|
|
61
|
+
if (chatId) {
|
|
62
|
+
return String(chatId);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
} catch (err) {
|
|
66
|
+
if (signal?.aborted) throw err;
|
|
67
|
+
// Network error — return null to allow retry
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Escape special MarkdownV2 characters */
|
|
75
|
+
function escapeMarkdown(text: string): string {
|
|
76
|
+
return text.replace(/[_*\[\]()~`>#+\-=|{}.!\\]/g, "\\$&");
|
|
77
|
+
}
|
package/settings.ts
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @pi-unipi/notify — Configuration management
|
|
3
|
+
*
|
|
4
|
+
* Loads, saves, and validates notification config from ~/.unipi/config/notify/config.json
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
|
|
8
|
+
import { dirname, join } from "path";
|
|
9
|
+
import { homedir } from "os";
|
|
10
|
+
import { NOTIFY_DIRS } from "@pi-unipi/core";
|
|
11
|
+
import type { NotifyConfig } from "./types.js";
|
|
12
|
+
|
|
13
|
+
/** Resolve config path (expands ~ to homedir) */
|
|
14
|
+
function resolveConfigPath(): string {
|
|
15
|
+
const base = NOTIFY_DIRS.CONFIG.replace("~", homedir());
|
|
16
|
+
return join(base, "config.json");
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Default configuration — native enabled, gotify/telegram disabled */
|
|
20
|
+
export const DEFAULT_CONFIG: NotifyConfig = {
|
|
21
|
+
defaultPlatforms: ["native"],
|
|
22
|
+
events: {
|
|
23
|
+
workflow_end: { enabled: true, platforms: [] },
|
|
24
|
+
ralph_loop_end: { enabled: true, platforms: [] },
|
|
25
|
+
mcp_server_error: { enabled: true, platforms: [] },
|
|
26
|
+
agent_end: { enabled: false, platforms: [] },
|
|
27
|
+
memory_consolidated: { enabled: false, platforms: [] },
|
|
28
|
+
session_shutdown: { enabled: false, platforms: [] },
|
|
29
|
+
},
|
|
30
|
+
native: {
|
|
31
|
+
enabled: true,
|
|
32
|
+
},
|
|
33
|
+
gotify: {
|
|
34
|
+
enabled: false,
|
|
35
|
+
priority: 5,
|
|
36
|
+
},
|
|
37
|
+
telegram: {
|
|
38
|
+
enabled: false,
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/** Load config from disk, returning defaults if missing or invalid */
|
|
43
|
+
export function loadConfig(): NotifyConfig {
|
|
44
|
+
const configPath = resolveConfigPath();
|
|
45
|
+
try {
|
|
46
|
+
if (existsSync(configPath)) {
|
|
47
|
+
const raw = readFileSync(configPath, "utf-8");
|
|
48
|
+
const parsed = JSON.parse(raw) as Partial<NotifyConfig>;
|
|
49
|
+
// Merge with defaults to ensure new fields are present
|
|
50
|
+
return mergeWithDefaults(parsed);
|
|
51
|
+
}
|
|
52
|
+
} catch (err) {
|
|
53
|
+
console.warn(
|
|
54
|
+
`[notify] Failed to load config from ${configPath}, using defaults:`,
|
|
55
|
+
err
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
return { ...DEFAULT_CONFIG };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Save config to disk, creating directory if needed */
|
|
62
|
+
export function saveConfig(config: NotifyConfig): void {
|
|
63
|
+
const configPath = resolveConfigPath();
|
|
64
|
+
const dir = dirname(configPath);
|
|
65
|
+
mkdirSync(dir, { recursive: true });
|
|
66
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Update config with partial changes */
|
|
70
|
+
export function updateConfig(partial: Partial<NotifyConfig>): NotifyConfig {
|
|
71
|
+
const current = loadConfig();
|
|
72
|
+
const updated = { ...current, ...partial };
|
|
73
|
+
saveConfig(updated);
|
|
74
|
+
return updated;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Validate that a config has required fields for enabled platforms */
|
|
78
|
+
export function validateConfig(config: NotifyConfig): string[] {
|
|
79
|
+
const errors: string[] = [];
|
|
80
|
+
|
|
81
|
+
if (config.gotify.enabled) {
|
|
82
|
+
if (!config.gotify.serverUrl) {
|
|
83
|
+
errors.push("Gotify: serverUrl is required");
|
|
84
|
+
}
|
|
85
|
+
if (!config.gotify.appToken) {
|
|
86
|
+
errors.push("Gotify: appToken is required");
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (config.telegram.enabled) {
|
|
91
|
+
if (!config.telegram.botToken) {
|
|
92
|
+
errors.push("Telegram: botToken is required");
|
|
93
|
+
}
|
|
94
|
+
if (!config.telegram.chatId) {
|
|
95
|
+
errors.push("Telegram: chatId is required");
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (config.gotify.priority < 1 || config.gotify.priority > 10) {
|
|
100
|
+
errors.push("Gotify: priority must be between 1 and 10");
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return errors;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Merge loaded config with defaults to ensure all fields exist */
|
|
107
|
+
function mergeWithDefaults(loaded: Partial<NotifyConfig>): NotifyConfig {
|
|
108
|
+
return {
|
|
109
|
+
defaultPlatforms: loaded.defaultPlatforms ?? DEFAULT_CONFIG.defaultPlatforms,
|
|
110
|
+
events: { ...DEFAULT_CONFIG.events, ...loaded.events },
|
|
111
|
+
native: { ...DEFAULT_CONFIG.native, ...loaded.native },
|
|
112
|
+
gotify: { ...DEFAULT_CONFIG.gotify, ...loaded.gotify },
|
|
113
|
+
telegram: { ...DEFAULT_CONFIG.telegram, ...loaded.telegram },
|
|
114
|
+
};
|
|
115
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: configure-notify
|
|
3
|
+
description: >
|
|
4
|
+
Help user configure Pi notification settings — platforms (native, Gotify, Telegram),
|
|
5
|
+
events, and per-event routing. Guide through setup or make changes directly.
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Configure Notify
|
|
9
|
+
|
|
10
|
+
Help users configure the `@pi-unipi/notify` notification system.
|
|
11
|
+
|
|
12
|
+
## When to use
|
|
13
|
+
|
|
14
|
+
- User asks to set up notifications
|
|
15
|
+
- User asks to enable/configure Gotify, Telegram, or native notifications
|
|
16
|
+
- User wants to change which events trigger notifications
|
|
17
|
+
- User asks about notification settings
|
|
18
|
+
|
|
19
|
+
## Config location
|
|
20
|
+
|
|
21
|
+
`~/.unipi/config/notify/config.json`
|
|
22
|
+
|
|
23
|
+
## Config structure
|
|
24
|
+
|
|
25
|
+
```json
|
|
26
|
+
{
|
|
27
|
+
"defaultPlatforms": ["native"],
|
|
28
|
+
"events": {
|
|
29
|
+
"workflow_end": { "enabled": true, "platforms": [] },
|
|
30
|
+
"ralph_loop_end": { "enabled": true, "platforms": [] },
|
|
31
|
+
"mcp_server_error": { "enabled": true, "platforms": [] },
|
|
32
|
+
"agent_end": { "enabled": false, "platforms": [] },
|
|
33
|
+
"memory_consolidated": { "enabled": false, "platforms": [] },
|
|
34
|
+
"session_shutdown": { "enabled": false, "platforms": [] }
|
|
35
|
+
},
|
|
36
|
+
"native": {
|
|
37
|
+
"enabled": true,
|
|
38
|
+
"windowsAppId": null
|
|
39
|
+
},
|
|
40
|
+
"gotify": {
|
|
41
|
+
"enabled": false,
|
|
42
|
+
"serverUrl": null,
|
|
43
|
+
"appToken": null,
|
|
44
|
+
"priority": 5
|
|
45
|
+
},
|
|
46
|
+
"telegram": {
|
|
47
|
+
"enabled": false,
|
|
48
|
+
"botToken": null,
|
|
49
|
+
"chatId": null
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Platforms
|
|
55
|
+
|
|
56
|
+
### Native OS (default: enabled)
|
|
57
|
+
|
|
58
|
+
Desktop notifications via node-notifier. Works out of the box on Windows, macOS, Linux.
|
|
59
|
+
|
|
60
|
+
### Gotify (default: disabled)
|
|
61
|
+
|
|
62
|
+
Self-hosted push notification server. Requires:
|
|
63
|
+
- `serverUrl` — URL of your Gotify server (e.g. `https://gotify.example.com`)
|
|
64
|
+
- `appToken` — Application token from Gotify web UI (Apps → Create Application)
|
|
65
|
+
- `priority` — 1-10 (default: 5)
|
|
66
|
+
|
|
67
|
+
**Setup options:**
|
|
68
|
+
1. **Interactive overlay:** Tell user to run `/unipi:notify-set-gotify` for guided setup with connection test
|
|
69
|
+
2. **Manual config:** Edit `config.json` directly with the fields above
|
|
70
|
+
3. **Agent can write config:** Read the current config, merge changes, write back
|
|
71
|
+
|
|
72
|
+
### Telegram (default: disabled)
|
|
73
|
+
|
|
74
|
+
Bot API notifications. Requires:
|
|
75
|
+
- `botToken` — From @BotFather
|
|
76
|
+
- `chatId` — Auto-detected by `/unipi:notify-set-tg`
|
|
77
|
+
|
|
78
|
+
## Commands
|
|
79
|
+
|
|
80
|
+
| Command | Description |
|
|
81
|
+
|---------|-------------|
|
|
82
|
+
| `/unipi:notify-settings` | TUI overlay to toggle platforms and events |
|
|
83
|
+
| `/unipi:notify-set-gotify` | Interactive Gotify setup wizard |
|
|
84
|
+
| `/unipi:notify-set-tg` | Interactive Telegram setup wizard |
|
|
85
|
+
| `/unipi:notify-test` | Send test notification to all enabled platforms |
|
|
86
|
+
|
|
87
|
+
## Events
|
|
88
|
+
|
|
89
|
+
| Event | Default | Description |
|
|
90
|
+
|-------|---------|-------------|
|
|
91
|
+
| `workflow_end` | On | Workflow command completes |
|
|
92
|
+
| `ralph_loop_end` | On | Ralph loop completes |
|
|
93
|
+
| `mcp_server_error` | On | MCP server error |
|
|
94
|
+
| `agent_end` | Off | Agent finishes responding |
|
|
95
|
+
| `memory_consolidated` | Off | Memory auto-saved |
|
|
96
|
+
| `session_shutdown` | Off | Session ends |
|
|
97
|
+
|
|
98
|
+
Each event can override `platforms` — empty array means use `defaultPlatforms`.
|
|
99
|
+
|
|
100
|
+
## Agent workflow
|
|
101
|
+
|
|
102
|
+
### Reading current config
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
cat ~/.unipi/config/notify/config.json
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Updating config programmatically
|
|
109
|
+
|
|
110
|
+
Read the JSON, make changes, write it back. Example:
|
|
111
|
+
|
|
112
|
+
```json
|
|
113
|
+
// To enable Gotify:
|
|
114
|
+
{
|
|
115
|
+
"gotify": {
|
|
116
|
+
"enabled": true,
|
|
117
|
+
"serverUrl": "https://gotify.example.com",
|
|
118
|
+
"appToken": "AT_xxxxx",
|
|
119
|
+
"priority": 7
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Guiding user to interactive setup
|
|
125
|
+
|
|
126
|
+
For Gotify: suggest running `/unipi:notify-set-gotify`
|
|
127
|
+
For Telegram: suggest running `/unipi:notify-set-tg`
|
|
128
|
+
For general settings: suggest `/unipi:notify-settings`
|
|
129
|
+
|
|
130
|
+
## Validation rules
|
|
131
|
+
|
|
132
|
+
- Gotify: `serverUrl` and `appToken` required when enabled
|
|
133
|
+
- Gotify: `priority` must be 1-10
|
|
134
|
+
- Telegram: `botToken` and `chatId` required when enabled
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: notify
|
|
3
|
+
description: >
|
|
4
|
+
Cross-platform notification system for Pi. Use notify_user when you need
|
|
5
|
+
to urgently alert the user about critical findings, errors, or completion
|
|
6
|
+
of long-running tasks.
|
|
7
|
+
allowed-tools:
|
|
8
|
+
- notify_user
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Notify User
|
|
12
|
+
|
|
13
|
+
Use the `notify_user` tool to send notifications to the user's configured
|
|
14
|
+
platforms (native OS, Gotify, Telegram).
|
|
15
|
+
|
|
16
|
+
## When to use notify_user
|
|
17
|
+
|
|
18
|
+
- Critical errors that need immediate attention
|
|
19
|
+
- Completion of long-running tasks (after user has been waiting)
|
|
20
|
+
- Security concerns or suspicious activity detected
|
|
21
|
+
- Results that the user explicitly asked to be notified about
|
|
22
|
+
|
|
23
|
+
## When NOT to use notify_user
|
|
24
|
+
|
|
25
|
+
- Routine status updates (use normal message instead)
|
|
26
|
+
- Non-urgent information (let user read at their pace)
|
|
27
|
+
- Every turn completion (spammy)
|
|
28
|
+
|
|
29
|
+
## Parameters
|
|
30
|
+
|
|
31
|
+
| Parameter | Type | Default | Description |
|
|
32
|
+
|-----------|------|---------|-------------|
|
|
33
|
+
| `message` | string | required | Notification body |
|
|
34
|
+
| `title` | string? | "Pi Notification" | Notification title |
|
|
35
|
+
| `priority` | string? | "normal" | "low", "normal", or "high" |
|
|
36
|
+
| `platforms` | string[]? | all enabled | Override which platforms to use |
|
|
37
|
+
|
|
38
|
+
## Examples
|
|
39
|
+
|
|
40
|
+
Alert on critical error:
|
|
41
|
+
```
|
|
42
|
+
notify_user({
|
|
43
|
+
title: "Build Failed",
|
|
44
|
+
message: "TypeScript compilation failed with 12 errors. Check src/auth.ts.",
|
|
45
|
+
priority: "high"
|
|
46
|
+
})
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Task completion:
|
|
50
|
+
```
|
|
51
|
+
notify_user({
|
|
52
|
+
title: "Deployment Complete",
|
|
53
|
+
message: "Successfully deployed to production. All health checks passed.",
|
|
54
|
+
priority: "normal"
|
|
55
|
+
})
|
|
56
|
+
```
|
package/tools.ts
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @pi-unipi/notify — Agent tool registration
|
|
3
|
+
*
|
|
4
|
+
* Registers the `notify_user` tool for ad-hoc notifications.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { ExtensionAPI, ExtensionContext } from "@mariozechner/pi-coding-agent";
|
|
8
|
+
import { Type } from "@sinclair/typebox";
|
|
9
|
+
import { NOTIFY_TOOLS } from "@pi-unipi/core";
|
|
10
|
+
import { loadConfig } from "./settings.js";
|
|
11
|
+
import { dispatchNotification } from "./events.js";
|
|
12
|
+
import type { NotifyDispatchResult } from "./types.js";
|
|
13
|
+
|
|
14
|
+
/** Schema for notify_user tool parameters */
|
|
15
|
+
const NotifyUserSchema = Type.Object({
|
|
16
|
+
message: Type.String({ description: "Notification message body" }),
|
|
17
|
+
title: Type.Optional(
|
|
18
|
+
Type.String({ description: "Notification title (default: Pi Notification)" })
|
|
19
|
+
),
|
|
20
|
+
priority: Type.Optional(
|
|
21
|
+
Type.String({
|
|
22
|
+
enum: ["low", "normal", "high"],
|
|
23
|
+
default: "normal",
|
|
24
|
+
description: "Priority level",
|
|
25
|
+
})
|
|
26
|
+
),
|
|
27
|
+
platforms: Type.Optional(
|
|
28
|
+
Type.Array(
|
|
29
|
+
Type.String({ enum: ["native", "gotify", "telegram"] }),
|
|
30
|
+
{ description: "Override platforms for this notification" }
|
|
31
|
+
)
|
|
32
|
+
),
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Register the notify_user tool with pi.
|
|
37
|
+
*/
|
|
38
|
+
export function registerNotifyTools(pi: ExtensionAPI): void {
|
|
39
|
+
pi.registerTool({
|
|
40
|
+
name: NOTIFY_TOOLS.NOTIFY_USER,
|
|
41
|
+
label: "Notify User",
|
|
42
|
+
description:
|
|
43
|
+
"Send a notification to the user's configured platforms (native OS, Gotify, Telegram). " +
|
|
44
|
+
"Use for critical errors, completion of long-running tasks, or when the user explicitly asked to be notified.",
|
|
45
|
+
parameters: NotifyUserSchema,
|
|
46
|
+
async execute(_toolCallId, params, _signal, _onUpdate, ctx: ExtensionContext) {
|
|
47
|
+
const {
|
|
48
|
+
message,
|
|
49
|
+
title,
|
|
50
|
+
priority: _priority,
|
|
51
|
+
platforms,
|
|
52
|
+
} = params as {
|
|
53
|
+
message: string;
|
|
54
|
+
title?: string;
|
|
55
|
+
priority?: "low" | "normal" | "high";
|
|
56
|
+
platforms?: Array<"native" | "gotify" | "telegram">;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const config = loadConfig();
|
|
60
|
+
|
|
61
|
+
// Resolve title
|
|
62
|
+
const notifTitle = title || "Pi Notification";
|
|
63
|
+
|
|
64
|
+
// Resolve platforms — use params.platforms or global defaults
|
|
65
|
+
const notifPlatforms = platforms || config.defaultPlatforms;
|
|
66
|
+
|
|
67
|
+
// Dispatch notification
|
|
68
|
+
const result: NotifyDispatchResult = await dispatchNotification(
|
|
69
|
+
pi,
|
|
70
|
+
notifTitle,
|
|
71
|
+
message,
|
|
72
|
+
notifPlatforms,
|
|
73
|
+
"agent_tool",
|
|
74
|
+
config
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
// Format result
|
|
78
|
+
const platformResults = result.results.map(
|
|
79
|
+
(r) => `${r.platform}: ${r.success ? "✓ sent" : `✗ ${r.error || "failed"}`}`
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
content: [
|
|
84
|
+
{
|
|
85
|
+
type: "text" as const,
|
|
86
|
+
text: `Notification sent to ${result.results.length} platform(s):\n${platformResults.join("\n")}`,
|
|
87
|
+
},
|
|
88
|
+
],
|
|
89
|
+
details: {
|
|
90
|
+
platforms: result.results.map((r) => r.platform),
|
|
91
|
+
allSuccess: result.allSuccess,
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
}
|