@pi-unipi/unipi 2.1.2 → 2.1.3
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/package.json +22 -22
- package/packages/ask-user/package.json +3 -3
- package/packages/autocomplete/package.json +1 -1
- package/packages/btw/package.json +2 -2
- package/packages/cocoindex/package.json +2 -2
- package/packages/compactor/package.json +4 -4
- package/packages/core/package.json +1 -1
- package/packages/footer/package.json +3 -3
- package/packages/info-screen/package.json +2 -2
- package/packages/input-shortcuts/package.json +3 -3
- package/packages/kanboard/package.json +2 -2
- package/packages/mcp/package.json +2 -2
- package/packages/memory/package.json +3 -3
- package/packages/milestone/package.json +2 -2
- package/packages/notify/README.md +2 -1
- package/packages/notify/events.ts +119 -81
- package/packages/notify/package.json +2 -2
- package/packages/notify/settings.ts +1 -0
- package/packages/notify/skills/configure-notify/SKILL.md +3 -1
- package/packages/notify/src/__tests__/event-bus.test.ts +3 -2
- package/packages/ralph/package.json +3 -3
- package/packages/subagents/package.json +5 -5
- package/packages/updater/package.json +3 -3
- package/packages/utility/package.json +2 -2
- package/packages/web-api/package.json +2 -2
- package/packages/workflow/package.json +2 -2
- package/packages/info-screen/tui/info-overlay.ts +0 -504
- package/packages/kanboard/tui/kanboard-overlay.ts +0 -300
- package/packages/memory/tui/settings-tui.ts +0 -281
- package/packages/notify/platforms/focus-win.ts +0 -123
- package/packages/notify/platforms/focus.ts +0 -33
- package/packages/notify/platforms/gotify.ts +0 -36
- package/packages/notify/platforms/native.ts +0 -79
- package/packages/notify/platforms/node-notifier.d.ts +0 -20
- package/packages/notify/platforms/ntfy.ts +0 -48
- package/packages/notify/platforms/telegram.ts +0 -77
- package/packages/notify/tui/gotify-setup.ts +0 -528
- package/packages/notify/tui/ntfy-setup.ts +0 -652
- package/packages/notify/tui/recap-model-selector.ts +0 -301
- package/packages/notify/tui/settings-overlay.ts +0 -352
- package/packages/notify/tui/telegram-setup.ts +0 -301
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @pi-unipi/notify — Focus detection abstraction
|
|
3
|
-
*
|
|
4
|
-
* Unified interface for checking whether the terminal window is the
|
|
5
|
-
* foreground (active) window. Platform-specific implementations are
|
|
6
|
-
* dispatched based on process.platform.
|
|
7
|
-
*
|
|
8
|
-
* Currently implemented:
|
|
9
|
-
* - Windows (win32): calls focus-win.ts
|
|
10
|
-
*
|
|
11
|
-
* Unimplemented platforms always return false (no suppression).
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import { isWindowFocusedOnWindows } from "./focus-win.js";
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Check whether the current terminal/console window is the foreground
|
|
18
|
-
* (active) window. Used by sendNativeNotification to optionally
|
|
19
|
-
* suppress notifications when the user is already looking at the screen.
|
|
20
|
-
*
|
|
21
|
-
* @returns true if the terminal is the foreground window, false otherwise.
|
|
22
|
-
* On unimplemented platforms, always returns false.
|
|
23
|
-
*/
|
|
24
|
-
export async function isWindowFocused(): Promise<boolean> {
|
|
25
|
-
switch (process.platform) {
|
|
26
|
-
case "win32":
|
|
27
|
-
return await isWindowFocusedOnWindows();
|
|
28
|
-
// TODO: macOS — use osascript to check frontmost application
|
|
29
|
-
// TODO: Linux — use xdotool (X11) or per-compositor tool (Wayland)
|
|
30
|
-
default:
|
|
31
|
-
return false;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @pi-unipi/notify — Gotify notification platform
|
|
3
|
-
*
|
|
4
|
-
* Sends push notifications to a Gotify server via HTTP POST.
|
|
5
|
-
* Gotify is a self-hosted push notification server.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
/** Send a notification to Gotify server */
|
|
9
|
-
export async function sendGotifyNotification(
|
|
10
|
-
serverUrl: string,
|
|
11
|
-
appToken: string,
|
|
12
|
-
title: string,
|
|
13
|
-
message: string,
|
|
14
|
-
priority: number = 5
|
|
15
|
-
): Promise<void> {
|
|
16
|
-
const url = serverUrl.replace(/\/$/, "") + "/message";
|
|
17
|
-
const response = await fetch(url, {
|
|
18
|
-
method: "POST",
|
|
19
|
-
headers: {
|
|
20
|
-
"Content-Type": "application/json",
|
|
21
|
-
"X-Gotify-Key": appToken,
|
|
22
|
-
},
|
|
23
|
-
body: JSON.stringify({
|
|
24
|
-
title,
|
|
25
|
-
message,
|
|
26
|
-
priority,
|
|
27
|
-
}),
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
if (!response.ok) {
|
|
31
|
-
const body = await response.text().catch(() => "<no body>");
|
|
32
|
-
throw new Error(
|
|
33
|
-
`Gotify API error ${response.status}: ${body}`
|
|
34
|
-
);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @pi-unipi/notify — Native OS notification platform
|
|
3
|
-
*
|
|
4
|
-
* Wraps node-notifier for cross-platform desktop notifications.
|
|
5
|
-
* Windows: SnoreToast (no admin required)
|
|
6
|
-
* macOS: terminal-notifier
|
|
7
|
-
* Linux: notify-send / libnotify
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import notifier from "node-notifier";
|
|
11
|
-
import { isWindowFocused } from "./focus.js";
|
|
12
|
-
|
|
13
|
-
/** Options for native notification */
|
|
14
|
-
export interface NativeNotificationOptions {
|
|
15
|
-
/** Windows appID to show instead of "SnoreToast" */
|
|
16
|
-
windowsAppId?: string;
|
|
17
|
-
/**
|
|
18
|
-
* When true, suppresses the notification if the terminal window is
|
|
19
|
-
* the foreground (active) window. Only effective on platforms where
|
|
20
|
-
* `isWindowFocused` is implemented (currently Windows).
|
|
21
|
-
*/
|
|
22
|
-
suppressWhenFocused?: boolean;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Thrown by sendNativeNotification when the notification was suppressed
|
|
27
|
-
* because suppressWhenFocused is set and the terminal window is focused.
|
|
28
|
-
*
|
|
29
|
-
* Callers should catch this and treat it as intentional suppression,
|
|
30
|
-
* NOT as a send failure.
|
|
31
|
-
*/
|
|
32
|
-
export class SuppressedError extends Error {
|
|
33
|
-
constructor() {
|
|
34
|
-
super("Notification suppressed: terminal window is focused");
|
|
35
|
-
this.name = "SuppressedError";
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Send a native OS notification.
|
|
41
|
-
*
|
|
42
|
-
* When `suppressWhenFocused` is true and `isWindowFocused()` returns true
|
|
43
|
-
* (i.e. the terminal is the foreground window), the notification is
|
|
44
|
-
* suppressed and the promise rejects with SuppressedError.
|
|
45
|
-
*
|
|
46
|
-
* Resolves when notification is shown, rejects with SuppressedError on
|
|
47
|
-
* suppression or with a standard Error on failure.
|
|
48
|
-
*/
|
|
49
|
-
export async function sendNativeNotification(
|
|
50
|
-
title: string,
|
|
51
|
-
message: string,
|
|
52
|
-
options?: NativeNotificationOptions
|
|
53
|
-
): Promise<void> {
|
|
54
|
-
// Suppress if the terminal window is currently focused
|
|
55
|
-
if (options?.suppressWhenFocused && await isWindowFocused()) {
|
|
56
|
-
throw new SuppressedError();
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
return new Promise((resolve, reject) => {
|
|
60
|
-
notifier.notify(
|
|
61
|
-
{
|
|
62
|
-
title,
|
|
63
|
-
message,
|
|
64
|
-
appID: options?.windowsAppId,
|
|
65
|
-
},
|
|
66
|
-
(err: Error | null) => {
|
|
67
|
-
if (err) {
|
|
68
|
-
reject(
|
|
69
|
-
new Error(
|
|
70
|
-
`Native notification failed: ${err.message}`
|
|
71
|
-
)
|
|
72
|
-
);
|
|
73
|
-
} else {
|
|
74
|
-
resolve();
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
);
|
|
78
|
-
});
|
|
79
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @pi-unipi/notify — ntfy notification platform
|
|
3
|
-
*
|
|
4
|
-
* Sends push notifications to an ntfy server via HTTP POST.
|
|
5
|
-
* ntfy is a simple HTTP-based pub-sub notification service.
|
|
6
|
-
* Supports self-hosted instances and ntfy.sh (public).
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
/** Send a notification to an ntfy server */
|
|
10
|
-
export async function sendNtfyNotification(
|
|
11
|
-
serverUrl: string,
|
|
12
|
-
topic: string,
|
|
13
|
-
title: string,
|
|
14
|
-
message: string,
|
|
15
|
-
priority: number = 3,
|
|
16
|
-
token?: string
|
|
17
|
-
): Promise<void> {
|
|
18
|
-
// ntfy supports POSTing to the server root with a JSON body that carries
|
|
19
|
-
// topic/title/message/priority. JSON bodies are UTF-8 safe, unlike HTTP
|
|
20
|
-
// headers which must be ByteString (Latin-1) and reject characters like
|
|
21
|
-
// em dash (U+2014). See https://docs.ntfy.sh/publish/#publish-as-json
|
|
22
|
-
const url = serverUrl.replace(/\/$/, "");
|
|
23
|
-
const headers: Record<string, string> = {
|
|
24
|
-
"Content-Type": "application/json",
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
if (token) {
|
|
28
|
-
headers["Authorization"] = `Bearer ${token}`;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const body = JSON.stringify({
|
|
32
|
-
topic,
|
|
33
|
-
title,
|
|
34
|
-
message,
|
|
35
|
-
priority: Math.max(1, Math.min(5, priority)),
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
const response = await fetch(url, {
|
|
39
|
-
method: "POST",
|
|
40
|
-
headers,
|
|
41
|
-
body,
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
if (!response.ok) {
|
|
45
|
-
const resBody = await response.text().catch(() => "<no body>");
|
|
46
|
-
throw new Error(`ntfy API error ${response.status}: ${resBody}`);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
@@ -1,77 +0,0 @@
|
|
|
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
|
-
}
|