@burdenoff/vibe-plugin-notify 1.0.0

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,188 @@
1
+ # @burdenoff/vibe-plugin-notify
2
+
3
+ Generic webhook notification plugin for AI coding tools - a VibeControls Agent plugin.
4
+
5
+ Receives events from AI coding tools (OpenCode, Claude Code, Gemini CLI, etc.) and delivers to configured webhooks (Slack, Discord, custom).
6
+
7
+ ## Architecture
8
+
9
+ ```
10
+ ┌─────────────────────┐ ┌─────────────────────────────────────────────┐
11
+ │ AI Coding Tool │ │ VibeControls Agent │
12
+ │ │ │ │
13
+ │ Tool-specific │────>│ vibe-plugin-notify │
14
+ │ plugin (e.g. │POST │ │ │
15
+ │ opencode-vibe- │ │ ├──> Slack webhook │
16
+ │ webhook) │ │ ├──> Discord webhook │
17
+ └─────────────────────┘ │ └──> Custom webhook │
18
+ └─────────────────────────────────────────────┘
19
+ ```
20
+
21
+ **Why this architecture?**
22
+
23
+ | Component | Purpose |
24
+ | -------------------- | -------------------------------------------------------------- |
25
+ | Tool-specific plugin | Runs **inside AI tool** - 100% reliable native event detection |
26
+ | `vibe-plugin-notify` | Runs in **VibeControls** - webhook management, CLI, delivery |
27
+
28
+ ## Supported AI Tools
29
+
30
+ | Tool | Plugin | Status |
31
+ | ----------- | ----------------------- | ------- |
32
+ | OpenCode | `opencode-vibe-webhook` | Ready |
33
+ | Claude Code | Coming soon | Planned |
34
+ | Gemini CLI | Coming soon | Planned |
35
+
36
+ ## Quick Start
37
+
38
+ ### 1. Install this plugin in VibeControls
39
+
40
+ ```bash
41
+ vibe plugin install @burdenoff/vibe-plugin-notify
42
+ ```
43
+
44
+ ### 2. Install tool-specific plugin
45
+
46
+ For OpenCode:
47
+
48
+ ```bash
49
+ # Add to opencode.json
50
+ {
51
+ "plugins": ["opencode-vibe-webhook"]
52
+ }
53
+ ```
54
+
55
+ ### 3. Add a webhook
56
+
57
+ ```bash
58
+ vibe notify add https://hooks.slack.com/services/xxx/yyy/zzz
59
+ ```
60
+
61
+ That's it! When your AI tool completes a task, you'll get a notification.
62
+
63
+ ## CLI Commands
64
+
65
+ | Command | Description |
66
+ | -------------------------- | ------------------------ |
67
+ | `vibe notify add <url>` | Add a webhook URL |
68
+ | `vibe notify list` | List all webhooks |
69
+ | `vibe notify remove <id>` | Remove a webhook |
70
+ | `vibe notify test <id>` | Send a test notification |
71
+ | `vibe notify enable <id>` | Enable a webhook |
72
+ | `vibe notify disable <id>` | Disable a webhook |
73
+
74
+ ## Usage Examples
75
+
76
+ ### Add a Slack Webhook
77
+
78
+ ```bash
79
+ vibe notify add https://hooks.slack.com/services/T00/B00/XXX --name "Slack Alerts"
80
+ ```
81
+
82
+ ### Add a Discord Webhook
83
+
84
+ ```bash
85
+ vibe notify add https://discord.com/api/webhooks/123/abc --name "Discord"
86
+ ```
87
+
88
+ ### Add with specific events
89
+
90
+ ```bash
91
+ vibe notify add https://example.com/webhook --events "session.idle,session.error"
92
+ ```
93
+
94
+ ## REST API
95
+
96
+ | Method | Endpoint | Description |
97
+ | -------- | ---------------------------------- | ------------------------------- |
98
+ | `GET` | `/api/notify/health` | Health check |
99
+ | `POST` | `/api/notify/hook` | Receive events from tool plugin |
100
+ | `GET` | `/api/notify/webhooks` | List webhooks |
101
+ | `POST` | `/api/notify/webhooks` | Create webhook |
102
+ | `DELETE` | `/api/notify/webhooks/:id` | Delete webhook |
103
+ | `POST` | `/api/notify/webhooks/:id/test` | Test webhook |
104
+ | `POST` | `/api/notify/webhooks/:id/enable` | Enable webhook |
105
+ | `POST` | `/api/notify/webhooks/:id/disable` | Disable webhook |
106
+ | `GET` | `/api/notify/deliveries` | Delivery history |
107
+
108
+ ## Webhook Payload
109
+
110
+ When an AI tool completes a task, webhooks receive:
111
+
112
+ ```json
113
+ {
114
+ "id": "dlv_abc123",
115
+ "timestamp": "2026-02-27T10:30:00.000Z",
116
+ "event": {
117
+ "type": "session.idle",
118
+ "sessionId": "sess_xyz",
119
+ "sessionTitle": "Add user authentication",
120
+ "projectName": "my-app",
121
+ "properties": {}
122
+ },
123
+ "source": {
124
+ "agent": "vibecontrols-agent",
125
+ "plugin": "notify",
126
+ "version": "1.0.0",
127
+ "tool": "opencode"
128
+ }
129
+ }
130
+ ```
131
+
132
+ ### Slack Format
133
+
134
+ ```
135
+ :white_check_mark: *Task Completed* (opencode)
136
+ > Add user authentication
137
+ ```
138
+
139
+ ### Discord Format
140
+
141
+ ```
142
+ **Task Completed** (opencode)
143
+ > Add user authentication
144
+ ```
145
+
146
+ ## Supported Events
147
+
148
+ | Event | Description |
149
+ | ------------------ | ------------------------ |
150
+ | `session.idle` | Task completed (default) |
151
+ | `session.error` | Task failed |
152
+ | `permission.asked` | AI needs user permission |
153
+ | `*` | All events |
154
+
155
+ ## Creating a Tool Plugin
156
+
157
+ To add support for a new AI coding tool, create a plugin that posts events to `/api/notify/hook`:
158
+
159
+ ```typescript
160
+ // Example event payload
161
+ const event = {
162
+ type: "session.idle",
163
+ source: "your-tool-name",
164
+ properties: {
165
+ sessionID: "sess_123",
166
+ sessionTitle: "Task description",
167
+ },
168
+ timestamp: new Date().toISOString(),
169
+ };
170
+
171
+ await fetch("http://localhost:4321/api/notify/hook", {
172
+ method: "POST",
173
+ headers: { "Content-Type": "application/json" },
174
+ body: JSON.stringify(event),
175
+ });
176
+ ```
177
+
178
+ See [opencode-vibe-webhook](../opencode-vibe-webhook/) for a reference implementation.
179
+
180
+ ## Requirements
181
+
182
+ - VibeControls Agent 2.0+
183
+ - Tool-specific plugin (e.g., `opencode-vibe-webhook` for OpenCode)
184
+ - Bun 1.3+
185
+
186
+ ## License
187
+
188
+ Proprietary - Burdenoff Consultancy Services Pvt. Ltd.
@@ -0,0 +1,15 @@
1
+ /**
2
+ * CLI Commands for vibe-plugin-notify
3
+ *
4
+ * Commands:
5
+ * vibe notify add <url> - Add a webhook
6
+ * vibe notify list - List all webhooks
7
+ * vibe notify remove <id> - Remove a webhook
8
+ * vibe notify test <id> - Test a webhook
9
+ * vibe notify enable <id> - Enable a webhook
10
+ * vibe notify disable <id> - Disable a webhook
11
+ */
12
+ import type { Command } from "commander";
13
+ import type { HostServices } from "../types.js";
14
+ export declare function registerCommands(program: Command, hostServices: HostServices): void;
15
+ //# sourceMappingURL=commands.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/cli/commands.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,YAAY,EAAmB,MAAM,aAAa,CAAC;AAqBjE,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,OAAO,EAChB,YAAY,EAAE,YAAY,GACzB,IAAI,CA+PN"}
@@ -0,0 +1,194 @@
1
+ /**
2
+ * CLI Commands for vibe-plugin-notify
3
+ *
4
+ * Commands:
5
+ * vibe notify add <url> - Add a webhook
6
+ * vibe notify list - List all webhooks
7
+ * vibe notify remove <id> - Remove a webhook
8
+ * vibe notify test <id> - Test a webhook
9
+ * vibe notify enable <id> - Enable a webhook
10
+ * vibe notify disable <id> - Disable a webhook
11
+ */
12
+ export function registerCommands(program, hostServices) {
13
+ const cmd = program
14
+ .command("notify")
15
+ .description("Webhook management for AI coding tool notifications");
16
+ // Add webhook
17
+ cmd
18
+ .command("add <url>")
19
+ .description("Add a webhook URL")
20
+ .option("-n, --name <name>", "Friendly name for the webhook")
21
+ .option("-e, --events <events>", "Comma-separated event types (default: session.idle)")
22
+ .action(async (url, options) => {
23
+ const baseUrl = hostServices.getAgentBaseUrl();
24
+ const eventTypes = options.events?.split(",").map((e) => e.trim());
25
+ try {
26
+ const response = await fetch(`${baseUrl}/api/notify/webhooks`, {
27
+ method: "POST",
28
+ headers: { "Content-Type": "application/json" },
29
+ body: JSON.stringify({
30
+ url,
31
+ name: options.name,
32
+ eventTypes,
33
+ test: true,
34
+ }),
35
+ });
36
+ const data = (await response.json());
37
+ if (!response.ok) {
38
+ const errorData = data;
39
+ console.error(`Error: ${errorData.error || "Failed to add webhook"}`);
40
+ process.exit(1);
41
+ }
42
+ const successData = data;
43
+ console.log("\nWebhook added successfully!\n");
44
+ console.log(` ID: ${successData.webhook.id}`);
45
+ console.log(` Name: ${successData.webhook.name}`);
46
+ console.log(` URL: ${successData.webhook.url}`);
47
+ console.log(` Events: ${successData.webhook.eventTypes.join(", ")}`);
48
+ if (successData.test) {
49
+ if (successData.test.success) {
50
+ console.log(` Test: Passed (${successData.test.statusCode})`);
51
+ }
52
+ else {
53
+ console.log(` Test: Failed - ${successData.test.error}`);
54
+ }
55
+ }
56
+ console.log();
57
+ }
58
+ catch (error) {
59
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
60
+ process.exit(1);
61
+ }
62
+ });
63
+ // List webhooks
64
+ cmd
65
+ .command("list")
66
+ .description("List all webhooks")
67
+ .action(async () => {
68
+ const baseUrl = hostServices.getAgentBaseUrl();
69
+ try {
70
+ const response = await fetch(`${baseUrl}/api/notify/webhooks`);
71
+ const data = (await response.json());
72
+ if (!response.ok) {
73
+ const errorData = data;
74
+ console.error(`Error: ${errorData.error || "Failed to list webhooks"}`);
75
+ process.exit(1);
76
+ }
77
+ const listData = data;
78
+ if (listData.webhooks.length === 0) {
79
+ console.log("\nNo webhooks configured.\n");
80
+ console.log("Add one with: vibe notify add <url>\n");
81
+ return;
82
+ }
83
+ console.log(`\nWebhooks (${listData.count}):\n`);
84
+ for (const webhook of listData.webhooks) {
85
+ const status = webhook.enabled ? "enabled" : "disabled";
86
+ console.log(` ${webhook.id}`);
87
+ console.log(` Name: ${webhook.name}`);
88
+ console.log(` URL: ${webhook.url}`);
89
+ console.log(` Status: ${status}`);
90
+ console.log(` Events: ${webhook.eventTypes.join(", ")}`);
91
+ console.log();
92
+ }
93
+ }
94
+ catch (error) {
95
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
96
+ process.exit(1);
97
+ }
98
+ });
99
+ // Remove webhook
100
+ cmd
101
+ .command("remove <id>")
102
+ .description("Remove a webhook")
103
+ .action(async (id) => {
104
+ const baseUrl = hostServices.getAgentBaseUrl();
105
+ try {
106
+ const response = await fetch(`${baseUrl}/api/notify/webhooks/${id}`, {
107
+ method: "DELETE",
108
+ });
109
+ const data = (await response.json());
110
+ if (!response.ok) {
111
+ const errorData = data;
112
+ console.error(`Error: ${errorData.error || "Failed to remove webhook"}`);
113
+ process.exit(1);
114
+ }
115
+ console.log(`\nWebhook ${id} removed.\n`);
116
+ }
117
+ catch (error) {
118
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
119
+ process.exit(1);
120
+ }
121
+ });
122
+ // Test webhook
123
+ cmd
124
+ .command("test <id>")
125
+ .description("Send a test notification to a webhook")
126
+ .action(async (id) => {
127
+ const baseUrl = hostServices.getAgentBaseUrl();
128
+ try {
129
+ const response = await fetch(`${baseUrl}/api/notify/webhooks/${id}/test`, { method: "POST" });
130
+ const data = (await response.json());
131
+ if (!response.ok) {
132
+ const errorData = data;
133
+ console.error(`Error: ${errorData.error || "Failed to test webhook"}`);
134
+ process.exit(1);
135
+ }
136
+ const testData = data;
137
+ console.log(`\nTest notification sent to: ${testData.webhook.name}\n`);
138
+ if (testData.test?.success) {
139
+ console.log(` Result: Passed (HTTP ${testData.test.statusCode})\n`);
140
+ }
141
+ else {
142
+ console.log(` Result: Failed\n`);
143
+ console.log(` Error: ${testData.test?.error}\n`);
144
+ }
145
+ }
146
+ catch (error) {
147
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
148
+ process.exit(1);
149
+ }
150
+ });
151
+ // Enable webhook
152
+ cmd
153
+ .command("enable <id>")
154
+ .description("Enable a webhook")
155
+ .action(async (id) => {
156
+ const baseUrl = hostServices.getAgentBaseUrl();
157
+ try {
158
+ const response = await fetch(`${baseUrl}/api/notify/webhooks/${id}/enable`, { method: "POST" });
159
+ const data = (await response.json());
160
+ if (!response.ok) {
161
+ const errorData = data;
162
+ console.error(`Error: ${errorData.error || "Failed to enable webhook"}`);
163
+ process.exit(1);
164
+ }
165
+ console.log(`\nWebhook ${id} enabled.\n`);
166
+ }
167
+ catch (error) {
168
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
169
+ process.exit(1);
170
+ }
171
+ });
172
+ // Disable webhook
173
+ cmd
174
+ .command("disable <id>")
175
+ .description("Disable a webhook")
176
+ .action(async (id) => {
177
+ const baseUrl = hostServices.getAgentBaseUrl();
178
+ try {
179
+ const response = await fetch(`${baseUrl}/api/notify/webhooks/${id}/disable`, { method: "POST" });
180
+ const data = (await response.json());
181
+ if (!response.ok) {
182
+ const errorData = data;
183
+ console.error(`Error: ${errorData.error || "Failed to disable webhook"}`);
184
+ process.exit(1);
185
+ }
186
+ console.log(`\nWebhook ${id} disabled.\n`);
187
+ }
188
+ catch (error) {
189
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
190
+ process.exit(1);
191
+ }
192
+ });
193
+ }
194
+ //# sourceMappingURL=commands.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.js","sourceRoot":"","sources":["../../src/cli/commands.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * @burdenoff/vibe-plugin-notify v1.0.0
3
+ *
4
+ * Generic webhook notifications for AI coding tools.
5
+ * Receives events from native plugins and delivers to configured webhooks.
6
+ *
7
+ * Supported AI Tools:
8
+ * - OpenCode (via opencode-vibe-webhook plugin)
9
+ * - Claude Code (via claudecode-vibe-webhook plugin) [future]
10
+ * - Gemini CLI (via gemini-vibe-webhook plugin) [future]
11
+ *
12
+ * Features:
13
+ * - Webhook CRUD management via CLI and REST API
14
+ * - Supports Slack, Discord, and custom webhooks
15
+ * - Delivery with retries and tracking
16
+ * - Simple CLI: vibe notify add <url>
17
+ *
18
+ * Install: vibe plugin install @burdenoff/vibe-plugin-notify
19
+ */
20
+ import type { VibePlugin } from "./types.js";
21
+ export type { VibePlugin, HostServices, WebhookEndpoint, WebhookDelivery, NotifyEvent, } from "./types.js";
22
+ export declare const vibePlugin: VibePlugin;
23
+ export default vibePlugin;
24
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAIH,OAAO,KAAK,EACV,UAAU,EAIX,MAAM,YAAY,CAAC;AAMpB,YAAY,EACV,UAAU,EACV,YAAY,EACZ,eAAe,EACf,eAAe,EACf,WAAW,GACZ,MAAM,YAAY,CAAC;AAcpB,eAAO,MAAM,UAAU,EAAE,UA2PxB,CAAC;AAEF,eAAe,UAAU,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,237 @@
1
+ /**
2
+ * @burdenoff/vibe-plugin-notify v1.0.0
3
+ *
4
+ * Generic webhook notifications for AI coding tools.
5
+ * Receives events from native plugins and delivers to configured webhooks.
6
+ *
7
+ * Supported AI Tools:
8
+ * - OpenCode (via opencode-vibe-webhook plugin)
9
+ * - Claude Code (via claudecode-vibe-webhook plugin) [future]
10
+ * - Gemini CLI (via gemini-vibe-webhook plugin) [future]
11
+ *
12
+ * Features:
13
+ * - Webhook CRUD management via CLI and REST API
14
+ * - Supports Slack, Discord, and custom webhooks
15
+ * - Delivery with retries and tracking
16
+ * - Simple CLI: vibe notify add <url>
17
+ *
18
+ * Install: vibe plugin install @burdenoff/vibe-plugin-notify
19
+ */
20
+ import { Elysia } from "elysia";
21
+ import { DEFAULT_CONFIG } from "./types.js";
22
+ import { WebhookDeliveryService } from "./services/webhook-delivery.js";
23
+ import { registerCommands } from "./cli/commands.js";
24
+ // ---------------------------------------------------------------------------
25
+ // Module-level references for lifecycle management
26
+ // ---------------------------------------------------------------------------
27
+ let deliveryService = null;
28
+ let hostServicesRef = null;
29
+ const config = { ...DEFAULT_CONFIG };
30
+ // ---------------------------------------------------------------------------
31
+ // Plugin definition
32
+ // ---------------------------------------------------------------------------
33
+ export const vibePlugin = {
34
+ name: "notify",
35
+ version: "1.0.0",
36
+ description: "Webhook notifications for AI coding tools",
37
+ tags: ["backend", "cli", "integration"],
38
+ cliCommand: "notify",
39
+ apiPrefix: "/api/notify",
40
+ publicPaths: ["/health", "/hook"],
41
+ /**
42
+ * Create routes for the plugin
43
+ */
44
+ createRoutes(_deps) {
45
+ return (new Elysia()
46
+ // Health check (public)
47
+ .get("/health", () => {
48
+ return {
49
+ ok: true,
50
+ plugin: "notify",
51
+ version: "1.0.0",
52
+ mode: "webhook-receiver",
53
+ };
54
+ })
55
+ // Hook endpoint - receives events from AI tool plugins (public)
56
+ .post("/hook", async ({ body, set }) => {
57
+ if (!deliveryService) {
58
+ set.status = 503;
59
+ return { error: "Plugin not initialized" };
60
+ }
61
+ const hookData = body;
62
+ // Extract event from payload (standard format from native plugins)
63
+ const eventData = hookData?.event;
64
+ // Determine source (opencode, claudecode, gemini, etc.)
65
+ const source = hookData?.source?.plugin || "unknown";
66
+ hostServicesRef?.logger.info("notify", `[HOOK] Event received from ${source}`, { type: eventData?.type || "unknown" });
67
+ const event = {
68
+ type: eventData?.type || "task.completed",
69
+ properties: {
70
+ sessionId: eventData?.sessionId,
71
+ sessionTitle: eventData?.sessionTitle,
72
+ projectName: eventData?.projectName,
73
+ projectPath: eventData?.projectPath,
74
+ source: source,
75
+ ...(eventData?.properties || {}),
76
+ },
77
+ };
78
+ // Deliver webhooks
79
+ await deliveryService.processEvent(event);
80
+ return { success: true, message: "Event processed" };
81
+ })
82
+ // List all webhooks
83
+ .get("/webhooks", async ({ set }) => {
84
+ if (!deliveryService) {
85
+ set.status = 503;
86
+ return { error: "Plugin not initialized" };
87
+ }
88
+ const manager = deliveryService.getWebhookManager();
89
+ const webhooks = await manager.listWebhooks();
90
+ return { webhooks, count: webhooks.length };
91
+ })
92
+ // Create a new webhook
93
+ .post("/webhooks", async ({ body, set }) => {
94
+ if (!deliveryService) {
95
+ set.status = 503;
96
+ return { error: "Plugin not initialized" };
97
+ }
98
+ const input = body;
99
+ try {
100
+ const manager = deliveryService.getWebhookManager();
101
+ const webhook = await manager.createWebhook({
102
+ url: input.url,
103
+ name: input.name,
104
+ eventTypes: input.eventTypes,
105
+ });
106
+ // Optionally send test webhook
107
+ let testResult = null;
108
+ if (input.test !== false) {
109
+ testResult = await deliveryService.sendTestWebhook(webhook.id);
110
+ }
111
+ return { webhook, test: testResult };
112
+ }
113
+ catch (error) {
114
+ set.status = 400;
115
+ return {
116
+ error: error instanceof Error ? error.message : String(error),
117
+ };
118
+ }
119
+ })
120
+ // Get a specific webhook
121
+ .get("/webhooks/:id", async ({ params, set }) => {
122
+ if (!deliveryService) {
123
+ set.status = 503;
124
+ return { error: "Plugin not initialized" };
125
+ }
126
+ const manager = deliveryService.getWebhookManager();
127
+ const webhook = await manager.getWebhook(params.id);
128
+ if (!webhook) {
129
+ set.status = 404;
130
+ return { error: "Webhook not found" };
131
+ }
132
+ return { webhook };
133
+ })
134
+ // Delete a webhook
135
+ .delete("/webhooks/:id", async ({ params, set }) => {
136
+ if (!deliveryService) {
137
+ set.status = 503;
138
+ return { error: "Plugin not initialized" };
139
+ }
140
+ const manager = deliveryService.getWebhookManager();
141
+ const deleted = await manager.deleteWebhook(params.id);
142
+ if (!deleted) {
143
+ set.status = 404;
144
+ return { error: "Webhook not found" };
145
+ }
146
+ return { success: true, message: "Webhook deleted" };
147
+ })
148
+ // Enable a webhook
149
+ .post("/webhooks/:id/enable", async ({ params, set }) => {
150
+ if (!deliveryService) {
151
+ set.status = 503;
152
+ return { error: "Plugin not initialized" };
153
+ }
154
+ const manager = deliveryService.getWebhookManager();
155
+ const webhook = await manager.enableWebhook(params.id);
156
+ if (!webhook) {
157
+ set.status = 404;
158
+ return { error: "Webhook not found" };
159
+ }
160
+ return { webhook };
161
+ })
162
+ // Disable a webhook
163
+ .post("/webhooks/:id/disable", async ({ params, set }) => {
164
+ if (!deliveryService) {
165
+ set.status = 503;
166
+ return { error: "Plugin not initialized" };
167
+ }
168
+ const manager = deliveryService.getWebhookManager();
169
+ const webhook = await manager.disableWebhook(params.id);
170
+ if (!webhook) {
171
+ set.status = 404;
172
+ return { error: "Webhook not found" };
173
+ }
174
+ return { webhook };
175
+ })
176
+ // Test a webhook
177
+ .post("/webhooks/:id/test", async ({ params, set }) => {
178
+ if (!deliveryService) {
179
+ set.status = 503;
180
+ return { error: "Plugin not initialized" };
181
+ }
182
+ const manager = deliveryService.getWebhookManager();
183
+ const webhook = await manager.getWebhook(params.id);
184
+ if (!webhook) {
185
+ set.status = 404;
186
+ return { error: "Webhook not found" };
187
+ }
188
+ const result = await deliveryService.sendTestWebhook(params.id);
189
+ return {
190
+ webhook: { id: webhook.id, name: webhook.name },
191
+ test: result,
192
+ };
193
+ })
194
+ // List deliveries
195
+ .get("/deliveries", async ({ query, set }) => {
196
+ if (!deliveryService) {
197
+ set.status = 503;
198
+ return { error: "Plugin not initialized" };
199
+ }
200
+ const q = query;
201
+ const deliveries = await deliveryService.getDeliveries({
202
+ webhookId: q.webhookId,
203
+ status: q.status,
204
+ limit: q.limit ? parseInt(q.limit, 10) : 50,
205
+ });
206
+ return { deliveries, count: deliveries.length };
207
+ }));
208
+ },
209
+ /**
210
+ * Initialize services when server starts
211
+ */
212
+ async onServerStart(_app, hostServices) {
213
+ const { storage, logger } = hostServices;
214
+ hostServicesRef = hostServices;
215
+ logger.info("notify", "Initializing plugin...");
216
+ // Initialize delivery service
217
+ deliveryService = new WebhookDeliveryService(storage, logger, config);
218
+ logger.info("notify", "Plugin initialized (webhook receiver mode)");
219
+ logger.info("notify", "Waiting for events from AI tool plugins at /api/notify/hook");
220
+ },
221
+ /**
222
+ * Cleanup when server stops
223
+ */
224
+ async onServerStop() {
225
+ deliveryService = null;
226
+ hostServicesRef = null;
227
+ console.log(" Plugin 'notify' cleaned up");
228
+ },
229
+ /**
230
+ * Register CLI commands
231
+ */
232
+ onCliSetup(program, hostServices) {
233
+ registerCommands(program, hostServices);
234
+ },
235
+ };
236
+ export default vibePlugin;
237
+ //# sourceMappingURL=index.js.map