@dexto/server 1.2.5
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/LICENSE +44 -0
- package/dist/a2a/adapters/index.cjs +42 -0
- package/dist/a2a/adapters/index.d.ts +10 -0
- package/dist/a2a/adapters/index.d.ts.map +1 -0
- package/dist/a2a/adapters/index.js +12 -0
- package/dist/a2a/adapters/message.cjs +193 -0
- package/dist/a2a/adapters/message.d.ts +50 -0
- package/dist/a2a/adapters/message.d.ts.map +1 -0
- package/dist/a2a/adapters/message.js +167 -0
- package/dist/a2a/adapters/state.cjs +57 -0
- package/dist/a2a/adapters/state.d.ts +36 -0
- package/dist/a2a/adapters/state.d.ts.map +1 -0
- package/dist/a2a/adapters/state.js +32 -0
- package/dist/a2a/adapters/task-view.cjs +85 -0
- package/dist/a2a/adapters/task-view.d.ts +58 -0
- package/dist/a2a/adapters/task-view.d.ts.map +1 -0
- package/dist/a2a/adapters/task-view.js +60 -0
- package/dist/a2a/index.cjs +51 -0
- package/dist/a2a/index.d.ts +15 -0
- package/dist/a2a/index.d.ts.map +1 -0
- package/dist/a2a/index.js +30 -0
- package/dist/a2a/jsonrpc/index.cjs +38 -0
- package/dist/a2a/jsonrpc/index.d.ts +11 -0
- package/dist/a2a/jsonrpc/index.d.ts.map +1 -0
- package/dist/a2a/jsonrpc/index.js +10 -0
- package/dist/a2a/jsonrpc/methods.cjs +183 -0
- package/dist/a2a/jsonrpc/methods.d.ts +110 -0
- package/dist/a2a/jsonrpc/methods.d.ts.map +1 -0
- package/dist/a2a/jsonrpc/methods.js +159 -0
- package/dist/a2a/jsonrpc/server.cjs +199 -0
- package/dist/a2a/jsonrpc/server.d.ts +100 -0
- package/dist/a2a/jsonrpc/server.d.ts.map +1 -0
- package/dist/a2a/jsonrpc/server.js +175 -0
- package/dist/a2a/jsonrpc/types.cjs +47 -0
- package/dist/a2a/jsonrpc/types.d.ts +91 -0
- package/dist/a2a/jsonrpc/types.d.ts.map +1 -0
- package/dist/a2a/jsonrpc/types.js +21 -0
- package/dist/a2a/types.cjs +16 -0
- package/dist/a2a/types.d.ts +250 -0
- package/dist/a2a/types.d.ts.map +1 -0
- package/dist/a2a/types.js +0 -0
- package/dist/approval/approval-coordinator.cjs +87 -0
- package/dist/approval/approval-coordinator.d.ts +52 -0
- package/dist/approval/approval-coordinator.d.ts.map +1 -0
- package/dist/approval/approval-coordinator.js +63 -0
- package/dist/approval/manual-approval-handler.cjs +100 -0
- package/dist/approval/manual-approval-handler.d.ts +32 -0
- package/dist/approval/manual-approval-handler.d.ts.map +1 -0
- package/dist/approval/manual-approval-handler.js +76 -0
- package/dist/events/a2a-sse-subscriber.cjs +271 -0
- package/dist/events/a2a-sse-subscriber.d.ts +94 -0
- package/dist/events/a2a-sse-subscriber.d.ts.map +1 -0
- package/dist/events/a2a-sse-subscriber.js +247 -0
- package/dist/events/types.cjs +16 -0
- package/dist/events/types.d.ts +15 -0
- package/dist/events/types.d.ts.map +1 -0
- package/dist/events/types.js +0 -0
- package/dist/events/webhook-subscriber.cjs +301 -0
- package/dist/events/webhook-subscriber.d.ts +64 -0
- package/dist/events/webhook-subscriber.d.ts.map +1 -0
- package/dist/events/webhook-subscriber.js +269 -0
- package/dist/events/webhook-types.cjs +16 -0
- package/dist/events/webhook-types.d.ts +91 -0
- package/dist/events/webhook-types.d.ts.map +1 -0
- package/dist/events/webhook-types.js +0 -0
- package/dist/hono/__tests__/test-fixtures.cjs +236 -0
- package/dist/hono/__tests__/test-fixtures.d.ts +65 -0
- package/dist/hono/__tests__/test-fixtures.d.ts.map +1 -0
- package/dist/hono/__tests__/test-fixtures.js +197 -0
- package/dist/hono/index.cjs +166 -0
- package/dist/hono/index.d.ts +2783 -0
- package/dist/hono/index.d.ts.map +1 -0
- package/dist/hono/index.js +141 -0
- package/dist/hono/middleware/auth.cjs +75 -0
- package/dist/hono/middleware/auth.d.ts +3 -0
- package/dist/hono/middleware/auth.d.ts.map +1 -0
- package/dist/hono/middleware/auth.js +51 -0
- package/dist/hono/middleware/cors.cjs +57 -0
- package/dist/hono/middleware/cors.d.ts +9 -0
- package/dist/hono/middleware/cors.d.ts.map +1 -0
- package/dist/hono/middleware/cors.js +33 -0
- package/dist/hono/middleware/error.cjs +131 -0
- package/dist/hono/middleware/error.d.ts +5 -0
- package/dist/hono/middleware/error.d.ts.map +1 -0
- package/dist/hono/middleware/error.js +105 -0
- package/dist/hono/middleware/redaction.cjs +45 -0
- package/dist/hono/middleware/redaction.d.ts +4 -0
- package/dist/hono/middleware/redaction.d.ts.map +1 -0
- package/dist/hono/middleware/redaction.js +20 -0
- package/dist/hono/node/index.cjs +139 -0
- package/dist/hono/node/index.d.ts +19 -0
- package/dist/hono/node/index.d.ts.map +1 -0
- package/dist/hono/node/index.js +115 -0
- package/dist/hono/routes/a2a-jsonrpc.cjs +119 -0
- package/dist/hono/routes/a2a-jsonrpc.d.ts +46 -0
- package/dist/hono/routes/a2a-jsonrpc.d.ts.map +1 -0
- package/dist/hono/routes/a2a-jsonrpc.js +95 -0
- package/dist/hono/routes/a2a-tasks.cjs +315 -0
- package/dist/hono/routes/a2a-tasks.d.ts +530 -0
- package/dist/hono/routes/a2a-tasks.d.ts.map +1 -0
- package/dist/hono/routes/a2a-tasks.js +291 -0
- package/dist/hono/routes/a2a.cjs +36 -0
- package/dist/hono/routes/a2a.d.ts +4 -0
- package/dist/hono/routes/a2a.d.ts.map +1 -0
- package/dist/hono/routes/a2a.js +12 -0
- package/dist/hono/routes/agents.cjs +735 -0
- package/dist/hono/routes/agents.d.ts +650 -0
- package/dist/hono/routes/agents.d.ts.map +1 -0
- package/dist/hono/routes/agents.js +711 -0
- package/dist/hono/routes/approvals.cjs +125 -0
- package/dist/hono/routes/approvals.d.ts +89 -0
- package/dist/hono/routes/approvals.d.ts.map +1 -0
- package/dist/hono/routes/approvals.js +101 -0
- package/dist/hono/routes/greeting.cjs +60 -0
- package/dist/hono/routes/greeting.d.ts +19 -0
- package/dist/hono/routes/greeting.d.ts.map +1 -0
- package/dist/hono/routes/greeting.js +36 -0
- package/dist/hono/routes/health.cjs +45 -0
- package/dist/hono/routes/health.d.ts +17 -0
- package/dist/hono/routes/health.d.ts.map +1 -0
- package/dist/hono/routes/health.js +21 -0
- package/dist/hono/routes/llm.cjs +298 -0
- package/dist/hono/routes/llm.d.ts +294 -0
- package/dist/hono/routes/llm.d.ts.map +1 -0
- package/dist/hono/routes/llm.js +287 -0
- package/dist/hono/routes/mcp.cjs +356 -0
- package/dist/hono/routes/mcp.d.ts +246 -0
- package/dist/hono/routes/mcp.d.ts.map +1 -0
- package/dist/hono/routes/mcp.js +332 -0
- package/dist/hono/routes/memory.cjs +192 -0
- package/dist/hono/routes/memory.d.ts +146 -0
- package/dist/hono/routes/memory.d.ts.map +1 -0
- package/dist/hono/routes/memory.js +168 -0
- package/dist/hono/routes/messages.cjs +320 -0
- package/dist/hono/routes/messages.d.ts +163 -0
- package/dist/hono/routes/messages.d.ts.map +1 -0
- package/dist/hono/routes/messages.js +296 -0
- package/dist/hono/routes/prompts.cjs +228 -0
- package/dist/hono/routes/prompts.d.ts +150 -0
- package/dist/hono/routes/prompts.d.ts.map +1 -0
- package/dist/hono/routes/prompts.js +204 -0
- package/dist/hono/routes/resources.cjs +110 -0
- package/dist/hono/routes/resources.d.ts +76 -0
- package/dist/hono/routes/resources.d.ts.map +1 -0
- package/dist/hono/routes/resources.js +86 -0
- package/dist/hono/routes/search.cjs +109 -0
- package/dist/hono/routes/search.d.ts +137 -0
- package/dist/hono/routes/search.d.ts.map +1 -0
- package/dist/hono/routes/search.js +85 -0
- package/dist/hono/routes/sessions.cjs +366 -0
- package/dist/hono/routes/sessions.d.ts +229 -0
- package/dist/hono/routes/sessions.d.ts.map +1 -0
- package/dist/hono/routes/sessions.js +342 -0
- package/dist/hono/routes/webhooks.cjs +228 -0
- package/dist/hono/routes/webhooks.d.ts +127 -0
- package/dist/hono/routes/webhooks.d.ts.map +1 -0
- package/dist/hono/routes/webhooks.js +204 -0
- package/dist/hono/schemas/responses.cjs +276 -0
- package/dist/hono/schemas/responses.d.ts +1418 -0
- package/dist/hono/schemas/responses.d.ts.map +1 -0
- package/dist/hono/schemas/responses.js +227 -0
- package/dist/hono/types.cjs +16 -0
- package/dist/hono/types.d.ts +6 -0
- package/dist/hono/types.d.ts.map +1 -0
- package/dist/hono/types.js +0 -0
- package/dist/index.cjs +38 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/mcp/mcp-handler.cjs +145 -0
- package/dist/mcp/mcp-handler.d.ts +14 -0
- package/dist/mcp/mcp-handler.d.ts.map +1 -0
- package/dist/mcp/mcp-handler.js +118 -0
- package/package.json +59 -0
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import { setMaxListeners } from "events";
|
|
2
|
+
import { logger } from "@dexto/core";
|
|
3
|
+
class A2ASseEventSubscriber {
|
|
4
|
+
connections = /* @__PURE__ */ new Map();
|
|
5
|
+
eventBus;
|
|
6
|
+
globalAbortController;
|
|
7
|
+
/**
|
|
8
|
+
* Subscribe to agent event bus.
|
|
9
|
+
* Sets up global event listeners that broadcast to all SSE connections.
|
|
10
|
+
*
|
|
11
|
+
* @param eventBus Agent event bus to subscribe to
|
|
12
|
+
*/
|
|
13
|
+
subscribe(eventBus) {
|
|
14
|
+
this.globalAbortController?.abort();
|
|
15
|
+
this.globalAbortController = new AbortController();
|
|
16
|
+
const { signal } = this.globalAbortController;
|
|
17
|
+
const MAX_SHARED_SIGNAL_LISTENERS = 20;
|
|
18
|
+
setMaxListeners(MAX_SHARED_SIGNAL_LISTENERS, signal);
|
|
19
|
+
this.eventBus = eventBus;
|
|
20
|
+
eventBus.on(
|
|
21
|
+
"llm:thinking",
|
|
22
|
+
(payload) => {
|
|
23
|
+
this.broadcastToTask(payload.sessionId, "task.thinking", {
|
|
24
|
+
taskId: payload.sessionId
|
|
25
|
+
});
|
|
26
|
+
},
|
|
27
|
+
{ signal }
|
|
28
|
+
);
|
|
29
|
+
eventBus.on(
|
|
30
|
+
"llm:chunk",
|
|
31
|
+
(payload) => {
|
|
32
|
+
this.broadcastToTask(payload.sessionId, "task.chunk", {
|
|
33
|
+
taskId: payload.sessionId,
|
|
34
|
+
type: payload.chunkType,
|
|
35
|
+
content: payload.content,
|
|
36
|
+
isComplete: payload.isComplete
|
|
37
|
+
});
|
|
38
|
+
},
|
|
39
|
+
{ signal }
|
|
40
|
+
);
|
|
41
|
+
eventBus.on(
|
|
42
|
+
"llm:tool-call",
|
|
43
|
+
(payload) => {
|
|
44
|
+
this.broadcastToTask(payload.sessionId, "task.toolCall", {
|
|
45
|
+
taskId: payload.sessionId,
|
|
46
|
+
toolName: payload.toolName,
|
|
47
|
+
args: payload.args,
|
|
48
|
+
callId: payload.callId
|
|
49
|
+
});
|
|
50
|
+
},
|
|
51
|
+
{ signal }
|
|
52
|
+
);
|
|
53
|
+
eventBus.on(
|
|
54
|
+
"llm:tool-result",
|
|
55
|
+
(payload) => {
|
|
56
|
+
const data = {
|
|
57
|
+
taskId: payload.sessionId,
|
|
58
|
+
toolName: payload.toolName,
|
|
59
|
+
callId: payload.callId,
|
|
60
|
+
success: payload.success,
|
|
61
|
+
sanitized: payload.sanitized
|
|
62
|
+
};
|
|
63
|
+
if (payload.rawResult !== void 0) {
|
|
64
|
+
data.rawResult = payload.rawResult;
|
|
65
|
+
}
|
|
66
|
+
this.broadcastToTask(payload.sessionId, "task.toolResult", data);
|
|
67
|
+
},
|
|
68
|
+
{ signal }
|
|
69
|
+
);
|
|
70
|
+
eventBus.on(
|
|
71
|
+
"llm:response",
|
|
72
|
+
(payload) => {
|
|
73
|
+
this.broadcastToTask(payload.sessionId, "task.message", {
|
|
74
|
+
taskId: payload.sessionId,
|
|
75
|
+
message: {
|
|
76
|
+
role: "agent",
|
|
77
|
+
content: [{ type: "text", text: payload.content }],
|
|
78
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
79
|
+
},
|
|
80
|
+
tokenUsage: payload.tokenUsage,
|
|
81
|
+
provider: payload.provider,
|
|
82
|
+
model: payload.model
|
|
83
|
+
});
|
|
84
|
+
},
|
|
85
|
+
{ signal }
|
|
86
|
+
);
|
|
87
|
+
eventBus.on(
|
|
88
|
+
"llm:error",
|
|
89
|
+
(payload) => {
|
|
90
|
+
this.broadcastToTask(payload.sessionId, "task.error", {
|
|
91
|
+
taskId: payload.sessionId,
|
|
92
|
+
error: {
|
|
93
|
+
message: payload.error.message,
|
|
94
|
+
recoverable: payload.recoverable
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
},
|
|
98
|
+
{ signal }
|
|
99
|
+
);
|
|
100
|
+
eventBus.on(
|
|
101
|
+
"session:reset",
|
|
102
|
+
(payload) => {
|
|
103
|
+
this.broadcastToTask(payload.sessionId, "task.reset", {
|
|
104
|
+
taskId: payload.sessionId
|
|
105
|
+
});
|
|
106
|
+
},
|
|
107
|
+
{ signal }
|
|
108
|
+
);
|
|
109
|
+
logger.debug("A2ASseEventSubscriber subscribed to agent events");
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Create a new SSE stream for a specific task.
|
|
113
|
+
*
|
|
114
|
+
* Returns a ReadableStream that emits SSE events for the task.
|
|
115
|
+
*
|
|
116
|
+
* @param taskId Task/Session ID to stream events for
|
|
117
|
+
* @returns ReadableStream for SSE connection
|
|
118
|
+
*/
|
|
119
|
+
createStream(taskId) {
|
|
120
|
+
const connectionId = `${taskId}-${Date.now()}`;
|
|
121
|
+
return new ReadableStream({
|
|
122
|
+
start: (controller) => {
|
|
123
|
+
const connection = {
|
|
124
|
+
taskId,
|
|
125
|
+
controller,
|
|
126
|
+
abortController: new AbortController(),
|
|
127
|
+
connectedAt: Date.now()
|
|
128
|
+
};
|
|
129
|
+
this.connections.set(connectionId, connection);
|
|
130
|
+
logger.debug(`SSE connection opened for task ${taskId}`);
|
|
131
|
+
this.sendSSEEvent(controller, "connected", {
|
|
132
|
+
taskId,
|
|
133
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
134
|
+
});
|
|
135
|
+
const keepaliveInterval = setInterval(() => {
|
|
136
|
+
try {
|
|
137
|
+
this.sendSSEComment(controller, "keepalive");
|
|
138
|
+
} catch (_error) {
|
|
139
|
+
clearInterval(keepaliveInterval);
|
|
140
|
+
}
|
|
141
|
+
}, 3e4);
|
|
142
|
+
connection.abortController.signal.addEventListener("abort", () => {
|
|
143
|
+
clearInterval(keepaliveInterval);
|
|
144
|
+
});
|
|
145
|
+
},
|
|
146
|
+
cancel: () => {
|
|
147
|
+
const connection = this.connections.get(connectionId);
|
|
148
|
+
if (connection) {
|
|
149
|
+
connection.abortController.abort();
|
|
150
|
+
this.connections.delete(connectionId);
|
|
151
|
+
logger.debug(`SSE connection closed for task ${taskId}`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Broadcast an event to a specific task's SSE connections.
|
|
158
|
+
*
|
|
159
|
+
* @param taskId Task ID to broadcast to
|
|
160
|
+
* @param eventName SSE event name
|
|
161
|
+
* @param data Event data
|
|
162
|
+
*/
|
|
163
|
+
broadcastToTask(taskId, eventName, data) {
|
|
164
|
+
let sent = 0;
|
|
165
|
+
for (const [connectionId, connection] of this.connections.entries()) {
|
|
166
|
+
if (connection.taskId === taskId) {
|
|
167
|
+
try {
|
|
168
|
+
this.sendSSEEvent(connection.controller, eventName, data);
|
|
169
|
+
sent++;
|
|
170
|
+
} catch (error) {
|
|
171
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
172
|
+
logger.warn(`Failed to send SSE event to ${connectionId}: ${errorMessage}`);
|
|
173
|
+
connection.abortController.abort();
|
|
174
|
+
this.connections.delete(connectionId);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
if (sent > 0) {
|
|
179
|
+
logger.debug(`Broadcast ${eventName} to ${sent} SSE connection(s) for task ${taskId}`);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Send an SSE event to a specific controller.
|
|
184
|
+
*
|
|
185
|
+
* Format: event: name\ndata: json\n\n
|
|
186
|
+
*
|
|
187
|
+
* @param controller Stream controller
|
|
188
|
+
* @param eventName Event name
|
|
189
|
+
* @param data Event data
|
|
190
|
+
*/
|
|
191
|
+
sendSSEEvent(controller, eventName, data) {
|
|
192
|
+
const event = `event: ${eventName}
|
|
193
|
+
data: ${JSON.stringify(data)}
|
|
194
|
+
|
|
195
|
+
`;
|
|
196
|
+
controller.enqueue(new TextEncoder().encode(event));
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Send an SSE comment (for keepalive).
|
|
200
|
+
*
|
|
201
|
+
* Format: : comment\n
|
|
202
|
+
*
|
|
203
|
+
* @param controller Stream controller
|
|
204
|
+
* @param comment Comment text
|
|
205
|
+
*/
|
|
206
|
+
sendSSEComment(controller, comment) {
|
|
207
|
+
const line = `: ${comment}
|
|
208
|
+
`;
|
|
209
|
+
controller.enqueue(new TextEncoder().encode(line));
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Close all connections and cleanup.
|
|
213
|
+
*/
|
|
214
|
+
cleanup() {
|
|
215
|
+
logger.debug(`Cleaning up ${this.connections.size} SSE connections`);
|
|
216
|
+
for (const [_connectionId, connection] of this.connections.entries()) {
|
|
217
|
+
connection.abortController.abort();
|
|
218
|
+
try {
|
|
219
|
+
connection.controller.close();
|
|
220
|
+
} catch (_error) {
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
this.connections.clear();
|
|
224
|
+
this.globalAbortController?.abort();
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Get active connection count.
|
|
228
|
+
*/
|
|
229
|
+
getConnectionCount() {
|
|
230
|
+
return this.connections.size;
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Get connection count for a specific task.
|
|
234
|
+
*/
|
|
235
|
+
getTaskConnectionCount(taskId) {
|
|
236
|
+
let count = 0;
|
|
237
|
+
for (const connection of this.connections.values()) {
|
|
238
|
+
if (connection.taskId === taskId) {
|
|
239
|
+
count++;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
return count;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
export {
|
|
246
|
+
A2ASseEventSubscriber
|
|
247
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __copyProps = (to, from, except, desc) => {
|
|
7
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
8
|
+
for (let key of __getOwnPropNames(from))
|
|
9
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
10
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
11
|
+
}
|
|
12
|
+
return to;
|
|
13
|
+
};
|
|
14
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
15
|
+
var types_exports = {};
|
|
16
|
+
module.exports = __toCommonJS(types_exports);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { AgentEventBus } from '@dexto/core';
|
|
2
|
+
/**
|
|
3
|
+
* Generic interface for subscribing to core events.
|
|
4
|
+
*/
|
|
5
|
+
export interface EventSubscriber {
|
|
6
|
+
/**
|
|
7
|
+
* Attach event handlers to the given event bus.
|
|
8
|
+
*/
|
|
9
|
+
subscribe(eventBus: AgentEventBus): void;
|
|
10
|
+
/**
|
|
11
|
+
* Clean up event listeners and resources.
|
|
12
|
+
*/
|
|
13
|
+
cleanup?(): void;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/events/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C;;GAEG;AACH,MAAM,WAAW,eAAe;IAC5B;;OAEG;IACH,SAAS,CAAC,QAAQ,EAAE,aAAa,GAAG,IAAI,CAAC;IAEzC;;OAEG;IACH,OAAO,CAAC,IAAI,IAAI,CAAC;CACpB"}
|
|
File without changes
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
var webhook_subscriber_exports = {};
|
|
30
|
+
__export(webhook_subscriber_exports, {
|
|
31
|
+
WebhookEventSubscriber: () => WebhookEventSubscriber
|
|
32
|
+
});
|
|
33
|
+
module.exports = __toCommonJS(webhook_subscriber_exports);
|
|
34
|
+
var import_crypto = __toESM(require("crypto"), 1);
|
|
35
|
+
var import_events = require("events");
|
|
36
|
+
var import_core = require("@dexto/core");
|
|
37
|
+
var import_core2 = require("@dexto/core");
|
|
38
|
+
const DEFAULT_DELIVERY_OPTIONS = {
|
|
39
|
+
maxRetries: 3,
|
|
40
|
+
timeout: 1e4,
|
|
41
|
+
// 10 seconds
|
|
42
|
+
includeSignature: true
|
|
43
|
+
};
|
|
44
|
+
class WebhookEventSubscriber {
|
|
45
|
+
webhooks = /* @__PURE__ */ new Map();
|
|
46
|
+
abortController;
|
|
47
|
+
deliveryOptions;
|
|
48
|
+
fetchFn;
|
|
49
|
+
constructor({
|
|
50
|
+
fetchFn,
|
|
51
|
+
...deliveryOptions
|
|
52
|
+
} = {}) {
|
|
53
|
+
this.deliveryOptions = { ...DEFAULT_DELIVERY_OPTIONS, ...deliveryOptions };
|
|
54
|
+
this.fetchFn = fetchFn || fetch;
|
|
55
|
+
import_core2.logger.debug("WebhookEventSubscriber initialized");
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Subscribe to agent events and deliver them to registered webhooks
|
|
59
|
+
*/
|
|
60
|
+
subscribe(eventBus) {
|
|
61
|
+
this.abortController?.abort();
|
|
62
|
+
this.abortController = new AbortController();
|
|
63
|
+
const { signal } = this.abortController;
|
|
64
|
+
const MAX_SHARED_SIGNAL_LISTENERS = 20;
|
|
65
|
+
(0, import_events.setMaxListeners)(MAX_SHARED_SIGNAL_LISTENERS, signal);
|
|
66
|
+
import_core.INTEGRATION_EVENTS.forEach((eventName) => {
|
|
67
|
+
eventBus.on(
|
|
68
|
+
eventName,
|
|
69
|
+
(payload) => {
|
|
70
|
+
this.deliverEvent(eventName, payload);
|
|
71
|
+
},
|
|
72
|
+
{ signal }
|
|
73
|
+
);
|
|
74
|
+
});
|
|
75
|
+
import_core2.logger.info(`Webhook subscriber active with ${this.webhooks.size} registered webhooks`);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Register a new webhook endpoint
|
|
79
|
+
*/
|
|
80
|
+
addWebhook(webhook) {
|
|
81
|
+
this.webhooks.set(webhook.id, webhook);
|
|
82
|
+
import_core2.logger.info(`Webhook registered: ${webhook.id} -> ${webhook.url}`);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Remove a webhook endpoint
|
|
86
|
+
*/
|
|
87
|
+
removeWebhook(webhookId) {
|
|
88
|
+
const removed = this.webhooks.delete(webhookId);
|
|
89
|
+
if (removed) {
|
|
90
|
+
import_core2.logger.info(`Webhook removed: ${webhookId}`);
|
|
91
|
+
} else {
|
|
92
|
+
import_core2.logger.warn(`Attempted to remove non-existent webhook: ${webhookId}`);
|
|
93
|
+
}
|
|
94
|
+
return removed;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Get all registered webhooks
|
|
98
|
+
*/
|
|
99
|
+
getWebhooks() {
|
|
100
|
+
return Array.from(this.webhooks.values());
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Get a specific webhook by ID
|
|
104
|
+
*/
|
|
105
|
+
getWebhook(webhookId) {
|
|
106
|
+
return this.webhooks.get(webhookId);
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Test a webhook by sending a sample event
|
|
110
|
+
*/
|
|
111
|
+
async testWebhook(webhookId) {
|
|
112
|
+
const webhook = this.webhooks.get(webhookId);
|
|
113
|
+
if (!webhook) {
|
|
114
|
+
throw new Error(`Webhook not found: ${webhookId}`);
|
|
115
|
+
}
|
|
116
|
+
const testEvent = {
|
|
117
|
+
id: `evt_test_${Date.now()}`,
|
|
118
|
+
type: "tools:available-updated",
|
|
119
|
+
data: {
|
|
120
|
+
tools: ["test-tool"],
|
|
121
|
+
source: "mcp"
|
|
122
|
+
},
|
|
123
|
+
created: (/* @__PURE__ */ new Date()).toISOString(),
|
|
124
|
+
apiVersion: "2025-07-03"
|
|
125
|
+
};
|
|
126
|
+
return this.deliverToWebhook(webhook, testEvent);
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Clean up event listeners and resources
|
|
130
|
+
*/
|
|
131
|
+
cleanup() {
|
|
132
|
+
if (this.abortController) {
|
|
133
|
+
this.abortController.abort();
|
|
134
|
+
delete this.abortController;
|
|
135
|
+
}
|
|
136
|
+
this.webhooks.clear();
|
|
137
|
+
import_core2.logger.debug("Webhook event subscriber cleaned up");
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Unsubscribe from current event bus without clearing registered webhooks
|
|
141
|
+
*/
|
|
142
|
+
unsubscribe() {
|
|
143
|
+
if (this.abortController) {
|
|
144
|
+
const controller = this.abortController;
|
|
145
|
+
delete this.abortController;
|
|
146
|
+
try {
|
|
147
|
+
controller.abort();
|
|
148
|
+
} catch (error) {
|
|
149
|
+
import_core2.logger.debug(
|
|
150
|
+
`Error aborting controller during unsubscribe: ${error instanceof Error ? error.message : String(error)}`,
|
|
151
|
+
{
|
|
152
|
+
location: "WebhookEventSubscriber.unsubscribe",
|
|
153
|
+
...error instanceof Error ? { stack: error.stack } : { value: String(error) }
|
|
154
|
+
}
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Deliver an event to all registered webhooks
|
|
161
|
+
*/
|
|
162
|
+
async deliverEvent(eventType, eventData) {
|
|
163
|
+
if (this.webhooks.size === 0) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
const webhookEvent = {
|
|
167
|
+
id: `evt_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`,
|
|
168
|
+
type: eventType,
|
|
169
|
+
data: eventData,
|
|
170
|
+
created: (/* @__PURE__ */ new Date()).toISOString(),
|
|
171
|
+
apiVersion: "2025-07-03"
|
|
172
|
+
};
|
|
173
|
+
import_core2.logger.debug(`Delivering webhook event: ${eventType} to ${this.webhooks.size} webhooks`);
|
|
174
|
+
const deliveryPromises = Array.from(this.webhooks.values()).map((webhook) => ({
|
|
175
|
+
webhook,
|
|
176
|
+
promise: this.deliverToWebhook(webhook, webhookEvent)
|
|
177
|
+
}));
|
|
178
|
+
const handleSettled = (results) => {
|
|
179
|
+
results.forEach((result, i) => {
|
|
180
|
+
if (result.status === "rejected") {
|
|
181
|
+
const webhook = deliveryPromises[i]?.webhook;
|
|
182
|
+
if (webhook) {
|
|
183
|
+
import_core2.logger.error(
|
|
184
|
+
`Webhook delivery failed for ${webhook.id}: ${result.reason instanceof Error ? result.reason.message : String(result.reason)}`
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
};
|
|
190
|
+
if (process.env.NODE_ENV === "test") {
|
|
191
|
+
const results = await Promise.allSettled(deliveryPromises.map((p) => p.promise));
|
|
192
|
+
handleSettled(results);
|
|
193
|
+
} else {
|
|
194
|
+
Promise.allSettled(deliveryPromises.map((p) => p.promise)).then(handleSettled);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Deliver an event to a specific webhook with retry logic
|
|
199
|
+
*/
|
|
200
|
+
async deliverToWebhook(webhook, event) {
|
|
201
|
+
const startTime = Date.now();
|
|
202
|
+
let lastError;
|
|
203
|
+
let lastStatusCode;
|
|
204
|
+
for (let attempt = 1; attempt <= this.deliveryOptions.maxRetries; attempt++) {
|
|
205
|
+
try {
|
|
206
|
+
const result2 = await this.sendWebhookRequest(webhook, event, attempt);
|
|
207
|
+
if (result2.success) {
|
|
208
|
+
return result2;
|
|
209
|
+
}
|
|
210
|
+
lastError = new Error(result2.error || `HTTP ${result2.statusCode}`);
|
|
211
|
+
lastStatusCode = result2.statusCode;
|
|
212
|
+
} catch (error) {
|
|
213
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
214
|
+
import_core2.logger.warn(
|
|
215
|
+
`Webhook delivery attempt ${attempt}/${this.deliveryOptions.maxRetries} failed for ${webhook.id}: ${lastError.message}`
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
if (attempt < this.deliveryOptions.maxRetries) {
|
|
219
|
+
const baseDelay = process.env.NODE_ENV === "test" ? 1 : 1e3;
|
|
220
|
+
const exp = baseDelay * Math.pow(2, attempt - 1);
|
|
221
|
+
const jitter = exp * 0.2 * Math.random();
|
|
222
|
+
const backoffMs = Math.min(exp + jitter, 1e4);
|
|
223
|
+
await new Promise((resolve) => setTimeout(resolve, backoffMs));
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
const totalTime = Date.now() - startTime;
|
|
227
|
+
const result = {
|
|
228
|
+
success: false,
|
|
229
|
+
error: lastError?.message || "Unknown error",
|
|
230
|
+
responseTime: totalTime,
|
|
231
|
+
attempt: this.deliveryOptions.maxRetries,
|
|
232
|
+
...lastStatusCode !== void 0 && { statusCode: lastStatusCode }
|
|
233
|
+
};
|
|
234
|
+
import_core2.logger.error(
|
|
235
|
+
`Webhook delivery failed after ${this.deliveryOptions.maxRetries} attempts for ${webhook.id}: ${result.error}`
|
|
236
|
+
);
|
|
237
|
+
return result;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Send HTTP request to webhook endpoint
|
|
241
|
+
*/
|
|
242
|
+
async sendWebhookRequest(webhook, event, attempt) {
|
|
243
|
+
const startTime = Date.now();
|
|
244
|
+
const payload = JSON.stringify(event);
|
|
245
|
+
const headers = {
|
|
246
|
+
"Content-Type": "application/json",
|
|
247
|
+
"User-Agent": "DextoAgent/1.0",
|
|
248
|
+
"X-Dexto-Event-Type": event.type,
|
|
249
|
+
"X-Dexto-Event-Id": event.id,
|
|
250
|
+
"X-Dexto-Delivery-Attempt": attempt.toString()
|
|
251
|
+
};
|
|
252
|
+
if (webhook.secret && this.deliveryOptions.includeSignature) {
|
|
253
|
+
const signature = this.generateSignature(payload, webhook.secret);
|
|
254
|
+
headers["X-Dexto-Signature-256"] = signature;
|
|
255
|
+
}
|
|
256
|
+
try {
|
|
257
|
+
const response = await this.fetchFn(webhook.url, {
|
|
258
|
+
method: "POST",
|
|
259
|
+
headers,
|
|
260
|
+
body: payload,
|
|
261
|
+
signal: AbortSignal.timeout(this.deliveryOptions.timeout)
|
|
262
|
+
});
|
|
263
|
+
const responseTime = Date.now() - startTime;
|
|
264
|
+
const success = response.ok;
|
|
265
|
+
const result = {
|
|
266
|
+
success,
|
|
267
|
+
statusCode: response.status,
|
|
268
|
+
responseTime,
|
|
269
|
+
attempt
|
|
270
|
+
};
|
|
271
|
+
if (!success) {
|
|
272
|
+
result.error = `HTTP ${response.status}: ${response.statusText}`;
|
|
273
|
+
}
|
|
274
|
+
import_core2.logger.debug(
|
|
275
|
+
`Webhook delivery ${success ? "succeeded" : "failed"} for ${webhook.id}: ${response.status} in ${responseTime}ms`
|
|
276
|
+
);
|
|
277
|
+
return result;
|
|
278
|
+
} catch (error) {
|
|
279
|
+
const responseTime = Date.now() - startTime;
|
|
280
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
281
|
+
return {
|
|
282
|
+
success: false,
|
|
283
|
+
error: errorMessage,
|
|
284
|
+
responseTime,
|
|
285
|
+
attempt
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Generate HMAC signature for webhook verification
|
|
291
|
+
*/
|
|
292
|
+
generateSignature(payload, secret) {
|
|
293
|
+
const hmac = import_crypto.default.createHmac("sha256", secret);
|
|
294
|
+
hmac.update(payload, "utf8");
|
|
295
|
+
return `sha256=${hmac.digest("hex")}`;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
299
|
+
0 && (module.exports = {
|
|
300
|
+
WebhookEventSubscriber
|
|
301
|
+
});
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { AgentEventBus } from '@dexto/core';
|
|
2
|
+
import { EventSubscriber } from './types.js';
|
|
3
|
+
import { type WebhookConfig, type WebhookDeliveryResult, type WebhookDeliveryOptions } from './webhook-types.js';
|
|
4
|
+
/**
|
|
5
|
+
* Webhook event subscriber that delivers agent events via HTTP POST
|
|
6
|
+
*/
|
|
7
|
+
export declare class WebhookEventSubscriber implements EventSubscriber {
|
|
8
|
+
private webhooks;
|
|
9
|
+
private abortController?;
|
|
10
|
+
private deliveryOptions;
|
|
11
|
+
private fetchFn;
|
|
12
|
+
constructor({ fetchFn, ...deliveryOptions }?: WebhookDeliveryOptions & {
|
|
13
|
+
fetchFn?: typeof globalThis.fetch;
|
|
14
|
+
});
|
|
15
|
+
/**
|
|
16
|
+
* Subscribe to agent events and deliver them to registered webhooks
|
|
17
|
+
*/
|
|
18
|
+
subscribe(eventBus: AgentEventBus): void;
|
|
19
|
+
/**
|
|
20
|
+
* Register a new webhook endpoint
|
|
21
|
+
*/
|
|
22
|
+
addWebhook(webhook: WebhookConfig): void;
|
|
23
|
+
/**
|
|
24
|
+
* Remove a webhook endpoint
|
|
25
|
+
*/
|
|
26
|
+
removeWebhook(webhookId: string): boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Get all registered webhooks
|
|
29
|
+
*/
|
|
30
|
+
getWebhooks(): WebhookConfig[];
|
|
31
|
+
/**
|
|
32
|
+
* Get a specific webhook by ID
|
|
33
|
+
*/
|
|
34
|
+
getWebhook(webhookId: string): WebhookConfig | undefined;
|
|
35
|
+
/**
|
|
36
|
+
* Test a webhook by sending a sample event
|
|
37
|
+
*/
|
|
38
|
+
testWebhook(webhookId: string): Promise<WebhookDeliveryResult>;
|
|
39
|
+
/**
|
|
40
|
+
* Clean up event listeners and resources
|
|
41
|
+
*/
|
|
42
|
+
cleanup(): void;
|
|
43
|
+
/**
|
|
44
|
+
* Unsubscribe from current event bus without clearing registered webhooks
|
|
45
|
+
*/
|
|
46
|
+
unsubscribe(): void;
|
|
47
|
+
/**
|
|
48
|
+
* Deliver an event to all registered webhooks
|
|
49
|
+
*/
|
|
50
|
+
private deliverEvent;
|
|
51
|
+
/**
|
|
52
|
+
* Deliver an event to a specific webhook with retry logic
|
|
53
|
+
*/
|
|
54
|
+
private deliverToWebhook;
|
|
55
|
+
/**
|
|
56
|
+
* Send HTTP request to webhook endpoint
|
|
57
|
+
*/
|
|
58
|
+
private sendWebhookRequest;
|
|
59
|
+
/**
|
|
60
|
+
* Generate HMAC signature for webhook verification
|
|
61
|
+
*/
|
|
62
|
+
private generateSignature;
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=webhook-subscriber.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhook-subscriber.d.ts","sourceRoot":"","sources":["../../src/events/webhook-subscriber.ts"],"names":[],"mappings":"AAEA,OAAO,EACH,aAAa,EAIhB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EACH,KAAK,aAAa,EAElB,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,EAC9B,MAAM,oBAAoB,CAAC;AAW5B;;GAEG;AACH,qBAAa,sBAAuB,YAAW,eAAe;IAC1D,OAAO,CAAC,QAAQ,CAAyC;IACzD,OAAO,CAAC,eAAe,CAAC,CAAkB;IAC1C,OAAO,CAAC,eAAe,CAAmC;IAC1D,OAAO,CAAC,OAAO,CAA0B;gBAE7B,EACR,OAAO,EACP,GAAG,eAAe,EACrB,GAAE,sBAAsB,GAAG;QAAE,OAAO,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAA;KAAO;IAOtE;;OAEG;IACH,SAAS,CAAC,QAAQ,EAAE,aAAa,GAAG,IAAI;IA4BxC;;OAEG;IACH,UAAU,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI;IAKxC;;OAEG;IACH,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAUzC;;OAEG;IACH,WAAW,IAAI,aAAa,EAAE;IAI9B;;OAEG;IACH,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAIxD;;OAEG;IACG,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAoBpE;;OAEG;IACH,OAAO,IAAI,IAAI;IAUf;;OAEG;IACH,WAAW,IAAI,IAAI;IAsBnB;;OAEG;YACW,YAAY;IA+C1B;;OAEG;YACW,gBAAgB;IAoD9B;;OAEG;YACW,kBAAkB;IA+DhC;;OAEG;IACH,OAAO,CAAC,iBAAiB;CAK5B"}
|