@h-rig/notifications-plugin 0.0.6-alpha.156

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 ADDED
@@ -0,0 +1 @@
1
+ # @h-rig/notifications-plugin
@@ -0,0 +1,2 @@
1
+ export type { RigEvent, NotificationTarget, NotificationConfig, } from "./notifications";
2
+ export { loadNotificationConfig, dispatchEventToTargets, buildTargetPayload, resolveNotifyTimeoutMs, } from "./notifications";
@@ -0,0 +1,74 @@
1
+ // @bun
2
+ // packages/notifications-plugin/src/notifications.ts
3
+ async function loadNotificationConfig(path) {
4
+ try {
5
+ const parsed = await Bun.file(path).json();
6
+ return {
7
+ targets: (parsed.targets || []).filter((target) => !!target && !!target.id && !!target.url)
8
+ };
9
+ } catch {
10
+ return { targets: [] };
11
+ }
12
+ }
13
+ async function dispatchEventToTargets(event, targets, deps = {}) {
14
+ const fetchFn = deps.fetch ?? globalThis.fetch;
15
+ const timeoutMs = resolveNotifyTimeoutMs();
16
+ let sent = 0;
17
+ for (const target of targets) {
18
+ if (target.enabled === false) {
19
+ continue;
20
+ }
21
+ if (target.events && target.events.length > 0 && !target.events.includes(event.type)) {
22
+ continue;
23
+ }
24
+ const payload = buildTargetPayload(target, event);
25
+ const controller = new AbortController;
26
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
27
+ try {
28
+ const response = await fetchFn(target.url, {
29
+ method: "POST",
30
+ headers: {
31
+ "Content-Type": "application/json",
32
+ ...target.headers || {}
33
+ },
34
+ body: JSON.stringify(payload),
35
+ signal: controller.signal
36
+ });
37
+ if (response.ok) {
38
+ sent += 1;
39
+ } else {
40
+ console.error(`[runtime] failed to dispatch event ${event.type} to ${target.id}: ${response.status} ${response.statusText}`);
41
+ }
42
+ } catch (error) {
43
+ console.error(`[runtime] failed to dispatch event ${event.type} to ${target.id}`, error);
44
+ } finally {
45
+ clearTimeout(timer);
46
+ }
47
+ }
48
+ return sent;
49
+ }
50
+ function buildTargetPayload(target, event) {
51
+ if (target.type === "slack-webhook") {
52
+ return {
53
+ text: `[Rig] ${event.type} ${event.runId ? `(run ${event.runId})` : ""}`.trim(),
54
+ event
55
+ };
56
+ }
57
+ return {
58
+ source: "project-rig",
59
+ target: target.id,
60
+ summary: `${event.type}${event.runId ? `:${event.runId}` : ""}`,
61
+ event
62
+ };
63
+ }
64
+ function resolveNotifyTimeoutMs() {
65
+ const raw = process.env.RIG_NOTIFY_TIMEOUT_MS;
66
+ const parsed = raw ? Number.parseInt(raw, 10) : NaN;
67
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : 1e4;
68
+ }
69
+ export {
70
+ resolveNotifyTimeoutMs,
71
+ loadNotificationConfig,
72
+ dispatchEventToTargets,
73
+ buildTargetPayload
74
+ };
@@ -0,0 +1,31 @@
1
+ export type RigEvent = {
2
+ id?: string;
3
+ runId?: string;
4
+ timestamp?: string;
5
+ type: string;
6
+ payload?: Record<string, unknown>;
7
+ };
8
+ export type NotificationTarget = {
9
+ id: string;
10
+ type: "slack-webhook" | "linear-webhook" | "notion-webhook" | "generic-webhook";
11
+ url: string;
12
+ enabled?: boolean;
13
+ events?: string[];
14
+ headers?: Record<string, string>;
15
+ };
16
+ export type NotificationConfig = {
17
+ targets: NotificationTarget[];
18
+ };
19
+ type NotificationFetchResponse = {
20
+ ok: boolean;
21
+ status?: number;
22
+ statusText?: string;
23
+ };
24
+ type NotificationFetch = (input: string, init: RequestInit) => Promise<NotificationFetchResponse>;
25
+ export declare function loadNotificationConfig(path: string): Promise<NotificationConfig>;
26
+ export declare function dispatchEventToTargets(event: RigEvent, targets: NotificationTarget[], deps?: {
27
+ fetch?: NotificationFetch;
28
+ }): Promise<number>;
29
+ export declare function buildTargetPayload(target: NotificationTarget, event: RigEvent): Record<string, unknown>;
30
+ export declare function resolveNotifyTimeoutMs(): number;
31
+ export {};
@@ -0,0 +1,74 @@
1
+ // @bun
2
+ // packages/notifications-plugin/src/notifications.ts
3
+ async function loadNotificationConfig(path) {
4
+ try {
5
+ const parsed = await Bun.file(path).json();
6
+ return {
7
+ targets: (parsed.targets || []).filter((target) => !!target && !!target.id && !!target.url)
8
+ };
9
+ } catch {
10
+ return { targets: [] };
11
+ }
12
+ }
13
+ async function dispatchEventToTargets(event, targets, deps = {}) {
14
+ const fetchFn = deps.fetch ?? globalThis.fetch;
15
+ const timeoutMs = resolveNotifyTimeoutMs();
16
+ let sent = 0;
17
+ for (const target of targets) {
18
+ if (target.enabled === false) {
19
+ continue;
20
+ }
21
+ if (target.events && target.events.length > 0 && !target.events.includes(event.type)) {
22
+ continue;
23
+ }
24
+ const payload = buildTargetPayload(target, event);
25
+ const controller = new AbortController;
26
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
27
+ try {
28
+ const response = await fetchFn(target.url, {
29
+ method: "POST",
30
+ headers: {
31
+ "Content-Type": "application/json",
32
+ ...target.headers || {}
33
+ },
34
+ body: JSON.stringify(payload),
35
+ signal: controller.signal
36
+ });
37
+ if (response.ok) {
38
+ sent += 1;
39
+ } else {
40
+ console.error(`[runtime] failed to dispatch event ${event.type} to ${target.id}: ${response.status} ${response.statusText}`);
41
+ }
42
+ } catch (error) {
43
+ console.error(`[runtime] failed to dispatch event ${event.type} to ${target.id}`, error);
44
+ } finally {
45
+ clearTimeout(timer);
46
+ }
47
+ }
48
+ return sent;
49
+ }
50
+ function buildTargetPayload(target, event) {
51
+ if (target.type === "slack-webhook") {
52
+ return {
53
+ text: `[Rig] ${event.type} ${event.runId ? `(run ${event.runId})` : ""}`.trim(),
54
+ event
55
+ };
56
+ }
57
+ return {
58
+ source: "project-rig",
59
+ target: target.id,
60
+ summary: `${event.type}${event.runId ? `:${event.runId}` : ""}`,
61
+ event
62
+ };
63
+ }
64
+ function resolveNotifyTimeoutMs() {
65
+ const raw = process.env.RIG_NOTIFY_TIMEOUT_MS;
66
+ const parsed = raw ? Number.parseInt(raw, 10) : NaN;
67
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : 1e4;
68
+ }
69
+ export {
70
+ resolveNotifyTimeoutMs,
71
+ loadNotificationConfig,
72
+ dispatchEventToTargets,
73
+ buildTargetPayload
74
+ };
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@h-rig/notifications-plugin",
3
+ "version": "0.0.6-alpha.156",
4
+ "type": "module",
5
+ "description": "First-party notification policy/channels capability plugin for Rig.",
6
+ "license": "UNLICENSED",
7
+ "files": [
8
+ "dist",
9
+ "README.md"
10
+ ],
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/src/index.d.ts",
14
+ "import": "./dist/src/index.js"
15
+ },
16
+ "./notifications": {
17
+ "types": "./dist/src/notifications.d.ts",
18
+ "import": "./dist/src/notifications.js"
19
+ }
20
+ },
21
+ "engines": {
22
+ "bun": ">=1.3.11"
23
+ },
24
+ "main": "./dist/src/index.js",
25
+ "module": "./dist/src/index.js",
26
+ "types": "./dist/src/index.d.ts"
27
+ }