@axiom-lattice/gateway 2.1.88 → 2.1.90
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/.turbo/turbo-build.log +14 -12
- package/AGENTS.md +62 -0
- package/CHANGELOG.md +22 -0
- package/dist/a2a-ERG5RMUW.mjs +567 -0
- package/dist/a2a-ERG5RMUW.mjs.map +1 -0
- package/dist/index.d.mts +62 -0
- package/dist/index.d.ts +62 -0
- package/dist/index.js +879 -29
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +294 -16
- package/dist/index.mjs.map +1 -1
- package/jest.config.js +1 -1
- package/package.json +6 -6
- package/src/__tests__/a2a.test.ts +346 -0
- package/src/index.ts +19 -0
- package/src/router/MessageRouter.ts +169 -17
- package/src/routes/a2a-bridge.ts +266 -0
- package/src/routes/a2a.ts +779 -0
- package/src/routes/index.ts +5 -0
|
@@ -0,0 +1,567 @@
|
|
|
1
|
+
// src/routes/a2a.ts
|
|
2
|
+
import { v4 } from "uuid";
|
|
3
|
+
import {
|
|
4
|
+
agentInstanceManager
|
|
5
|
+
} from "@axiom-lattice/core";
|
|
6
|
+
import { MessageChunkTypes } from "@axiom-lattice/protocols";
|
|
7
|
+
var taskStore = /* @__PURE__ */ new Map();
|
|
8
|
+
var _a2aKeyStore = null;
|
|
9
|
+
var _storeKeyMap = /* @__PURE__ */ new Map();
|
|
10
|
+
function setA2AKeyStore(store) {
|
|
11
|
+
_a2aKeyStore = store;
|
|
12
|
+
}
|
|
13
|
+
async function refreshStoreKeyMap() {
|
|
14
|
+
if (!_a2aKeyStore) return;
|
|
15
|
+
_storeKeyMap = await _a2aKeyStore.loadIntoMap();
|
|
16
|
+
}
|
|
17
|
+
function parseEnvKeyMap() {
|
|
18
|
+
const keysEnv = process.env.A2A_API_KEYS || "";
|
|
19
|
+
const defaultTenantId = process.env.A2A_DEFAULT_TENANT_ID || "a2a-default-tenant";
|
|
20
|
+
const defaultProjectId = process.env.A2A_DEFAULT_PROJECT_ID || "";
|
|
21
|
+
const defaultWorkspaceId = process.env.A2A_DEFAULT_WORKSPACE_ID || "";
|
|
22
|
+
const map = /* @__PURE__ */ new Map();
|
|
23
|
+
if (keysEnv) {
|
|
24
|
+
keysEnv.split(",").forEach((entry) => {
|
|
25
|
+
const trimmed = entry.trim();
|
|
26
|
+
if (!trimmed) return;
|
|
27
|
+
const parts = trimmed.split(":");
|
|
28
|
+
const key = parts[0];
|
|
29
|
+
if (!key) return;
|
|
30
|
+
map.set(key, {
|
|
31
|
+
key,
|
|
32
|
+
tenantId: parts[1]?.trim() || defaultTenantId,
|
|
33
|
+
projectId: parts[2]?.trim() || defaultProjectId || void 0,
|
|
34
|
+
workspaceId: parts[3]?.trim() || defaultWorkspaceId || void 0
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
return map;
|
|
39
|
+
}
|
|
40
|
+
function getA2AConfig() {
|
|
41
|
+
const envMap = parseEnvKeyMap();
|
|
42
|
+
const apiKeyMap = new Map([..._storeKeyMap, ...envMap]);
|
|
43
|
+
return {
|
|
44
|
+
agentName: process.env.A2A_AGENT_NAME || "Axiom Lattice Agent",
|
|
45
|
+
agentDescription: process.env.A2A_AGENT_DESCRIPTION || "AI agent powered by Axiom Lattice framework",
|
|
46
|
+
organization: process.env.A2A_ORGANIZATION || "Axiom Lattice",
|
|
47
|
+
organizationUrl: process.env.A2A_ORGANIZATION_URL || "https://axiom-lattice.ai",
|
|
48
|
+
version: process.env.A2A_VERSION || "1.0.0",
|
|
49
|
+
defaultAssistantId: process.env.A2A_DEFAULT_AGENT_ID || "",
|
|
50
|
+
defaultTenantId: process.env.A2A_DEFAULT_TENANT_ID || "a2a-default-tenant",
|
|
51
|
+
defaultProjectId: process.env.A2A_DEFAULT_PROJECT_ID || "",
|
|
52
|
+
defaultWorkspaceId: process.env.A2A_DEFAULT_WORKSPACE_ID || "",
|
|
53
|
+
apiKeyMap
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function authenticateA2A(request) {
|
|
57
|
+
const config = getA2AConfig();
|
|
58
|
+
const noAuthRequired = config.apiKeyMap.size === 0;
|
|
59
|
+
const token = request.headers.authorization?.startsWith("Bearer ") ? request.headers.authorization.substring(7) : request.headers["x-api-key"];
|
|
60
|
+
if (!token) {
|
|
61
|
+
return {
|
|
62
|
+
authenticated: noAuthRequired,
|
|
63
|
+
tenantId: config.defaultTenantId,
|
|
64
|
+
projectId: config.defaultProjectId || void 0,
|
|
65
|
+
workspaceId: config.defaultWorkspaceId || void 0
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
const entry = config.apiKeyMap.get(token);
|
|
69
|
+
if (entry) {
|
|
70
|
+
return {
|
|
71
|
+
authenticated: true,
|
|
72
|
+
apiKey: token,
|
|
73
|
+
tenantId: entry.tenantId,
|
|
74
|
+
projectId: entry.projectId,
|
|
75
|
+
workspaceId: entry.workspaceId,
|
|
76
|
+
source: request.headers.authorization?.startsWith("Bearer ") ? "bearer" : "x-api-key"
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
return { authenticated: false };
|
|
80
|
+
}
|
|
81
|
+
function requireAuth(request, reply) {
|
|
82
|
+
const auth = authenticateA2A(request);
|
|
83
|
+
if (!auth.authenticated) {
|
|
84
|
+
reply.status(401).send({
|
|
85
|
+
error: "Unauthorized",
|
|
86
|
+
message: "Valid API key required via Bearer token or X-API-Key header"
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
return auth;
|
|
90
|
+
}
|
|
91
|
+
function buildAgentCard(baseUrl) {
|
|
92
|
+
const config = getA2AConfig();
|
|
93
|
+
return {
|
|
94
|
+
name: config.agentName,
|
|
95
|
+
description: config.agentDescription,
|
|
96
|
+
url: `${baseUrl}/api/a2a`,
|
|
97
|
+
provider: {
|
|
98
|
+
organization: config.organization,
|
|
99
|
+
url: config.organizationUrl
|
|
100
|
+
},
|
|
101
|
+
version: config.version,
|
|
102
|
+
capabilities: {
|
|
103
|
+
streaming: true,
|
|
104
|
+
pushNotifications: false,
|
|
105
|
+
stateTransitionHistory: false
|
|
106
|
+
},
|
|
107
|
+
defaultInputModes: ["text", "text/plain"],
|
|
108
|
+
defaultOutputModes: ["text", "text/plain", "text/markdown"],
|
|
109
|
+
skills: []
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
function extractTextFromParts(parts) {
|
|
113
|
+
return parts.filter((p) => p.type === "text").map((p) => p.text).join("\n");
|
|
114
|
+
}
|
|
115
|
+
function buildTextPart(text) {
|
|
116
|
+
return { type: "text", text };
|
|
117
|
+
}
|
|
118
|
+
function isoNow() {
|
|
119
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
120
|
+
}
|
|
121
|
+
async function streamChunksAsA2A(stream, taskId, sendEvent, onInterrupt) {
|
|
122
|
+
let accumulatedText = "";
|
|
123
|
+
for await (const chunk of stream) {
|
|
124
|
+
const chunkText = chunk.data?.content || "";
|
|
125
|
+
const chunkType = chunk.type;
|
|
126
|
+
if (chunkType === MessageChunkTypes.INTERRUPT) {
|
|
127
|
+
sendEvent("status-update", {
|
|
128
|
+
id: taskId,
|
|
129
|
+
status: {
|
|
130
|
+
state: "input-required",
|
|
131
|
+
message: {
|
|
132
|
+
role: "agent",
|
|
133
|
+
parts: [buildTextPart(accumulatedText)]
|
|
134
|
+
},
|
|
135
|
+
timestamp: isoNow()
|
|
136
|
+
},
|
|
137
|
+
final: false
|
|
138
|
+
});
|
|
139
|
+
onInterrupt?.();
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
if (chunkType === MessageChunkTypes.AI || chunkType === MessageChunkTypes.TOOL) {
|
|
143
|
+
if (chunkText) {
|
|
144
|
+
accumulatedText += chunkText;
|
|
145
|
+
sendEvent("status-update", {
|
|
146
|
+
id: taskId,
|
|
147
|
+
status: {
|
|
148
|
+
state: "working",
|
|
149
|
+
message: {
|
|
150
|
+
role: "agent",
|
|
151
|
+
parts: [buildTextPart(chunkText)]
|
|
152
|
+
},
|
|
153
|
+
timestamp: isoNow()
|
|
154
|
+
},
|
|
155
|
+
final: false
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return accumulatedText;
|
|
161
|
+
}
|
|
162
|
+
async function getTaskStatus(taskId) {
|
|
163
|
+
const record = taskStore.get(taskId);
|
|
164
|
+
if (!record) return null;
|
|
165
|
+
return {
|
|
166
|
+
id: record.id,
|
|
167
|
+
status: {
|
|
168
|
+
state: record.status,
|
|
169
|
+
timestamp: record.updatedAt
|
|
170
|
+
},
|
|
171
|
+
artifacts: [],
|
|
172
|
+
history: record.history
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
async function handleAgentCard(request, reply) {
|
|
176
|
+
const protocol = request.headers["x-forwarded-proto"] || request.protocol;
|
|
177
|
+
const host = request.hostname;
|
|
178
|
+
const baseUrl = `${protocol}://${host}`;
|
|
179
|
+
const card = buildAgentCard(baseUrl);
|
|
180
|
+
reply.status(200).send(card);
|
|
181
|
+
}
|
|
182
|
+
async function handleTasksGet(request, reply) {
|
|
183
|
+
const auth = requireAuth(request, reply);
|
|
184
|
+
if (!auth.authenticated) return;
|
|
185
|
+
const task = await getTaskStatus(request.params.taskId);
|
|
186
|
+
if (!task) {
|
|
187
|
+
reply.status(404).send({ error: "Task not found" });
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
reply.status(200).send(task);
|
|
191
|
+
}
|
|
192
|
+
async function handleTasksSend(request, reply) {
|
|
193
|
+
const auth = requireAuth(request, reply);
|
|
194
|
+
if (!auth.authenticated) return;
|
|
195
|
+
const config = getA2AConfig();
|
|
196
|
+
const body = request.body;
|
|
197
|
+
if (!body.message || !body.message.parts || body.message.parts.length === 0) {
|
|
198
|
+
reply.status(400).send({ error: "message.parts is required" });
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
const text = extractTextFromParts(body.message.parts);
|
|
202
|
+
if (!text.trim()) {
|
|
203
|
+
reply.status(400).send({ error: "message must contain text content" });
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
const assistantId = request.headers["x-a2a-agent-id"] || config.defaultAssistantId;
|
|
207
|
+
if (!assistantId) {
|
|
208
|
+
reply.status(500).send({
|
|
209
|
+
error: "No agent configured",
|
|
210
|
+
message: "Set A2A_DEFAULT_AGENT_ID env var or provide x-a2a-agent-id header"
|
|
211
|
+
});
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
const taskId = body.id || v4();
|
|
215
|
+
const threadId = v4();
|
|
216
|
+
const tenantId = auth.tenantId || config.defaultTenantId;
|
|
217
|
+
const now = isoNow();
|
|
218
|
+
const record = {
|
|
219
|
+
id: taskId,
|
|
220
|
+
threadId,
|
|
221
|
+
assistantId,
|
|
222
|
+
tenantId,
|
|
223
|
+
projectId: auth.projectId || void 0,
|
|
224
|
+
workspaceId: auth.workspaceId || void 0,
|
|
225
|
+
status: "working",
|
|
226
|
+
createdAt: now,
|
|
227
|
+
updatedAt: now,
|
|
228
|
+
history: [
|
|
229
|
+
{
|
|
230
|
+
role: "user",
|
|
231
|
+
parts: body.message.parts
|
|
232
|
+
}
|
|
233
|
+
]
|
|
234
|
+
};
|
|
235
|
+
taskStore.set(taskId, record);
|
|
236
|
+
let agent;
|
|
237
|
+
try {
|
|
238
|
+
agent = agentInstanceManager.getAgent({
|
|
239
|
+
assistant_id: assistantId,
|
|
240
|
+
thread_id: threadId,
|
|
241
|
+
tenant_id: tenantId,
|
|
242
|
+
workspace_id: auth.workspaceId || void 0,
|
|
243
|
+
project_id: auth.projectId || void 0
|
|
244
|
+
});
|
|
245
|
+
} catch (err) {
|
|
246
|
+
record.status = "failed";
|
|
247
|
+
record.updatedAt = isoNow();
|
|
248
|
+
reply.status(500).send({
|
|
249
|
+
id: taskId,
|
|
250
|
+
error: "Failed to initialize agent",
|
|
251
|
+
message: err instanceof Error ? err.message : String(err)
|
|
252
|
+
});
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
reply.hijack();
|
|
256
|
+
reply.raw.writeHead(200, {
|
|
257
|
+
"Content-Type": "text/event-stream",
|
|
258
|
+
"Cache-Control": "no-cache",
|
|
259
|
+
Connection: "keep-alive",
|
|
260
|
+
"Access-Control-Allow-Origin": "*"
|
|
261
|
+
});
|
|
262
|
+
const sendEvent = (event, data) => {
|
|
263
|
+
reply.raw.write(`event: ${event}
|
|
264
|
+
data: ${JSON.stringify(data)}
|
|
265
|
+
|
|
266
|
+
`);
|
|
267
|
+
};
|
|
268
|
+
try {
|
|
269
|
+
const result = await agent.addMessage({
|
|
270
|
+
input: { message: text }
|
|
271
|
+
});
|
|
272
|
+
record.messageId = result.messageId;
|
|
273
|
+
const stream = agent.chunkStream(result.messageId, [
|
|
274
|
+
MessageChunkTypes.MESSAGE_COMPLETED
|
|
275
|
+
]);
|
|
276
|
+
const accumulatedText = await streamChunksAsA2A(stream, taskId, sendEvent, () => {
|
|
277
|
+
record.status = "input-required";
|
|
278
|
+
record.updatedAt = isoNow();
|
|
279
|
+
});
|
|
280
|
+
record.status = "completed";
|
|
281
|
+
record.updatedAt = isoNow();
|
|
282
|
+
record.history.push({
|
|
283
|
+
role: "agent",
|
|
284
|
+
parts: [buildTextPart(accumulatedText)]
|
|
285
|
+
});
|
|
286
|
+
sendEvent("status-update", {
|
|
287
|
+
id: taskId,
|
|
288
|
+
status: {
|
|
289
|
+
state: "completed",
|
|
290
|
+
timestamp: isoNow()
|
|
291
|
+
},
|
|
292
|
+
final: true
|
|
293
|
+
});
|
|
294
|
+
} catch (err) {
|
|
295
|
+
record.status = "failed";
|
|
296
|
+
record.updatedAt = isoNow();
|
|
297
|
+
sendEvent("error", {
|
|
298
|
+
code: "EXECUTION_ERROR",
|
|
299
|
+
message: err instanceof Error ? err.message : String(err)
|
|
300
|
+
});
|
|
301
|
+
sendEvent("status-update", {
|
|
302
|
+
id: taskId,
|
|
303
|
+
status: {
|
|
304
|
+
state: "failed",
|
|
305
|
+
timestamp: isoNow()
|
|
306
|
+
},
|
|
307
|
+
final: true
|
|
308
|
+
});
|
|
309
|
+
} finally {
|
|
310
|
+
reply.raw.end();
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
async function handleTasksCancel(request, reply) {
|
|
314
|
+
const auth = requireAuth(request, reply);
|
|
315
|
+
if (!auth.authenticated) return;
|
|
316
|
+
const { taskId } = request.params;
|
|
317
|
+
const record = taskStore.get(taskId);
|
|
318
|
+
if (!record) {
|
|
319
|
+
reply.status(404).send({ error: "Task not found" });
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
if (record.status === "completed" || record.status === "failed" || record.status === "canceled") {
|
|
323
|
+
reply.status(409).send({
|
|
324
|
+
error: "Task is already in a terminal state",
|
|
325
|
+
task: { id: record.id, status: record.status }
|
|
326
|
+
});
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
try {
|
|
330
|
+
const agent = agentInstanceManager.getAgent({
|
|
331
|
+
assistant_id: record.assistantId,
|
|
332
|
+
thread_id: record.threadId,
|
|
333
|
+
tenant_id: record.tenantId,
|
|
334
|
+
workspace_id: record.workspaceId,
|
|
335
|
+
project_id: record.projectId
|
|
336
|
+
});
|
|
337
|
+
await agent.abort();
|
|
338
|
+
} catch (err) {
|
|
339
|
+
console.warn({
|
|
340
|
+
event: "a2a:cancel:no_agent",
|
|
341
|
+
taskId,
|
|
342
|
+
threadId: record.threadId,
|
|
343
|
+
error: err instanceof Error ? err.message : String(err)
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
record.status = "canceled";
|
|
347
|
+
record.updatedAt = isoNow();
|
|
348
|
+
reply.status(200).send({
|
|
349
|
+
id: taskId,
|
|
350
|
+
status: {
|
|
351
|
+
state: "canceled",
|
|
352
|
+
timestamp: record.updatedAt
|
|
353
|
+
}
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
async function handleTasksStream(request, reply) {
|
|
357
|
+
const auth = requireAuth(request, reply);
|
|
358
|
+
if (!auth.authenticated) return;
|
|
359
|
+
const { taskId } = request.params;
|
|
360
|
+
const record = taskStore.get(taskId);
|
|
361
|
+
if (!record) {
|
|
362
|
+
reply.status(404).send({ error: "Task not found" });
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
let agent;
|
|
366
|
+
try {
|
|
367
|
+
agent = agentInstanceManager.getAgent({
|
|
368
|
+
assistant_id: record.assistantId,
|
|
369
|
+
thread_id: record.threadId,
|
|
370
|
+
tenant_id: record.tenantId,
|
|
371
|
+
workspace_id: record.workspaceId,
|
|
372
|
+
project_id: record.projectId
|
|
373
|
+
});
|
|
374
|
+
} catch {
|
|
375
|
+
reply.status(404).send({
|
|
376
|
+
error: "Agent session not found. The task may have already completed."
|
|
377
|
+
});
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
reply.hijack();
|
|
381
|
+
reply.raw.writeHead(200, {
|
|
382
|
+
"Content-Type": "text/event-stream",
|
|
383
|
+
"Cache-Control": "no-cache",
|
|
384
|
+
Connection: "keep-alive",
|
|
385
|
+
"Access-Control-Allow-Origin": "*"
|
|
386
|
+
});
|
|
387
|
+
const sendEvent = (event, data) => {
|
|
388
|
+
reply.raw.write(`event: ${event}
|
|
389
|
+
data: ${JSON.stringify(data)}
|
|
390
|
+
|
|
391
|
+
`);
|
|
392
|
+
};
|
|
393
|
+
if (!record.messageId) {
|
|
394
|
+
sendEvent("status-update", {
|
|
395
|
+
id: taskId,
|
|
396
|
+
status: {
|
|
397
|
+
state: record.status,
|
|
398
|
+
timestamp: record.updatedAt
|
|
399
|
+
},
|
|
400
|
+
final: true
|
|
401
|
+
});
|
|
402
|
+
reply.raw.end();
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
try {
|
|
406
|
+
const stream = agent.chunkStream(record.messageId, [
|
|
407
|
+
MessageChunkTypes.MESSAGE_COMPLETED
|
|
408
|
+
]);
|
|
409
|
+
await streamChunksAsA2A(stream, taskId, sendEvent);
|
|
410
|
+
sendEvent("status-update", {
|
|
411
|
+
id: taskId,
|
|
412
|
+
status: {
|
|
413
|
+
state: record.status,
|
|
414
|
+
timestamp: record.updatedAt
|
|
415
|
+
},
|
|
416
|
+
final: true
|
|
417
|
+
});
|
|
418
|
+
} catch (err) {
|
|
419
|
+
sendEvent("error", {
|
|
420
|
+
code: "STREAM_ERROR",
|
|
421
|
+
message: err instanceof Error ? err.message : String(err)
|
|
422
|
+
});
|
|
423
|
+
} finally {
|
|
424
|
+
reply.raw.end();
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
async function handleApiKeyList(request, reply) {
|
|
428
|
+
const auth = requireAuth(request, reply);
|
|
429
|
+
if (!auth.authenticated) return;
|
|
430
|
+
if (!_a2aKeyStore) {
|
|
431
|
+
reply.status(501).send({ error: "Key store not configured" });
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
const records = await _a2aKeyStore.list({
|
|
435
|
+
tenantId: request.query.tenantId,
|
|
436
|
+
limit: request.query.limit ? parseInt(request.query.limit, 10) : void 0,
|
|
437
|
+
offset: request.query.offset ? parseInt(request.query.offset, 10) : void 0
|
|
438
|
+
});
|
|
439
|
+
reply.status(200).send({
|
|
440
|
+
success: true,
|
|
441
|
+
data: {
|
|
442
|
+
records: records.map((r) => ({ ...r, key: r.key.slice(0, 8) + "..." })),
|
|
443
|
+
total: records.length
|
|
444
|
+
}
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
async function handleApiKeyCreate(request, reply) {
|
|
448
|
+
const auth = requireAuth(request, reply);
|
|
449
|
+
if (!auth.authenticated) return;
|
|
450
|
+
if (!_a2aKeyStore) {
|
|
451
|
+
reply.status(501).send({ error: "Key store not configured" });
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
const body = request.body;
|
|
455
|
+
const tenantId = body.tenantId || request.headers["x-tenant-id"] || auth.tenantId;
|
|
456
|
+
const projectId = body.projectId || request.headers["x-project-id"] || auth.projectId || "default";
|
|
457
|
+
const workspaceId = body.workspaceId || request.headers["x-workspace-id"] || auth.workspaceId;
|
|
458
|
+
if (!tenantId) {
|
|
459
|
+
reply.status(400).send({
|
|
460
|
+
error: "tenantId is required",
|
|
461
|
+
message: "Provide via body.tenantId, x-tenant-id header, or authenticate with a scoped API key"
|
|
462
|
+
});
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
const record = await _a2aKeyStore.create({
|
|
466
|
+
tenantId,
|
|
467
|
+
projectId,
|
|
468
|
+
workspaceId,
|
|
469
|
+
label: body.label
|
|
470
|
+
});
|
|
471
|
+
await refreshStoreKeyMap();
|
|
472
|
+
reply.status(201).send({ success: true, data: record });
|
|
473
|
+
}
|
|
474
|
+
async function handleApiKeyDelete(request, reply) {
|
|
475
|
+
const auth = requireAuth(request, reply);
|
|
476
|
+
if (!auth.authenticated) return;
|
|
477
|
+
if (!_a2aKeyStore) {
|
|
478
|
+
reply.status(501).send({ error: "Key store not configured" });
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
await _a2aKeyStore.delete(request.params.id);
|
|
482
|
+
await refreshStoreKeyMap();
|
|
483
|
+
reply.status(200).send({ success: true });
|
|
484
|
+
}
|
|
485
|
+
async function handleApiKeyDisable(request, reply) {
|
|
486
|
+
const auth = requireAuth(request, reply);
|
|
487
|
+
if (!auth.authenticated) return;
|
|
488
|
+
if (!_a2aKeyStore) {
|
|
489
|
+
reply.status(501).send({ error: "Key store not configured" });
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
await _a2aKeyStore.disable(request.params.id);
|
|
493
|
+
await refreshStoreKeyMap();
|
|
494
|
+
reply.status(200).send({ success: true });
|
|
495
|
+
}
|
|
496
|
+
async function handleApiKeyEnable(request, reply) {
|
|
497
|
+
const auth = requireAuth(request, reply);
|
|
498
|
+
if (!auth.authenticated) return;
|
|
499
|
+
if (!_a2aKeyStore) {
|
|
500
|
+
reply.status(501).send({ error: "Key store not configured" });
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
await _a2aKeyStore.enable(request.params.id);
|
|
504
|
+
await refreshStoreKeyMap();
|
|
505
|
+
reply.status(200).send({ success: true });
|
|
506
|
+
}
|
|
507
|
+
async function handleApiKeyRotate(request, reply) {
|
|
508
|
+
const auth = requireAuth(request, reply);
|
|
509
|
+
if (!auth.authenticated) return;
|
|
510
|
+
if (!_a2aKeyStore) {
|
|
511
|
+
reply.status(501).send({ error: "Key store not configured" });
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
const record = await _a2aKeyStore.rotate(request.params.id);
|
|
515
|
+
await refreshStoreKeyMap();
|
|
516
|
+
reply.status(200).send({ success: true, data: record });
|
|
517
|
+
}
|
|
518
|
+
function registerA2ARoutes(app) {
|
|
519
|
+
app.get("/api/a2a/.well-known/agent.json", handleAgentCard);
|
|
520
|
+
app.get("/api/a2a/.well-known/agent-card.json", handleAgentCard);
|
|
521
|
+
app.post(
|
|
522
|
+
"/api/a2a/tasks/send",
|
|
523
|
+
handleTasksSend
|
|
524
|
+
);
|
|
525
|
+
app.get(
|
|
526
|
+
"/api/a2a/tasks/:taskId",
|
|
527
|
+
handleTasksGet
|
|
528
|
+
);
|
|
529
|
+
app.get(
|
|
530
|
+
"/api/a2a/tasks/:taskId/stream",
|
|
531
|
+
handleTasksStream
|
|
532
|
+
);
|
|
533
|
+
app.post(
|
|
534
|
+
"/api/a2a/tasks/:taskId/cancel",
|
|
535
|
+
handleTasksCancel
|
|
536
|
+
);
|
|
537
|
+
app.get(
|
|
538
|
+
"/api/a2a/keys",
|
|
539
|
+
handleApiKeyList
|
|
540
|
+
);
|
|
541
|
+
app.post(
|
|
542
|
+
"/api/a2a/keys",
|
|
543
|
+
handleApiKeyCreate
|
|
544
|
+
);
|
|
545
|
+
app.delete(
|
|
546
|
+
"/api/a2a/keys/:id",
|
|
547
|
+
handleApiKeyDelete
|
|
548
|
+
);
|
|
549
|
+
app.post(
|
|
550
|
+
"/api/a2a/keys/:id/disable",
|
|
551
|
+
handleApiKeyDisable
|
|
552
|
+
);
|
|
553
|
+
app.post(
|
|
554
|
+
"/api/a2a/keys/:id/enable",
|
|
555
|
+
handleApiKeyEnable
|
|
556
|
+
);
|
|
557
|
+
app.post(
|
|
558
|
+
"/api/a2a/keys/:id/rotate",
|
|
559
|
+
handleApiKeyRotate
|
|
560
|
+
);
|
|
561
|
+
}
|
|
562
|
+
export {
|
|
563
|
+
refreshStoreKeyMap,
|
|
564
|
+
registerA2ARoutes,
|
|
565
|
+
setA2AKeyStore
|
|
566
|
+
};
|
|
567
|
+
//# sourceMappingURL=a2a-ERG5RMUW.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/routes/a2a.ts"],"sourcesContent":["import type { FastifyInstance, FastifyRequest, FastifyReply } from \"fastify\";\nimport { v4 } from \"uuid\";\nimport {\n agentInstanceManager,\n} from \"@axiom-lattice/core\";\nimport { MessageChunkTypes } from \"@axiom-lattice/protocols\";\nimport type {\n AgentCard,\n A2ATask,\n A2ATaskState,\n A2ATaskSendRequest,\n A2AMessage,\n A2APart,\n A2AAuthContext,\n A2AApiKeyStore,\n A2AApiKeyRecord,\n CreateA2AApiKeyInput,\n A2AApiKeyEntry,\n} from \"@axiom-lattice/protocols\";\n\n// ─── Task Tracking ────────────────────────────────────────────────────────\n\ninterface TaskRecord {\n id: string;\n threadId: string;\n messageId?: string;\n assistantId: string;\n tenantId: string;\n projectId?: string;\n workspaceId?: string;\n status: A2ATaskState;\n createdAt: string;\n updatedAt: string;\n history: A2AMessage[];\n}\n\nconst taskStore = new Map<string, TaskRecord>();\n\n// ─── Key Store ────────────────────────────────────────────────────────────\n\nlet _a2aKeyStore: A2AApiKeyStore | null = null;\nlet _storeKeyMap: Map<string, A2AApiKeyEntry> = new Map();\n\nexport function setA2AKeyStore(store: A2AApiKeyStore): void {\n _a2aKeyStore = store;\n}\n\n/**\n * Reload the cached key map from the store.\n * Call after any CRUD mutation or at startup.\n */\nexport async function refreshStoreKeyMap(): Promise<void> {\n if (!_a2aKeyStore) return;\n _storeKeyMap = await _a2aKeyStore.loadIntoMap();\n}\n\n// ─── Config ───────────────────────────────────────────────────────────────\n\nfunction parseEnvKeyMap(): Map<string, A2AApiKeyEntry> {\n const keysEnv = process.env.A2A_API_KEYS || \"\";\n const defaultTenantId = process.env.A2A_DEFAULT_TENANT_ID || \"a2a-default-tenant\";\n const defaultProjectId = process.env.A2A_DEFAULT_PROJECT_ID || \"\";\n const defaultWorkspaceId = process.env.A2A_DEFAULT_WORKSPACE_ID || \"\";\n const map = new Map<string, A2AApiKeyEntry>();\n\n if (keysEnv) {\n keysEnv.split(\",\").forEach((entry) => {\n const trimmed = entry.trim();\n if (!trimmed) return;\n const parts = trimmed.split(\":\");\n const key = parts[0];\n if (!key) return;\n map.set(key, {\n key,\n tenantId: parts[1]?.trim() || defaultTenantId,\n projectId: parts[2]?.trim() || defaultProjectId || undefined,\n workspaceId: parts[3]?.trim() || defaultWorkspaceId || undefined,\n });\n });\n }\n\n return map;\n}\n\nfunction getA2AConfig() {\n const envMap = parseEnvKeyMap();\n // Store keys take priority; env keys serve as fallback\n const apiKeyMap = new Map([..._storeKeyMap, ...envMap]);\n\n return {\n agentName: process.env.A2A_AGENT_NAME || \"Axiom Lattice Agent\",\n agentDescription:\n process.env.A2A_AGENT_DESCRIPTION ||\n \"AI agent powered by Axiom Lattice framework\",\n organization: process.env.A2A_ORGANIZATION || \"Axiom Lattice\",\n organizationUrl: process.env.A2A_ORGANIZATION_URL || \"https://axiom-lattice.ai\",\n version: process.env.A2A_VERSION || \"1.0.0\",\n defaultAssistantId: process.env.A2A_DEFAULT_AGENT_ID || \"\",\n defaultTenantId: process.env.A2A_DEFAULT_TENANT_ID || \"a2a-default-tenant\",\n defaultProjectId: process.env.A2A_DEFAULT_PROJECT_ID || \"\",\n defaultWorkspaceId: process.env.A2A_DEFAULT_WORKSPACE_ID || \"\",\n apiKeyMap,\n };\n}\n\n// ─── Auth ─────────────────────────────────────────────────────────────────\n\nfunction authenticateA2A(\n request: FastifyRequest,\n): A2AAuthContext {\n const config = getA2AConfig();\n const noAuthRequired = config.apiKeyMap.size === 0;\n\n const token =\n request.headers.authorization?.startsWith(\"Bearer \")\n ? request.headers.authorization.substring(7)\n : (request.headers[\"x-api-key\"] as string | undefined);\n\n if (!token) {\n return {\n authenticated: noAuthRequired,\n tenantId: config.defaultTenantId,\n projectId: config.defaultProjectId || undefined,\n workspaceId: config.defaultWorkspaceId || undefined,\n };\n }\n\n const entry = config.apiKeyMap.get(token);\n if (entry) {\n return {\n authenticated: true,\n apiKey: token,\n tenantId: entry.tenantId,\n projectId: entry.projectId,\n workspaceId: entry.workspaceId,\n source: request.headers.authorization?.startsWith(\"Bearer \") ? \"bearer\" : \"x-api-key\",\n };\n }\n\n return { authenticated: false };\n}\n\nfunction requireAuth(\n request: FastifyRequest,\n reply: FastifyReply,\n): A2AAuthContext {\n const auth = authenticateA2A(request);\n if (!auth.authenticated) {\n reply.status(401).send({\n error: \"Unauthorized\",\n message: \"Valid API key required via Bearer token or X-API-Key header\",\n });\n }\n return auth;\n}\n\n// ─── Agent Card ───────────────────────────────────────────────────────────\n\nfunction buildAgentCard(baseUrl: string): AgentCard {\n const config = getA2AConfig();\n return {\n name: config.agentName,\n description: config.agentDescription,\n url: `${baseUrl}/api/a2a`,\n provider: {\n organization: config.organization,\n url: config.organizationUrl,\n },\n version: config.version,\n capabilities: {\n streaming: true,\n pushNotifications: false,\n stateTransitionHistory: false,\n },\n defaultInputModes: [\"text\", \"text/plain\"],\n defaultOutputModes: [\"text\", \"text/plain\", \"text/markdown\"],\n skills: [],\n };\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────\n\nfunction extractTextFromParts(parts: A2APart[]): string {\n return parts\n .filter((p): p is { type: \"text\"; text: string } => p.type === \"text\")\n .map((p) => p.text)\n .join(\"\\n\");\n}\n\nfunction buildTextPart(text: string): A2APart {\n return { type: \"text\", text };\n}\n\nfunction isoNow(): string {\n return new Date().toISOString();\n}\n\ntype SendEventFn = (event: string, data: Record<string, unknown>) => void;\n\nasync function streamChunksAsA2A(\n stream: AsyncIterable<{ type: unknown; data?: { content?: string } }>,\n taskId: string,\n sendEvent: SendEventFn,\n onInterrupt?: () => void,\n): Promise<string> {\n let accumulatedText = \"\";\n\n for await (const chunk of stream) {\n const chunkText = (chunk as { data?: { content?: string } }).data?.content || \"\";\n const chunkType = (chunk as { type: string }).type;\n\n if (chunkType === MessageChunkTypes.INTERRUPT) {\n sendEvent(\"status-update\", {\n id: taskId,\n status: {\n state: \"input-required\",\n message: {\n role: \"agent\",\n parts: [buildTextPart(accumulatedText)],\n },\n timestamp: isoNow(),\n },\n final: false,\n });\n onInterrupt?.();\n continue;\n }\n\n if (\n chunkType === MessageChunkTypes.AI ||\n chunkType === MessageChunkTypes.TOOL\n ) {\n if (chunkText) {\n accumulatedText += chunkText;\n sendEvent(\"status-update\", {\n id: taskId,\n status: {\n state: \"working\",\n message: {\n role: \"agent\",\n parts: [buildTextPart(chunkText)],\n },\n timestamp: isoNow(),\n },\n final: false,\n });\n }\n }\n }\n\n return accumulatedText;\n}\n\n// ─── Task Status ──────────────────────────────────────────────────────────\n\nasync function getTaskStatus(taskId: string): Promise<A2ATask | null> {\n const record = taskStore.get(taskId);\n if (!record) return null;\n\n return {\n id: record.id,\n status: {\n state: record.status,\n timestamp: record.updatedAt,\n },\n artifacts: [],\n history: record.history,\n };\n}\n\n// ─── Handlers ─────────────────────────────────────────────────────────────\n\nasync function handleAgentCard(\n request: FastifyRequest,\n reply: FastifyReply,\n): Promise<void> {\n const protocol =\n (request.headers[\"x-forwarded-proto\"] as string) ||\n request.protocol;\n const host = request.hostname;\n const baseUrl = `${protocol}://${host}`;\n const card = buildAgentCard(baseUrl);\n reply.status(200).send(card);\n}\n\nasync function handleTasksGet(\n request: FastifyRequest<{ Params: { taskId: string } }>,\n reply: FastifyReply,\n): Promise<void> {\n const auth = requireAuth(request, reply);\n if (!auth.authenticated) return;\n const task = await getTaskStatus(request.params.taskId);\n if (!task) {\n reply.status(404).send({ error: \"Task not found\" });\n return;\n }\n reply.status(200).send(task);\n}\n\nasync function handleTasksSend(\n request: FastifyRequest<{ Body: A2ATaskSendRequest }>,\n reply: FastifyReply,\n): Promise<void> {\n const auth = requireAuth(request, reply);\n if (!auth.authenticated) return;\n\n const config = getA2AConfig();\n const body = request.body as A2ATaskSendRequest;\n\n if (!body.message || !body.message.parts || body.message.parts.length === 0) {\n reply.status(400).send({ error: \"message.parts is required\" });\n return;\n }\n\n const text = extractTextFromParts(body.message.parts);\n if (!text.trim()) {\n reply.status(400).send({ error: \"message must contain text content\" });\n return;\n }\n\n const assistantId =\n (request.headers[\"x-a2a-agent-id\"] as string) ||\n config.defaultAssistantId;\n\n if (!assistantId) {\n reply.status(500).send({\n error: \"No agent configured\",\n message:\n \"Set A2A_DEFAULT_AGENT_ID env var or provide x-a2a-agent-id header\",\n });\n return;\n }\n\n const taskId = body.id || v4();\n const threadId = v4();\n\n const tenantId = auth.tenantId || config.defaultTenantId;\n\n const now = isoNow();\n\n const record: TaskRecord = {\n id: taskId,\n threadId,\n assistantId,\n tenantId,\n projectId: auth.projectId || undefined,\n workspaceId: auth.workspaceId || undefined,\n status: \"working\",\n createdAt: now,\n updatedAt: now,\n history: [\n {\n role: \"user\",\n parts: body.message.parts,\n },\n ],\n };\n\n taskStore.set(taskId, record);\n\n let agent;\n try {\n agent = agentInstanceManager.getAgent({\n assistant_id: assistantId,\n thread_id: threadId,\n tenant_id: tenantId,\n workspace_id: auth.workspaceId || undefined,\n project_id: auth.projectId || undefined,\n });\n } catch (err) {\n record.status = \"failed\";\n record.updatedAt = isoNow();\n reply.status(500).send({\n id: taskId,\n error: \"Failed to initialize agent\",\n message: err instanceof Error ? err.message : String(err),\n });\n return;\n }\n\n reply.hijack();\n reply.raw.writeHead(200, {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n \"Access-Control-Allow-Origin\": \"*\",\n });\n\n const sendEvent = (event: string, data: Record<string, unknown>): void => {\n reply.raw.write(`event: ${event}\\ndata: ${JSON.stringify(data)}\\n\\n`);\n };\n\n try {\n const result = await agent.addMessage({\n input: { message: text },\n });\n\n record.messageId = result.messageId;\n\n const stream = agent.chunkStream(result.messageId, [\n MessageChunkTypes.MESSAGE_COMPLETED,\n ]);\n\n const accumulatedText = await streamChunksAsA2A(stream, taskId, sendEvent, () => {\n record.status = \"input-required\";\n record.updatedAt = isoNow();\n });\n\n record.status = \"completed\";\n record.updatedAt = isoNow();\n record.history.push({\n role: \"agent\",\n parts: [buildTextPart(accumulatedText)],\n });\n\n sendEvent(\"status-update\", {\n id: taskId,\n status: {\n state: \"completed\",\n timestamp: isoNow(),\n },\n final: true,\n });\n } catch (err) {\n record.status = \"failed\";\n record.updatedAt = isoNow();\n\n sendEvent(\"error\", {\n code: \"EXECUTION_ERROR\",\n message: err instanceof Error ? err.message : String(err),\n });\n\n sendEvent(\"status-update\", {\n id: taskId,\n status: {\n state: \"failed\",\n timestamp: isoNow(),\n },\n final: true,\n });\n } finally {\n reply.raw.end();\n }\n}\n\nasync function handleTasksCancel(\n request: FastifyRequest<{ Params: { taskId: string } }>,\n reply: FastifyReply,\n): Promise<void> {\n const auth = requireAuth(request, reply);\n if (!auth.authenticated) return;\n const { taskId } = request.params;\n const record = taskStore.get(taskId);\n\n if (!record) {\n reply.status(404).send({ error: \"Task not found\" });\n return;\n }\n\n if (record.status === \"completed\" || record.status === \"failed\" || record.status === \"canceled\") {\n reply.status(409).send({\n error: \"Task is already in a terminal state\",\n task: { id: record.id, status: record.status },\n });\n return;\n }\n\n try {\n const agent = agentInstanceManager.getAgent({\n assistant_id: record.assistantId,\n thread_id: record.threadId,\n tenant_id: record.tenantId,\n workspace_id: record.workspaceId,\n project_id: record.projectId,\n });\n await agent.abort();\n } catch (err) {\n // Agent session may have already been cleaned up\n console.warn({\n event: \"a2a:cancel:no_agent\",\n taskId,\n threadId: record.threadId,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n\n record.status = \"canceled\";\n record.updatedAt = isoNow();\n\n reply.status(200).send({\n id: taskId,\n status: {\n state: \"canceled\",\n timestamp: record.updatedAt,\n },\n });\n}\n\nasync function handleTasksStream(\n request: FastifyRequest<{ Params: { taskId: string } }>,\n reply: FastifyReply,\n): Promise<void> {\n const auth = requireAuth(request, reply);\n if (!auth.authenticated) return;\n\n const { taskId } = request.params;\n const record = taskStore.get(taskId);\n\n if (!record) {\n reply.status(404).send({ error: \"Task not found\" });\n return;\n }\n\n let agent;\n try {\n agent = agentInstanceManager.getAgent({\n assistant_id: record.assistantId,\n thread_id: record.threadId,\n tenant_id: record.tenantId,\n workspace_id: record.workspaceId,\n project_id: record.projectId,\n });\n } catch {\n reply.status(404).send({\n error: \"Agent session not found. The task may have already completed.\",\n });\n return;\n }\n\n reply.hijack();\n reply.raw.writeHead(200, {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n \"Access-Control-Allow-Origin\": \"*\",\n });\n\n const sendEvent = (event: string, data: Record<string, unknown>): void => {\n reply.raw.write(`event: ${event}\\ndata: ${JSON.stringify(data)}\\n\\n`);\n };\n\n if (!record.messageId) {\n sendEvent(\"status-update\", {\n id: taskId,\n status: {\n state: record.status,\n timestamp: record.updatedAt,\n },\n final: true,\n });\n reply.raw.end();\n return;\n }\n\n try {\n const stream = agent.chunkStream(record.messageId, [\n MessageChunkTypes.MESSAGE_COMPLETED,\n ]);\n\n await streamChunksAsA2A(stream, taskId, sendEvent);\n\n sendEvent(\"status-update\", {\n id: taskId,\n status: {\n state: record.status,\n timestamp: record.updatedAt,\n },\n final: true,\n });\n } catch (err) {\n sendEvent(\"error\", {\n code: \"STREAM_ERROR\",\n message: err instanceof Error ? err.message : String(err),\n });\n } finally {\n reply.raw.end();\n }\n}\n\n// ─── API Key Management Handlers ─────────────────────────────────────────\n\nasync function handleApiKeyList(\n request: FastifyRequest<{ Querystring: { tenantId?: string; limit?: string; offset?: string } }>,\n reply: FastifyReply,\n): Promise<void> {\n const auth = requireAuth(request, reply);\n if (!auth.authenticated) return;\n\n if (!_a2aKeyStore) {\n reply.status(501).send({ error: \"Key store not configured\" });\n return;\n }\n\n const records = await _a2aKeyStore.list({\n tenantId: request.query.tenantId,\n limit: request.query.limit ? parseInt(request.query.limit, 10) : undefined,\n offset: request.query.offset ? parseInt(request.query.offset, 10) : undefined,\n });\n\n reply.status(200).send({\n success: true,\n data: {\n records: records.map((r) => ({ ...r, key: r.key.slice(0, 8) + \"...\" })),\n total: records.length,\n },\n });\n}\n\nasync function handleApiKeyCreate(\n request: FastifyRequest<{ Body: Partial<CreateA2AApiKeyInput> }>,\n reply: FastifyReply,\n): Promise<void> {\n const auth = requireAuth(request, reply);\n if (!auth.authenticated) return;\n\n if (!_a2aKeyStore) {\n reply.status(501).send({ error: \"Key store not configured\" });\n return;\n }\n\n const body = request.body as Partial<CreateA2AApiKeyInput>;\n // Scope from request body / headers; fallback to auth context\n const tenantId =\n body.tenantId ||\n (request.headers[\"x-tenant-id\"] as string) ||\n auth.tenantId;\n const projectId =\n body.projectId || (request.headers[\"x-project-id\"] as string) || auth.projectId || \"default\";\n const workspaceId =\n body.workspaceId || (request.headers[\"x-workspace-id\"] as string) || auth.workspaceId;\n\n if (!tenantId) {\n reply.status(400).send({\n error: \"tenantId is required\",\n message: \"Provide via body.tenantId, x-tenant-id header, or authenticate with a scoped API key\",\n });\n return;\n }\n\n const record = await _a2aKeyStore.create({\n tenantId,\n projectId,\n workspaceId,\n label: body.label,\n });\n await refreshStoreKeyMap();\n\n reply.status(201).send({ success: true, data: record });\n}\n\nasync function handleApiKeyDelete(\n request: FastifyRequest<{ Params: { id: string } }>,\n reply: FastifyReply,\n): Promise<void> {\n const auth = requireAuth(request, reply);\n if (!auth.authenticated) return;\n\n if (!_a2aKeyStore) {\n reply.status(501).send({ error: \"Key store not configured\" });\n return;\n }\n\n await _a2aKeyStore.delete(request.params.id);\n await refreshStoreKeyMap();\n\n reply.status(200).send({ success: true });\n}\n\nasync function handleApiKeyDisable(\n request: FastifyRequest<{ Params: { id: string } }>,\n reply: FastifyReply,\n): Promise<void> {\n const auth = requireAuth(request, reply);\n if (!auth.authenticated) return;\n\n if (!_a2aKeyStore) {\n reply.status(501).send({ error: \"Key store not configured\" });\n return;\n }\n\n await _a2aKeyStore.disable(request.params.id);\n await refreshStoreKeyMap();\n\n reply.status(200).send({ success: true });\n}\n\nasync function handleApiKeyEnable(\n request: FastifyRequest<{ Params: { id: string } }>,\n reply: FastifyReply,\n): Promise<void> {\n const auth = requireAuth(request, reply);\n if (!auth.authenticated) return;\n\n if (!_a2aKeyStore) {\n reply.status(501).send({ error: \"Key store not configured\" });\n return;\n }\n\n await _a2aKeyStore.enable(request.params.id);\n await refreshStoreKeyMap();\n\n reply.status(200).send({ success: true });\n}\n\nasync function handleApiKeyRotate(\n request: FastifyRequest<{ Params: { id: string } }>,\n reply: FastifyReply,\n): Promise<void> {\n const auth = requireAuth(request, reply);\n if (!auth.authenticated) return;\n\n if (!_a2aKeyStore) {\n reply.status(501).send({ error: \"Key store not configured\" });\n return;\n }\n\n const record = await _a2aKeyStore.rotate(request.params.id);\n await refreshStoreKeyMap();\n\n reply.status(200).send({ success: true, data: record });\n}\n\n// ─── Route Registration ───────────────────────────────────────────────────\n\nexport function registerA2ARoutes(app: FastifyInstance): void {\n app.get(\"/api/a2a/.well-known/agent.json\", handleAgentCard);\n app.get(\"/api/a2a/.well-known/agent-card.json\", handleAgentCard);\n\n app.post<{ Body: A2ATaskSendRequest }>(\n \"/api/a2a/tasks/send\",\n handleTasksSend,\n );\n\n app.get<{ Params: { taskId: string } }>(\n \"/api/a2a/tasks/:taskId\",\n handleTasksGet,\n );\n\n app.get<{ Params: { taskId: string } }>(\n \"/api/a2a/tasks/:taskId/stream\",\n handleTasksStream,\n );\n\n app.post<{ Params: { taskId: string } }>(\n \"/api/a2a/tasks/:taskId/cancel\",\n handleTasksCancel,\n );\n\n // A2A API Key management\n app.get<{ Querystring: { tenantId?: string; limit?: string; offset?: string } }>(\n \"/api/a2a/keys\",\n handleApiKeyList,\n );\n\n app.post<{ Body: Partial<CreateA2AApiKeyInput> }>(\n \"/api/a2a/keys\",\n handleApiKeyCreate,\n );\n\n app.delete<{ Params: { id: string } }>(\n \"/api/a2a/keys/:id\",\n handleApiKeyDelete,\n );\n\n app.post<{ Params: { id: string } }>(\n \"/api/a2a/keys/:id/disable\",\n handleApiKeyDisable,\n );\n\n app.post<{ Params: { id: string } }>(\n \"/api/a2a/keys/:id/enable\",\n handleApiKeyEnable,\n );\n\n app.post<{ Params: { id: string } }>(\n \"/api/a2a/keys/:id/rotate\",\n handleApiKeyRotate,\n );\n}\n"],"mappings":"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","names":[]}
|