@ginkoai/cli 1.6.2 → 1.7.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.
Files changed (224) hide show
  1. package/dist/commands/agent/agent-client.d.ts +150 -0
  2. package/dist/commands/agent/agent-client.d.ts.map +1 -0
  3. package/dist/commands/agent/agent-client.js +170 -0
  4. package/dist/commands/agent/agent-client.js.map +1 -0
  5. package/dist/commands/agent/index.d.ts +22 -0
  6. package/dist/commands/agent/index.d.ts.map +1 -0
  7. package/dist/commands/agent/index.js +121 -0
  8. package/dist/commands/agent/index.js.map +1 -0
  9. package/dist/commands/agent/list.d.ts +22 -0
  10. package/dist/commands/agent/list.d.ts.map +1 -0
  11. package/dist/commands/agent/list.js +119 -0
  12. package/dist/commands/agent/list.js.map +1 -0
  13. package/dist/commands/agent/register.d.ts +21 -0
  14. package/dist/commands/agent/register.d.ts.map +1 -0
  15. package/dist/commands/agent/register.js +97 -0
  16. package/dist/commands/agent/register.js.map +1 -0
  17. package/dist/commands/agent/status.d.ts +19 -0
  18. package/dist/commands/agent/status.d.ts.map +1 -0
  19. package/dist/commands/agent/status.js +271 -0
  20. package/dist/commands/agent/status.js.map +1 -0
  21. package/dist/commands/agent/work.d.ts +22 -0
  22. package/dist/commands/agent/work.d.ts.map +1 -0
  23. package/dist/commands/agent/work.js +459 -0
  24. package/dist/commands/agent/work.js.map +1 -0
  25. package/dist/commands/checkpoint/create.d.ts +27 -0
  26. package/dist/commands/checkpoint/create.d.ts.map +1 -0
  27. package/dist/commands/checkpoint/create.js +82 -0
  28. package/dist/commands/checkpoint/create.js.map +1 -0
  29. package/dist/commands/checkpoint/index.d.ts +23 -0
  30. package/dist/commands/checkpoint/index.d.ts.map +1 -0
  31. package/dist/commands/checkpoint/index.js +91 -0
  32. package/dist/commands/checkpoint/index.js.map +1 -0
  33. package/dist/commands/checkpoint/list.d.ts +27 -0
  34. package/dist/commands/checkpoint/list.d.ts.map +1 -0
  35. package/dist/commands/checkpoint/list.js +115 -0
  36. package/dist/commands/checkpoint/list.js.map +1 -0
  37. package/dist/commands/checkpoint/show.d.ts +23 -0
  38. package/dist/commands/checkpoint/show.d.ts.map +1 -0
  39. package/dist/commands/checkpoint/show.js +102 -0
  40. package/dist/commands/checkpoint/show.js.map +1 -0
  41. package/dist/commands/dlq.d.ts +24 -0
  42. package/dist/commands/dlq.d.ts.map +1 -0
  43. package/dist/commands/dlq.js +172 -0
  44. package/dist/commands/dlq.js.map +1 -0
  45. package/dist/commands/escalation/create.d.ts +22 -0
  46. package/dist/commands/escalation/create.d.ts.map +1 -0
  47. package/dist/commands/escalation/create.js +122 -0
  48. package/dist/commands/escalation/create.js.map +1 -0
  49. package/dist/commands/escalation/escalation-client.d.ts +101 -0
  50. package/dist/commands/escalation/escalation-client.d.ts.map +1 -0
  51. package/dist/commands/escalation/escalation-client.js +129 -0
  52. package/dist/commands/escalation/escalation-client.js.map +1 -0
  53. package/dist/commands/escalation/index.d.ts +22 -0
  54. package/dist/commands/escalation/index.d.ts.map +1 -0
  55. package/dist/commands/escalation/index.js +94 -0
  56. package/dist/commands/escalation/index.js.map +1 -0
  57. package/dist/commands/escalation/list.d.ts +24 -0
  58. package/dist/commands/escalation/list.d.ts.map +1 -0
  59. package/dist/commands/escalation/list.js +170 -0
  60. package/dist/commands/escalation/list.js.map +1 -0
  61. package/dist/commands/escalation/resolve.d.ts +20 -0
  62. package/dist/commands/escalation/resolve.d.ts.map +1 -0
  63. package/dist/commands/escalation/resolve.js +102 -0
  64. package/dist/commands/escalation/resolve.js.map +1 -0
  65. package/dist/commands/graph/api-client.d.ts +21 -1
  66. package/dist/commands/graph/api-client.d.ts.map +1 -1
  67. package/dist/commands/graph/api-client.js +23 -0
  68. package/dist/commands/graph/api-client.js.map +1 -1
  69. package/dist/commands/handoff.d.ts.map +1 -1
  70. package/dist/commands/handoff.js +9 -1
  71. package/dist/commands/handoff.js.map +1 -1
  72. package/dist/commands/log.d.ts +3 -0
  73. package/dist/commands/log.d.ts.map +1 -1
  74. package/dist/commands/log.js +73 -14
  75. package/dist/commands/log.js.map +1 -1
  76. package/dist/commands/notifications/history.d.ts +21 -0
  77. package/dist/commands/notifications/history.d.ts.map +1 -0
  78. package/dist/commands/notifications/history.js +160 -0
  79. package/dist/commands/notifications/history.js.map +1 -0
  80. package/dist/commands/notifications/index.d.ts +22 -0
  81. package/dist/commands/notifications/index.d.ts.map +1 -0
  82. package/dist/commands/notifications/index.js +87 -0
  83. package/dist/commands/notifications/index.js.map +1 -0
  84. package/dist/commands/notifications/list.d.ts +19 -0
  85. package/dist/commands/notifications/list.d.ts.map +1 -0
  86. package/dist/commands/notifications/list.js +132 -0
  87. package/dist/commands/notifications/list.js.map +1 -0
  88. package/dist/commands/notifications/test.d.ts +19 -0
  89. package/dist/commands/notifications/test.d.ts.map +1 -0
  90. package/dist/commands/notifications/test.js +217 -0
  91. package/dist/commands/notifications/test.js.map +1 -0
  92. package/dist/commands/orchestrate.d.ts +25 -0
  93. package/dist/commands/orchestrate.d.ts.map +1 -0
  94. package/dist/commands/orchestrate.js +858 -0
  95. package/dist/commands/orchestrate.js.map +1 -0
  96. package/dist/commands/sprint/deps.d.ts +29 -0
  97. package/dist/commands/sprint/deps.d.ts.map +1 -0
  98. package/dist/commands/sprint/deps.js +269 -0
  99. package/dist/commands/sprint/deps.js.map +1 -0
  100. package/dist/commands/sprint/index.d.ts +10 -5
  101. package/dist/commands/sprint/index.d.ts.map +1 -1
  102. package/dist/commands/sprint/index.js +26 -5
  103. package/dist/commands/sprint/index.js.map +1 -1
  104. package/dist/commands/start/index.d.ts.map +1 -1
  105. package/dist/commands/start/index.js +6 -0
  106. package/dist/commands/start/index.js.map +1 -1
  107. package/dist/commands/start/start-reflection.d.ts.map +1 -1
  108. package/dist/commands/start/start-reflection.js +8 -0
  109. package/dist/commands/start/start-reflection.js.map +1 -1
  110. package/dist/commands/verify.d.ts +17 -0
  111. package/dist/commands/verify.d.ts.map +1 -0
  112. package/dist/commands/verify.js +232 -0
  113. package/dist/commands/verify.js.map +1 -0
  114. package/dist/core/session-log-manager.d.ts +1 -1
  115. package/dist/core/session-log-manager.d.ts.map +1 -1
  116. package/dist/index.js +78 -1
  117. package/dist/index.js.map +1 -1
  118. package/dist/lib/__tests__/task-timeout.test.d.ts +12 -0
  119. package/dist/lib/__tests__/task-timeout.test.d.ts.map +1 -0
  120. package/dist/lib/__tests__/task-timeout.test.js +278 -0
  121. package/dist/lib/__tests__/task-timeout.test.js.map +1 -0
  122. package/dist/lib/agent-heartbeat.d.ts +68 -0
  123. package/dist/lib/agent-heartbeat.d.ts.map +1 -0
  124. package/dist/lib/agent-heartbeat.js +117 -0
  125. package/dist/lib/agent-heartbeat.js.map +1 -0
  126. package/dist/lib/checkpoint.d.ts +85 -0
  127. package/dist/lib/checkpoint.d.ts.map +1 -0
  128. package/dist/lib/checkpoint.js +323 -0
  129. package/dist/lib/checkpoint.js.map +1 -0
  130. package/dist/lib/context-metrics.d.ts +230 -0
  131. package/dist/lib/context-metrics.d.ts.map +1 -0
  132. package/dist/lib/context-metrics.js +372 -0
  133. package/dist/lib/context-metrics.js.map +1 -0
  134. package/dist/lib/dead-letter-queue.d.ts +108 -0
  135. package/dist/lib/dead-letter-queue.d.ts.map +1 -0
  136. package/dist/lib/dead-letter-queue.js +378 -0
  137. package/dist/lib/dead-letter-queue.js.map +1 -0
  138. package/dist/lib/event-logger.d.ts +9 -1
  139. package/dist/lib/event-logger.d.ts.map +1 -1
  140. package/dist/lib/event-logger.js +45 -3
  141. package/dist/lib/event-logger.js.map +1 -1
  142. package/dist/lib/event-queue.d.ts.map +1 -1
  143. package/dist/lib/event-queue.js +13 -2
  144. package/dist/lib/event-queue.js.map +1 -1
  145. package/dist/lib/examples/timeout-demo.d.ts +13 -0
  146. package/dist/lib/examples/timeout-demo.d.ts.map +1 -0
  147. package/dist/lib/examples/timeout-demo.js +102 -0
  148. package/dist/lib/examples/timeout-demo.js.map +1 -0
  149. package/dist/lib/examples/timeout-integration-example.d.ts +17 -0
  150. package/dist/lib/examples/timeout-integration-example.d.ts.map +1 -0
  151. package/dist/lib/examples/timeout-integration-example.js +223 -0
  152. package/dist/lib/examples/timeout-integration-example.js.map +1 -0
  153. package/dist/lib/notification-hooks.d.ts +103 -0
  154. package/dist/lib/notification-hooks.d.ts.map +1 -0
  155. package/dist/lib/notification-hooks.js +223 -0
  156. package/dist/lib/notification-hooks.js.map +1 -0
  157. package/dist/lib/notifications/discord.d.ts +20 -0
  158. package/dist/lib/notifications/discord.d.ts.map +1 -0
  159. package/dist/lib/notifications/discord.js +140 -0
  160. package/dist/lib/notifications/discord.js.map +1 -0
  161. package/dist/lib/notifications/index.d.ts +66 -0
  162. package/dist/lib/notifications/index.d.ts.map +1 -0
  163. package/dist/lib/notifications/index.js +120 -0
  164. package/dist/lib/notifications/index.js.map +1 -0
  165. package/dist/lib/notifications/slack.d.ts +20 -0
  166. package/dist/lib/notifications/slack.d.ts.map +1 -0
  167. package/dist/lib/notifications/slack.js +186 -0
  168. package/dist/lib/notifications/slack.js.map +1 -0
  169. package/dist/lib/notifications/teams.d.ts +20 -0
  170. package/dist/lib/notifications/teams.d.ts.map +1 -0
  171. package/dist/lib/notifications/teams.js +146 -0
  172. package/dist/lib/notifications/teams.js.map +1 -0
  173. package/dist/lib/notifications/webhook.d.ts +23 -0
  174. package/dist/lib/notifications/webhook.d.ts.map +1 -0
  175. package/dist/lib/notifications/webhook.js +65 -0
  176. package/dist/lib/notifications/webhook.js.map +1 -0
  177. package/dist/lib/orchestrator-state.d.ts +194 -0
  178. package/dist/lib/orchestrator-state.d.ts.map +1 -0
  179. package/dist/lib/orchestrator-state.js +332 -0
  180. package/dist/lib/orchestrator-state.js.map +1 -0
  181. package/dist/lib/realtime-cursor.d.ts +107 -0
  182. package/dist/lib/realtime-cursor.d.ts.map +1 -0
  183. package/dist/lib/realtime-cursor.js +260 -0
  184. package/dist/lib/realtime-cursor.js.map +1 -0
  185. package/dist/lib/rollback.d.ts +86 -0
  186. package/dist/lib/rollback.d.ts.map +1 -0
  187. package/dist/lib/rollback.js +405 -0
  188. package/dist/lib/rollback.js.map +1 -0
  189. package/dist/lib/sprint-loader.d.ts +39 -2
  190. package/dist/lib/sprint-loader.d.ts.map +1 -1
  191. package/dist/lib/sprint-loader.js +269 -5
  192. package/dist/lib/sprint-loader.js.map +1 -1
  193. package/dist/lib/stale-agent-detector.d.ts +102 -0
  194. package/dist/lib/stale-agent-detector.d.ts.map +1 -0
  195. package/dist/lib/stale-agent-detector.js +156 -0
  196. package/dist/lib/stale-agent-detector.js.map +1 -0
  197. package/dist/lib/task-dependencies.d.ts +143 -0
  198. package/dist/lib/task-dependencies.d.ts.map +1 -0
  199. package/dist/lib/task-dependencies.js +357 -0
  200. package/dist/lib/task-dependencies.js.map +1 -0
  201. package/dist/lib/task-timeout.d.ts +153 -0
  202. package/dist/lib/task-timeout.d.ts.map +1 -0
  203. package/dist/lib/task-timeout.js +505 -0
  204. package/dist/lib/task-timeout.js.map +1 -0
  205. package/dist/lib/verification/build-check.d.ts +55 -0
  206. package/dist/lib/verification/build-check.d.ts.map +1 -0
  207. package/dist/lib/verification/build-check.js +111 -0
  208. package/dist/lib/verification/build-check.js.map +1 -0
  209. package/dist/lib/verification/index.d.ts +19 -0
  210. package/dist/lib/verification/index.d.ts.map +1 -0
  211. package/dist/lib/verification/index.js +17 -0
  212. package/dist/lib/verification/index.js.map +1 -0
  213. package/dist/lib/verification/lint-check.d.ts +34 -0
  214. package/dist/lib/verification/lint-check.d.ts.map +1 -0
  215. package/dist/lib/verification/lint-check.js +215 -0
  216. package/dist/lib/verification/lint-check.js.map +1 -0
  217. package/dist/lib/verification/test-runner.d.ts +50 -0
  218. package/dist/lib/verification/test-runner.d.ts.map +1 -0
  219. package/dist/lib/verification/test-runner.js +225 -0
  220. package/dist/lib/verification/test-runner.js.map +1 -0
  221. package/dist/utils/command-helpers.d.ts.map +1 -1
  222. package/dist/utils/command-helpers.js +7 -0
  223. package/dist/utils/command-helpers.js.map +1 -1
  224. package/package.json +1 -1
@@ -0,0 +1,223 @@
1
+ /**
2
+ * @fileType: utility
3
+ * @status: current
4
+ * @updated: 2025-12-07
5
+ * @tags: [notifications, hooks, epic-004, resilience, alerts]
6
+ * @related: [notifications/index.ts, event-logger.ts, orchestrator-state.ts]
7
+ * @priority: high
8
+ * @complexity: medium
9
+ * @dependencies: [fs-extra, minimatch]
10
+ */
11
+ import fs from 'fs-extra';
12
+ import path from 'path';
13
+ import minimatch from 'minimatch';
14
+ import { getProjectRoot } from '../utils/config-loader.js';
15
+ /**
16
+ * Load notification hooks from ginko.config.json
17
+ *
18
+ * @returns Array of notification hooks
19
+ */
20
+ export async function loadNotificationHooks() {
21
+ try {
22
+ const projectRoot = await getProjectRoot();
23
+ const configPath = path.join(projectRoot, 'ginko.config.json');
24
+ // Check if config file exists
25
+ if (!await fs.pathExists(configPath)) {
26
+ console.log('[NotificationHooks] No ginko.config.json found, no hooks configured');
27
+ return [];
28
+ }
29
+ // Load config
30
+ const config = await fs.readJSON(configPath);
31
+ // Extract notification hooks
32
+ if (!config.notifications?.hooks) {
33
+ console.log('[NotificationHooks] No notification hooks configured');
34
+ return [];
35
+ }
36
+ const hooks = config.notifications.hooks;
37
+ // Validate hooks
38
+ const validHooks = hooks.filter(hook => {
39
+ if (!hook.id || !hook.events || !hook.destination) {
40
+ console.warn(`[NotificationHooks] Invalid hook configuration (missing id, events, or destination)`);
41
+ return false;
42
+ }
43
+ if (!Array.isArray(hook.events) || hook.events.length === 0) {
44
+ console.warn(`[NotificationHooks] Invalid hook ${hook.id}: events must be non-empty array`);
45
+ return false;
46
+ }
47
+ if (!hook.destination.type || !hook.destination.config) {
48
+ console.warn(`[NotificationHooks] Invalid hook ${hook.id}: destination must have type and config`);
49
+ return false;
50
+ }
51
+ return true;
52
+ });
53
+ // Filter enabled hooks (default: true)
54
+ const enabledHooks = validHooks.filter(hook => hook.enabled !== false);
55
+ console.log(`[NotificationHooks] Loaded ${enabledHooks.length} notification hooks`);
56
+ return enabledHooks;
57
+ }
58
+ catch (error) {
59
+ console.error('[NotificationHooks] Failed to load notification hooks:', error instanceof Error ? error.message : String(error));
60
+ return [];
61
+ }
62
+ }
63
+ /**
64
+ * Check if a hook matches the given payload based on filters
65
+ *
66
+ * @param hook - Notification hook to check
67
+ * @param payload - Notification payload to match against
68
+ * @returns True if hook matches filters
69
+ */
70
+ export function matchesFilter(hook, payload) {
71
+ const filter = hook.filter;
72
+ // No filter means match all
73
+ if (!filter) {
74
+ return true;
75
+ }
76
+ // Check severity filter
77
+ if (filter.severity && filter.severity.length > 0) {
78
+ if (!filter.severity.includes(payload.severity)) {
79
+ return false;
80
+ }
81
+ }
82
+ // Check epic ID filter
83
+ if (filter.epicId && payload.epicId) {
84
+ if (filter.epicId !== payload.epicId) {
85
+ return false;
86
+ }
87
+ }
88
+ // Check task pattern filter (glob)
89
+ if (filter.taskPattern && payload.taskId) {
90
+ if (!minimatch(payload.taskId, filter.taskPattern)) {
91
+ return false;
92
+ }
93
+ }
94
+ return true;
95
+ }
96
+ /**
97
+ * Trigger a notification to all matching hooks
98
+ *
99
+ * @param event - Notification event type
100
+ * @param payload - Notification payload
101
+ */
102
+ export async function triggerNotification(event, payload) {
103
+ try {
104
+ // Load hooks
105
+ const hooks = await loadNotificationHooks();
106
+ if (hooks.length === 0) {
107
+ console.log(`[NotificationHooks] No hooks configured for event: ${event}`);
108
+ return;
109
+ }
110
+ // Filter hooks that match this event
111
+ const matchingHooks = hooks.filter(hook => {
112
+ // Check if hook listens for this event type
113
+ if (!hook.events.includes(event)) {
114
+ return false;
115
+ }
116
+ // Check if hook matches filters
117
+ return matchesFilter(hook, payload);
118
+ });
119
+ if (matchingHooks.length === 0) {
120
+ console.log(`[NotificationHooks] No matching hooks for event: ${event}`);
121
+ return;
122
+ }
123
+ console.log(`[NotificationHooks] Triggering ${matchingHooks.length} hooks for event: ${event}`);
124
+ // Dispatch to each matching hook (in parallel, non-blocking)
125
+ const dispatchPromises = matchingHooks.map(async (hook) => {
126
+ try {
127
+ const success = await dispatchToDestination(hook.destination, payload);
128
+ if (success) {
129
+ console.log(`[NotificationHooks] ✓ Notification sent to ${hook.id} (${hook.destination.type})`);
130
+ }
131
+ else {
132
+ console.warn(`[NotificationHooks] ✗ Notification failed for ${hook.id} (${hook.destination.type})`);
133
+ }
134
+ }
135
+ catch (error) {
136
+ // Log error but don't propagate - notification failures shouldn't block workflow
137
+ console.error(`[NotificationHooks] Error dispatching to ${hook.id}:`, error instanceof Error ? error.message : String(error));
138
+ }
139
+ });
140
+ // Wait for all dispatches to complete (but don't block on errors)
141
+ await Promise.allSettled(dispatchPromises);
142
+ }
143
+ catch (error) {
144
+ // Top-level error handling - log but don't propagate
145
+ console.error('[NotificationHooks] Failed to trigger notifications:', error instanceof Error ? error.message : String(error));
146
+ }
147
+ }
148
+ /**
149
+ * Dispatch a notification to a specific destination
150
+ *
151
+ * @param destination - Notification destination config
152
+ * @param payload - Notification payload
153
+ * @returns Promise<boolean> - True if dispatch succeeded
154
+ */
155
+ export async function dispatchToDestination(destination, payload) {
156
+ try {
157
+ // Dynamically import the appropriate adapter
158
+ const { sendNotification } = await import(`./notifications/${destination.type}.js`);
159
+ // Call adapter's sendNotification function
160
+ const result = await sendNotification(destination.config, payload);
161
+ return result;
162
+ }
163
+ catch (error) {
164
+ if (error instanceof Error && error.message.includes('Cannot find module')) {
165
+ console.error(`[NotificationHooks] Unsupported destination type: ${destination.type}`);
166
+ return false;
167
+ }
168
+ console.error(`[NotificationHooks] Dispatch error for ${destination.type}:`, error instanceof Error ? error.message : String(error));
169
+ return false;
170
+ }
171
+ }
172
+ /**
173
+ * Validate notification hook configuration
174
+ *
175
+ * @param hook - Hook configuration to validate
176
+ * @returns Validation result with errors
177
+ */
178
+ export function validateHook(hook) {
179
+ const errors = [];
180
+ if (!hook.id) {
181
+ errors.push('Hook must have an id');
182
+ }
183
+ if (!hook.events || !Array.isArray(hook.events) || hook.events.length === 0) {
184
+ errors.push('Hook must have a non-empty events array');
185
+ }
186
+ if (!hook.destination) {
187
+ errors.push('Hook must have a destination');
188
+ }
189
+ else {
190
+ if (!hook.destination.type) {
191
+ errors.push('Destination must have a type');
192
+ }
193
+ if (!hook.destination.config || typeof hook.destination.config !== 'object') {
194
+ errors.push('Destination must have a config object');
195
+ }
196
+ }
197
+ // Validate event types
198
+ const validEvents = [
199
+ 'escalation',
200
+ 'blocker',
201
+ 'failure',
202
+ 'milestone',
203
+ 'completion',
204
+ 'stale_agent',
205
+ 'human_required'
206
+ ];
207
+ if (hook.events) {
208
+ const invalidEvents = hook.events.filter(e => !validEvents.includes(e));
209
+ if (invalidEvents.length > 0) {
210
+ errors.push(`Invalid event types: ${invalidEvents.join(', ')}`);
211
+ }
212
+ }
213
+ // Validate destination type
214
+ const validDestinations = ['slack', 'discord', 'teams', 'webhook', 'email'];
215
+ if (hook.destination?.type && !validDestinations.includes(hook.destination.type)) {
216
+ errors.push(`Invalid destination type: ${hook.destination.type}. Must be one of: ${validDestinations.join(', ')}`);
217
+ }
218
+ return {
219
+ valid: errors.length === 0,
220
+ errors
221
+ };
222
+ }
223
+ //# sourceMappingURL=notification-hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notification-hooks.js","sourceRoot":"","sources":["../../src/lib/notification-hooks.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * @fileType: utility
3
+ * @status: current
4
+ * @updated: 2025-12-07
5
+ * @tags: [notifications, discord, webhook, epic-004]
6
+ * @related: [index.ts, notification-hooks.ts, slack.ts, teams.ts, webhook.ts]
7
+ * @priority: high
8
+ * @complexity: low
9
+ * @dependencies: []
10
+ */
11
+ import { NotificationPayload } from '../notification-hooks.js';
12
+ /**
13
+ * Send notification to Discord via webhook
14
+ *
15
+ * @param config - Discord configuration (must include webhook_url)
16
+ * @param payload - Notification payload
17
+ * @returns Promise<boolean> - True if notification sent successfully
18
+ */
19
+ export declare function sendNotification(config: Record<string, string>, payload: NotificationPayload): Promise<boolean>;
20
+ //# sourceMappingURL=discord.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discord.d.ts","sourceRoot":"","sources":["../../../src/lib/notifications/discord.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAU/D;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC9B,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,OAAO,CAAC,CAoClB"}
@@ -0,0 +1,140 @@
1
+ /**
2
+ * @fileType: utility
3
+ * @status: current
4
+ * @updated: 2025-12-07
5
+ * @tags: [notifications, discord, webhook, epic-004]
6
+ * @related: [index.ts, notification-hooks.ts, slack.ts, teams.ts, webhook.ts]
7
+ * @priority: high
8
+ * @complexity: low
9
+ * @dependencies: []
10
+ */
11
+ import { retryWithBackoff, truncate } from './index.js';
12
+ /**
13
+ * Send notification to Discord via webhook
14
+ *
15
+ * @param config - Discord configuration (must include webhook_url)
16
+ * @param payload - Notification payload
17
+ * @returns Promise<boolean> - True if notification sent successfully
18
+ */
19
+ export async function sendNotification(config, payload) {
20
+ const discordConfig = config;
21
+ // Validate required config
22
+ if (!discordConfig.webhook_url) {
23
+ console.error('[DiscordNotification] Missing webhook_url in config');
24
+ return false;
25
+ }
26
+ try {
27
+ // Build Discord message
28
+ const message = buildDiscordMessage(payload);
29
+ // Send with retry logic
30
+ await retryWithBackoff(async () => {
31
+ const response = await fetch(discordConfig.webhook_url, {
32
+ method: 'POST',
33
+ headers: {
34
+ 'Content-Type': 'application/json',
35
+ },
36
+ body: JSON.stringify(message),
37
+ signal: AbortSignal.timeout(5000), // 5 second timeout
38
+ });
39
+ if (!response.ok) {
40
+ throw new Error(`Discord webhook returned ${response.status}: ${response.statusText}`);
41
+ }
42
+ return response;
43
+ });
44
+ return true;
45
+ }
46
+ catch (error) {
47
+ console.error('[DiscordNotification] Failed to send notification:', error instanceof Error ? error.message : String(error));
48
+ return false;
49
+ }
50
+ }
51
+ /**
52
+ * Build Discord message with embed
53
+ *
54
+ * @param payload - Notification payload
55
+ * @returns Discord message object
56
+ */
57
+ function buildDiscordMessage(payload) {
58
+ const embed = {
59
+ title: truncate(payload.title, 256), // Discord limit: 256 chars
60
+ description: truncate(payload.description, 4096), // Discord limit: 4096 chars
61
+ color: getSeverityColor(payload.severity),
62
+ fields: [],
63
+ timestamp: payload.timestamp,
64
+ footer: {
65
+ text: `Ginko ${payload.event}`,
66
+ },
67
+ };
68
+ // Add fields
69
+ if (payload.severity) {
70
+ embed.fields.push({
71
+ name: 'Severity',
72
+ value: capitalizeFirst(payload.severity),
73
+ inline: true,
74
+ });
75
+ }
76
+ if (payload.taskId) {
77
+ embed.fields.push({
78
+ name: 'Task',
79
+ value: payload.taskId,
80
+ inline: true,
81
+ });
82
+ }
83
+ if (payload.epicId) {
84
+ embed.fields.push({
85
+ name: 'Epic',
86
+ value: payload.epicId,
87
+ inline: true,
88
+ });
89
+ }
90
+ if (payload.agentId) {
91
+ embed.fields.push({
92
+ name: 'Agent',
93
+ value: payload.agentId,
94
+ inline: true,
95
+ });
96
+ }
97
+ // Add custom metadata fields (limit to 25 fields total - Discord limit)
98
+ if (payload.metadata) {
99
+ for (const [key, value] of Object.entries(payload.metadata)) {
100
+ if (embed.fields.length >= 25)
101
+ break;
102
+ embed.fields.push({
103
+ name: capitalizeFirst(key),
104
+ value: truncate(String(value), 1024), // Discord limit: 1024 chars per field
105
+ inline: true,
106
+ });
107
+ }
108
+ }
109
+ return { embeds: [embed] };
110
+ }
111
+ /**
112
+ * Get color for severity level (Discord uses decimal color codes)
113
+ *
114
+ * @param severity - Severity level
115
+ * @returns Discord color code
116
+ */
117
+ function getSeverityColor(severity) {
118
+ switch (severity) {
119
+ case 'critical':
120
+ return 0xFF0000; // Red
121
+ case 'high':
122
+ return 0xFF8C00; // Orange
123
+ case 'medium':
124
+ return 0xFFD700; // Yellow/Gold
125
+ case 'low':
126
+ return 0x1E90FF; // Blue
127
+ default:
128
+ return 0x808080; // Gray
129
+ }
130
+ }
131
+ /**
132
+ * Capitalize first letter of string
133
+ *
134
+ * @param str - String to capitalize
135
+ * @returns Capitalized string
136
+ */
137
+ function capitalizeFirst(str) {
138
+ return str.charAt(0).toUpperCase() + str.slice(1);
139
+ }
140
+ //# sourceMappingURL=discord.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discord.js","sourceRoot":"","sources":["../../../src/lib/notifications/discord.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * @fileType: utility
3
+ * @status: current
4
+ * @updated: 2025-12-07
5
+ * @tags: [notifications, adapters, epic-004, resilience]
6
+ * @related: [notification-hooks.ts, slack.ts, discord.ts, teams.ts, webhook.ts]
7
+ * @priority: high
8
+ * @complexity: low
9
+ * @dependencies: []
10
+ */
11
+ import { NotificationPayload } from '../notification-hooks.js';
12
+ /**
13
+ * Standard notification adapter interface
14
+ * All adapters must implement this interface
15
+ */
16
+ export interface NotificationAdapter {
17
+ /**
18
+ * Send a notification via this adapter
19
+ *
20
+ * @param config - Adapter-specific configuration
21
+ * @param payload - Notification payload
22
+ * @returns Promise<boolean> - True if notification sent successfully
23
+ */
24
+ sendNotification(config: Record<string, string>, payload: NotificationPayload): Promise<boolean>;
25
+ }
26
+ /**
27
+ * Retry configuration for notification dispatches
28
+ */
29
+ export interface RetryConfig {
30
+ maxAttempts: number;
31
+ initialDelayMs: number;
32
+ maxDelayMs: number;
33
+ backoffMultiplier: number;
34
+ }
35
+ /**
36
+ * Retry helper for notification dispatches
37
+ * Implements exponential backoff with configurable limits
38
+ *
39
+ * @param fn - Async function to retry
40
+ * @param config - Retry configuration
41
+ * @returns Promise with function result
42
+ */
43
+ export declare function retryWithBackoff<T>(fn: () => Promise<T>, config?: Partial<RetryConfig>): Promise<T>;
44
+ /**
45
+ * Format severity with emoji for notifications
46
+ *
47
+ * @param severity - Severity level
48
+ * @returns Formatted severity string with emoji
49
+ */
50
+ export declare function formatSeverity(severity: string): string;
51
+ /**
52
+ * Truncate text to a maximum length
53
+ *
54
+ * @param text - Text to truncate
55
+ * @param maxLength - Maximum length
56
+ * @returns Truncated text
57
+ */
58
+ export declare function truncate(text: string, maxLength: number): string;
59
+ /**
60
+ * Format timestamp for display
61
+ *
62
+ * @param timestamp - ISO timestamp string
63
+ * @returns Formatted timestamp
64
+ */
65
+ export declare function formatTimestamp(timestamp: string): string;
66
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/notifications/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAE/D;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;;;;OAMG;IACH,gBAAgB,CACd,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC9B,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,OAAO,CAAC,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AASD;;;;;;;GAOG;AACH,wBAAsB,gBAAgB,CAAC,CAAC,EACtC,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,MAAM,GAAE,OAAO,CAAC,WAAW,CAAM,GAChC,OAAO,CAAC,CAAC,CAAC,CA2BZ;AA0BD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAavD;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAKhE;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAUzD"}
@@ -0,0 +1,120 @@
1
+ /**
2
+ * @fileType: utility
3
+ * @status: current
4
+ * @updated: 2025-12-07
5
+ * @tags: [notifications, adapters, epic-004, resilience]
6
+ * @related: [notification-hooks.ts, slack.ts, discord.ts, teams.ts, webhook.ts]
7
+ * @priority: high
8
+ * @complexity: low
9
+ * @dependencies: []
10
+ */
11
+ const DEFAULT_RETRY_CONFIG = {
12
+ maxAttempts: 3,
13
+ initialDelayMs: 1000,
14
+ maxDelayMs: 5000,
15
+ backoffMultiplier: 2
16
+ };
17
+ /**
18
+ * Retry helper for notification dispatches
19
+ * Implements exponential backoff with configurable limits
20
+ *
21
+ * @param fn - Async function to retry
22
+ * @param config - Retry configuration
23
+ * @returns Promise with function result
24
+ */
25
+ export async function retryWithBackoff(fn, config = {}) {
26
+ const finalConfig = { ...DEFAULT_RETRY_CONFIG, ...config };
27
+ let lastError = null;
28
+ let delay = finalConfig.initialDelayMs;
29
+ for (let attempt = 1; attempt <= finalConfig.maxAttempts; attempt++) {
30
+ try {
31
+ return await fn();
32
+ }
33
+ catch (error) {
34
+ lastError = error instanceof Error ? error : new Error(String(error));
35
+ // Check if error is retryable (4xx errors are not retryable)
36
+ if (isNonRetryableError(error)) {
37
+ throw lastError;
38
+ }
39
+ if (attempt < finalConfig.maxAttempts) {
40
+ console.log(`[NotificationRetry] Attempt ${attempt}/${finalConfig.maxAttempts} failed, retrying in ${delay}ms...`);
41
+ await new Promise(resolve => setTimeout(resolve, delay));
42
+ // Exponential backoff with max delay cap
43
+ delay = Math.min(delay * finalConfig.backoffMultiplier, finalConfig.maxDelayMs);
44
+ }
45
+ }
46
+ }
47
+ throw lastError || new Error('Retry failed with unknown error');
48
+ }
49
+ /**
50
+ * Check if an error is non-retryable (client errors)
51
+ *
52
+ * @param error - Error to check
53
+ * @returns True if error should not be retried
54
+ */
55
+ function isNonRetryableError(error) {
56
+ // Check for HTTP client errors (4xx)
57
+ if (error.response?.status) {
58
+ const status = error.response.status;
59
+ return status >= 400 && status < 500 && status !== 429; // Retry 429 (rate limit)
60
+ }
61
+ // Check for specific error messages
62
+ if (error instanceof Error) {
63
+ const message = error.message.toLowerCase();
64
+ if (message.includes('invalid') || message.includes('unauthorized') || message.includes('forbidden')) {
65
+ return true;
66
+ }
67
+ }
68
+ return false;
69
+ }
70
+ /**
71
+ * Format severity with emoji for notifications
72
+ *
73
+ * @param severity - Severity level
74
+ * @returns Formatted severity string with emoji
75
+ */
76
+ export function formatSeverity(severity) {
77
+ switch (severity) {
78
+ case 'critical':
79
+ return '🚨 Critical';
80
+ case 'high':
81
+ return 'âš ī¸ High';
82
+ case 'medium':
83
+ return '💡 Medium';
84
+ case 'low':
85
+ return 'â„šī¸ Low';
86
+ default:
87
+ return severity;
88
+ }
89
+ }
90
+ /**
91
+ * Truncate text to a maximum length
92
+ *
93
+ * @param text - Text to truncate
94
+ * @param maxLength - Maximum length
95
+ * @returns Truncated text
96
+ */
97
+ export function truncate(text, maxLength) {
98
+ if (text.length <= maxLength) {
99
+ return text;
100
+ }
101
+ return text.substring(0, maxLength - 3) + '...';
102
+ }
103
+ /**
104
+ * Format timestamp for display
105
+ *
106
+ * @param timestamp - ISO timestamp string
107
+ * @returns Formatted timestamp
108
+ */
109
+ export function formatTimestamp(timestamp) {
110
+ const date = new Date(timestamp);
111
+ return date.toLocaleString('en-US', {
112
+ year: 'numeric',
113
+ month: 'short',
114
+ day: 'numeric',
115
+ hour: '2-digit',
116
+ minute: '2-digit',
117
+ timeZoneName: 'short'
118
+ });
119
+ }
120
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lib/notifications/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * @fileType: utility
3
+ * @status: current
4
+ * @updated: 2025-12-07
5
+ * @tags: [notifications, slack, webhook, epic-004]
6
+ * @related: [index.ts, notification-hooks.ts, discord.ts, teams.ts, webhook.ts]
7
+ * @priority: high
8
+ * @complexity: low
9
+ * @dependencies: []
10
+ */
11
+ import { NotificationPayload } from '../notification-hooks.js';
12
+ /**
13
+ * Send notification to Slack via webhook
14
+ *
15
+ * @param config - Slack configuration (must include webhook_url)
16
+ * @param payload - Notification payload
17
+ * @returns Promise<boolean> - True if notification sent successfully
18
+ */
19
+ export declare function sendNotification(config: Record<string, string>, payload: NotificationPayload): Promise<boolean>;
20
+ //# sourceMappingURL=slack.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slack.d.ts","sourceRoot":"","sources":["../../../src/lib/notifications/slack.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AA6B/D;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC9B,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,OAAO,CAAC,CAoClB"}