@kuntur/a2a-carbon-chat-adapter 0.1.1 → 0.1.2

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.
@@ -0,0 +1,147 @@
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 __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/server/index.ts
21
+ var server_exports = {};
22
+ __export(server_exports, {
23
+ createA2AHandler: () => createA2AHandler
24
+ });
25
+ module.exports = __toCommonJS(server_exports);
26
+
27
+ // src/server/create-api-handler.ts
28
+ function generateUUID() {
29
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
30
+ const r = Math.random() * 16 | 0;
31
+ const v = c === "x" ? r : r & 3 | 8;
32
+ return v.toString(16);
33
+ });
34
+ }
35
+ function createA2AHandler(options = {}) {
36
+ const { onRequest, onError, timeout = 12e4, allowedAgentUrls } = options;
37
+ return async function handler(request) {
38
+ try {
39
+ let body = await request.json();
40
+ if (!body.agentUrl || !body.message) {
41
+ return new Response(
42
+ JSON.stringify({ error: "Missing required fields: agentUrl, message" }),
43
+ { status: 400, headers: { "Content-Type": "application/json" } }
44
+ );
45
+ }
46
+ if (allowedAgentUrls && allowedAgentUrls.length > 0) {
47
+ const isAllowed = allowedAgentUrls.some((pattern) => {
48
+ if (typeof pattern === "string") {
49
+ return body.agentUrl.startsWith(pattern);
50
+ }
51
+ return pattern.test(body.agentUrl);
52
+ });
53
+ if (!isAllowed) {
54
+ return new Response(
55
+ JSON.stringify({ error: "Agent URL not allowed" }),
56
+ { status: 403, headers: { "Content-Type": "application/json" } }
57
+ );
58
+ }
59
+ }
60
+ if (onRequest) {
61
+ body = await onRequest(body);
62
+ }
63
+ let normalizedUrl = body.agentUrl.replace(/\/$/, "");
64
+ if (!normalizedUrl.endsWith("/jsonrpc")) {
65
+ normalizedUrl = `${normalizedUrl}/jsonrpc/`;
66
+ } else {
67
+ normalizedUrl = `${normalizedUrl}/`;
68
+ }
69
+ const payload = {
70
+ jsonrpc: "2.0",
71
+ method: "message/stream",
72
+ params: {
73
+ message: {
74
+ role: "user",
75
+ messageId: generateUUID(),
76
+ parts: [{ kind: "text", text: body.message }]
77
+ },
78
+ ...body.extensions && { extensions: body.extensions }
79
+ },
80
+ id: generateUUID()
81
+ };
82
+ const headers = {
83
+ "Content-Type": "application/json",
84
+ Accept: "text/event-stream"
85
+ };
86
+ if (body.apiKey) {
87
+ headers["Authorization"] = `Bearer ${body.apiKey}`;
88
+ }
89
+ const controller = new AbortController();
90
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
91
+ try {
92
+ const agentResponse = await fetch(normalizedUrl, {
93
+ method: "POST",
94
+ headers,
95
+ body: JSON.stringify(payload),
96
+ signal: controller.signal
97
+ });
98
+ clearTimeout(timeoutId);
99
+ if (!agentResponse.ok) {
100
+ const errorText = await agentResponse.text();
101
+ return new Response(
102
+ JSON.stringify({
103
+ error: `Agent error: ${agentResponse.status}`,
104
+ details: errorText
105
+ }),
106
+ { status: agentResponse.status, headers: { "Content-Type": "application/json" } }
107
+ );
108
+ }
109
+ if (!agentResponse.body) {
110
+ return new Response(
111
+ JSON.stringify({ error: "Agent response body is null" }),
112
+ { status: 500, headers: { "Content-Type": "application/json" } }
113
+ );
114
+ }
115
+ return new Response(agentResponse.body, {
116
+ headers: {
117
+ "Content-Type": "text/event-stream",
118
+ "Cache-Control": "no-cache",
119
+ Connection: "keep-alive"
120
+ }
121
+ });
122
+ } finally {
123
+ clearTimeout(timeoutId);
124
+ }
125
+ } catch (error) {
126
+ onError?.(error);
127
+ if (error.name === "AbortError") {
128
+ return new Response(
129
+ JSON.stringify({ error: "Request timeout" }),
130
+ { status: 504, headers: { "Content-Type": "application/json" } }
131
+ );
132
+ }
133
+ return new Response(
134
+ JSON.stringify({
135
+ error: "Internal server error",
136
+ message: error.message
137
+ }),
138
+ { status: 500, headers: { "Content-Type": "application/json" } }
139
+ );
140
+ }
141
+ };
142
+ }
143
+ // Annotate the CommonJS export names for ESM import in node:
144
+ 0 && (module.exports = {
145
+ createA2AHandler
146
+ });
147
+ //# sourceMappingURL=server.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/server/index.ts","../src/server/create-api-handler.ts"],"sourcesContent":["/**\n * Server-side utilities\n *\n * @example\n * import { createA2AHandler } from '@kuntur/a2a-carbon-chat-adapter/server';\n */\n\nexport { createA2AHandler } from './create-api-handler';\nexport type { A2AHandlerOptions } from './create-api-handler';\n","/**\n * Factory for creating Next.js API route handlers for A2A proxy\n *\n * @example\n * // app/api/agent/chat/route.ts\n * import { createA2AHandler } from '@kuntur/a2a-carbon-chat-adapter/server';\n *\n * export const POST = createA2AHandler({\n * allowedAgentUrls: ['https://trusted-agents.example.com']\n * });\n */\n\nexport interface A2AHandlerOptions {\n /**\n * Called before forwarding request to agent\n * Use for authentication, validation, rate limiting\n */\n onRequest?: (request: {\n agentUrl: string;\n apiKey?: string;\n message: string;\n extensions?: Record<string, unknown>;\n }) => Promise<typeof request> | typeof request;\n\n /**\n * Called on error\n */\n onError?: (error: Error) => void;\n\n /**\n * Request timeout in milliseconds\n * @default 120000 (2 minutes)\n */\n timeout?: number;\n\n /**\n * Allowed agent URL patterns (for security)\n * If provided, requests to non-matching URLs will be rejected\n */\n allowedAgentUrls?: (string | RegExp)[];\n}\n\nfunction generateUUID(): string {\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === 'x' ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n}\n\nexport function createA2AHandler(options: A2AHandlerOptions = {}) {\n const { onRequest, onError, timeout = 120000, allowedAgentUrls } = options;\n\n return async function handler(request: Request): Promise<Response> {\n try {\n let body = await request.json();\n\n // Validate required fields\n if (!body.agentUrl || !body.message) {\n return new Response(\n JSON.stringify({ error: 'Missing required fields: agentUrl, message' }),\n { status: 400, headers: { 'Content-Type': 'application/json' } }\n );\n }\n\n // Check allowed URLs\n if (allowedAgentUrls && allowedAgentUrls.length > 0) {\n const isAllowed = allowedAgentUrls.some((pattern) => {\n if (typeof pattern === 'string') {\n return body.agentUrl.startsWith(pattern);\n }\n return pattern.test(body.agentUrl);\n });\n\n if (!isAllowed) {\n return new Response(\n JSON.stringify({ error: 'Agent URL not allowed' }),\n { status: 403, headers: { 'Content-Type': 'application/json' } }\n );\n }\n }\n\n // Allow request transformation\n if (onRequest) {\n body = await onRequest(body);\n }\n\n // Normalize URL\n let normalizedUrl = body.agentUrl.replace(/\\/$/, '');\n if (!normalizedUrl.endsWith('/jsonrpc')) {\n normalizedUrl = `${normalizedUrl}/jsonrpc/`;\n } else {\n normalizedUrl = `${normalizedUrl}/`;\n }\n\n // Build A2A payload\n const payload = {\n jsonrpc: '2.0',\n method: 'message/stream',\n params: {\n message: {\n role: 'user',\n messageId: generateUUID(),\n parts: [{ kind: 'text', text: body.message }],\n },\n ...(body.extensions && { extensions: body.extensions }),\n },\n id: generateUUID(),\n };\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n Accept: 'text/event-stream',\n };\n\n if (body.apiKey) {\n headers['Authorization'] = `Bearer ${body.apiKey}`;\n }\n\n // Create abort controller for timeout\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n try {\n const agentResponse = await fetch(normalizedUrl, {\n method: 'POST',\n headers,\n body: JSON.stringify(payload),\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (!agentResponse.ok) {\n const errorText = await agentResponse.text();\n return new Response(\n JSON.stringify({\n error: `Agent error: ${agentResponse.status}`,\n details: errorText,\n }),\n { status: agentResponse.status, headers: { 'Content-Type': 'application/json' } }\n );\n }\n\n if (!agentResponse.body) {\n return new Response(\n JSON.stringify({ error: 'Agent response body is null' }),\n { status: 500, headers: { 'Content-Type': 'application/json' } }\n );\n }\n\n // Stream the response\n return new Response(agentResponse.body, {\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n },\n });\n } finally {\n clearTimeout(timeoutId);\n }\n } catch (error) {\n onError?.(error as Error);\n\n if ((error as Error).name === 'AbortError') {\n return new Response(\n JSON.stringify({ error: 'Request timeout' }),\n { status: 504, headers: { 'Content-Type': 'application/json' } }\n );\n }\n\n return new Response(\n JSON.stringify({\n error: 'Internal server error',\n message: (error as Error).message,\n }),\n { status: 500, headers: { 'Content-Type': 'application/json' } }\n );\n }\n };\n}\n\nexport default createA2AHandler;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;AC0CA,SAAS,eAAuB;AAC9B,SAAO,uCAAuC,QAAQ,SAAS,CAAC,MAAM;AACpE,UAAM,IAAK,KAAK,OAAO,IAAI,KAAM;AACjC,UAAM,IAAI,MAAM,MAAM,IAAK,IAAI,IAAO;AACtC,WAAO,EAAE,SAAS,EAAE;AAAA,EACtB,CAAC;AACH;AAEO,SAAS,iBAAiB,UAA6B,CAAC,GAAG;AAChE,QAAM,EAAE,WAAW,SAAS,UAAU,MAAQ,iBAAiB,IAAI;AAEnE,SAAO,eAAe,QAAQ,SAAqC;AACjE,QAAI;AACF,UAAI,OAAO,MAAM,QAAQ,KAAK;AAG9B,UAAI,CAAC,KAAK,YAAY,CAAC,KAAK,SAAS;AACnC,eAAO,IAAI;AAAA,UACT,KAAK,UAAU,EAAE,OAAO,6CAA6C,CAAC;AAAA,UACtE,EAAE,QAAQ,KAAK,SAAS,EAAE,gBAAgB,mBAAmB,EAAE;AAAA,QACjE;AAAA,MACF;AAGA,UAAI,oBAAoB,iBAAiB,SAAS,GAAG;AACnD,cAAM,YAAY,iBAAiB,KAAK,CAAC,YAAY;AACnD,cAAI,OAAO,YAAY,UAAU;AAC/B,mBAAO,KAAK,SAAS,WAAW,OAAO;AAAA,UACzC;AACA,iBAAO,QAAQ,KAAK,KAAK,QAAQ;AAAA,QACnC,CAAC;AAED,YAAI,CAAC,WAAW;AACd,iBAAO,IAAI;AAAA,YACT,KAAK,UAAU,EAAE,OAAO,wBAAwB,CAAC;AAAA,YACjD,EAAE,QAAQ,KAAK,SAAS,EAAE,gBAAgB,mBAAmB,EAAE;AAAA,UACjE;AAAA,QACF;AAAA,MACF;AAGA,UAAI,WAAW;AACb,eAAO,MAAM,UAAU,IAAI;AAAA,MAC7B;AAGA,UAAI,gBAAgB,KAAK,SAAS,QAAQ,OAAO,EAAE;AACnD,UAAI,CAAC,cAAc,SAAS,UAAU,GAAG;AACvC,wBAAgB,GAAG,aAAa;AAAA,MAClC,OAAO;AACL,wBAAgB,GAAG,aAAa;AAAA,MAClC;AAGA,YAAM,UAAU;AAAA,QACd,SAAS;AAAA,QACT,QAAQ;AAAA,QACR,QAAQ;AAAA,UACN,SAAS;AAAA,YACP,MAAM;AAAA,YACN,WAAW,aAAa;AAAA,YACxB,OAAO,CAAC,EAAE,MAAM,QAAQ,MAAM,KAAK,QAAQ,CAAC;AAAA,UAC9C;AAAA,UACA,GAAI,KAAK,cAAc,EAAE,YAAY,KAAK,WAAW;AAAA,QACvD;AAAA,QACA,IAAI,aAAa;AAAA,MACnB;AAEA,YAAM,UAAkC;AAAA,QACtC,gBAAgB;AAAA,QAChB,QAAQ;AAAA,MACV;AAEA,UAAI,KAAK,QAAQ;AACf,gBAAQ,eAAe,IAAI,UAAU,KAAK,MAAM;AAAA,MAClD;AAGA,YAAM,aAAa,IAAI,gBAAgB;AACvC,YAAM,YAAY,WAAW,MAAM,WAAW,MAAM,GAAG,OAAO;AAE9D,UAAI;AACF,cAAM,gBAAgB,MAAM,MAAM,eAAe;AAAA,UAC/C,QAAQ;AAAA,UACR;AAAA,UACA,MAAM,KAAK,UAAU,OAAO;AAAA,UAC5B,QAAQ,WAAW;AAAA,QACrB,CAAC;AAED,qBAAa,SAAS;AAEtB,YAAI,CAAC,cAAc,IAAI;AACrB,gBAAM,YAAY,MAAM,cAAc,KAAK;AAC3C,iBAAO,IAAI;AAAA,YACT,KAAK,UAAU;AAAA,cACb,OAAO,gBAAgB,cAAc,MAAM;AAAA,cAC3C,SAAS;AAAA,YACX,CAAC;AAAA,YACD,EAAE,QAAQ,cAAc,QAAQ,SAAS,EAAE,gBAAgB,mBAAmB,EAAE;AAAA,UAClF;AAAA,QACF;AAEA,YAAI,CAAC,cAAc,MAAM;AACvB,iBAAO,IAAI;AAAA,YACT,KAAK,UAAU,EAAE,OAAO,8BAA8B,CAAC;AAAA,YACvD,EAAE,QAAQ,KAAK,SAAS,EAAE,gBAAgB,mBAAmB,EAAE;AAAA,UACjE;AAAA,QACF;AAGA,eAAO,IAAI,SAAS,cAAc,MAAM;AAAA,UACtC,SAAS;AAAA,YACP,gBAAgB;AAAA,YAChB,iBAAiB;AAAA,YACjB,YAAY;AAAA,UACd;AAAA,QACF,CAAC;AAAA,MACH,UAAE;AACA,qBAAa,SAAS;AAAA,MACxB;AAAA,IACF,SAAS,OAAO;AACd,gBAAU,KAAc;AAExB,UAAK,MAAgB,SAAS,cAAc;AAC1C,eAAO,IAAI;AAAA,UACT,KAAK,UAAU,EAAE,OAAO,kBAAkB,CAAC;AAAA,UAC3C,EAAE,QAAQ,KAAK,SAAS,EAAE,gBAAgB,mBAAmB,EAAE;AAAA,QACjE;AAAA,MACF;AAEA,aAAO,IAAI;AAAA,QACT,KAAK,UAAU;AAAA,UACb,OAAO;AAAA,UACP,SAAU,MAAgB;AAAA,QAC5B,CAAC;AAAA,QACD,EAAE,QAAQ,KAAK,SAAS,EAAE,gBAAgB,mBAAmB,EAAE;AAAA,MACjE;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Factory for creating Next.js API route handlers for A2A proxy
3
+ *
4
+ * @example
5
+ * // app/api/agent/chat/route.ts
6
+ * import { createA2AHandler } from '@kuntur/a2a-carbon-chat-adapter/server';
7
+ *
8
+ * export const POST = createA2AHandler({
9
+ * allowedAgentUrls: ['https://trusted-agents.example.com']
10
+ * });
11
+ */
12
+ interface A2AHandlerOptions {
13
+ /**
14
+ * Called before forwarding request to agent
15
+ * Use for authentication, validation, rate limiting
16
+ */
17
+ onRequest?: (request: {
18
+ agentUrl: string;
19
+ apiKey?: string;
20
+ message: string;
21
+ extensions?: Record<string, unknown>;
22
+ }) => Promise<typeof request> | typeof request;
23
+ /**
24
+ * Called on error
25
+ */
26
+ onError?: (error: Error) => void;
27
+ /**
28
+ * Request timeout in milliseconds
29
+ * @default 120000 (2 minutes)
30
+ */
31
+ timeout?: number;
32
+ /**
33
+ * Allowed agent URL patterns (for security)
34
+ * If provided, requests to non-matching URLs will be rejected
35
+ */
36
+ allowedAgentUrls?: (string | RegExp)[];
37
+ }
38
+ declare function createA2AHandler(options?: A2AHandlerOptions): (request: Request) => Promise<Response>;
39
+
40
+ export { type A2AHandlerOptions, createA2AHandler };
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Factory for creating Next.js API route handlers for A2A proxy
3
+ *
4
+ * @example
5
+ * // app/api/agent/chat/route.ts
6
+ * import { createA2AHandler } from '@kuntur/a2a-carbon-chat-adapter/server';
7
+ *
8
+ * export const POST = createA2AHandler({
9
+ * allowedAgentUrls: ['https://trusted-agents.example.com']
10
+ * });
11
+ */
12
+ interface A2AHandlerOptions {
13
+ /**
14
+ * Called before forwarding request to agent
15
+ * Use for authentication, validation, rate limiting
16
+ */
17
+ onRequest?: (request: {
18
+ agentUrl: string;
19
+ apiKey?: string;
20
+ message: string;
21
+ extensions?: Record<string, unknown>;
22
+ }) => Promise<typeof request> | typeof request;
23
+ /**
24
+ * Called on error
25
+ */
26
+ onError?: (error: Error) => void;
27
+ /**
28
+ * Request timeout in milliseconds
29
+ * @default 120000 (2 minutes)
30
+ */
31
+ timeout?: number;
32
+ /**
33
+ * Allowed agent URL patterns (for security)
34
+ * If provided, requests to non-matching URLs will be rejected
35
+ */
36
+ allowedAgentUrls?: (string | RegExp)[];
37
+ }
38
+ declare function createA2AHandler(options?: A2AHandlerOptions): (request: Request) => Promise<Response>;
39
+
40
+ export { type A2AHandlerOptions, createA2AHandler };
package/dist/server.js ADDED
@@ -0,0 +1,120 @@
1
+ // src/server/create-api-handler.ts
2
+ function generateUUID() {
3
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
4
+ const r = Math.random() * 16 | 0;
5
+ const v = c === "x" ? r : r & 3 | 8;
6
+ return v.toString(16);
7
+ });
8
+ }
9
+ function createA2AHandler(options = {}) {
10
+ const { onRequest, onError, timeout = 12e4, allowedAgentUrls } = options;
11
+ return async function handler(request) {
12
+ try {
13
+ let body = await request.json();
14
+ if (!body.agentUrl || !body.message) {
15
+ return new Response(
16
+ JSON.stringify({ error: "Missing required fields: agentUrl, message" }),
17
+ { status: 400, headers: { "Content-Type": "application/json" } }
18
+ );
19
+ }
20
+ if (allowedAgentUrls && allowedAgentUrls.length > 0) {
21
+ const isAllowed = allowedAgentUrls.some((pattern) => {
22
+ if (typeof pattern === "string") {
23
+ return body.agentUrl.startsWith(pattern);
24
+ }
25
+ return pattern.test(body.agentUrl);
26
+ });
27
+ if (!isAllowed) {
28
+ return new Response(
29
+ JSON.stringify({ error: "Agent URL not allowed" }),
30
+ { status: 403, headers: { "Content-Type": "application/json" } }
31
+ );
32
+ }
33
+ }
34
+ if (onRequest) {
35
+ body = await onRequest(body);
36
+ }
37
+ let normalizedUrl = body.agentUrl.replace(/\/$/, "");
38
+ if (!normalizedUrl.endsWith("/jsonrpc")) {
39
+ normalizedUrl = `${normalizedUrl}/jsonrpc/`;
40
+ } else {
41
+ normalizedUrl = `${normalizedUrl}/`;
42
+ }
43
+ const payload = {
44
+ jsonrpc: "2.0",
45
+ method: "message/stream",
46
+ params: {
47
+ message: {
48
+ role: "user",
49
+ messageId: generateUUID(),
50
+ parts: [{ kind: "text", text: body.message }]
51
+ },
52
+ ...body.extensions && { extensions: body.extensions }
53
+ },
54
+ id: generateUUID()
55
+ };
56
+ const headers = {
57
+ "Content-Type": "application/json",
58
+ Accept: "text/event-stream"
59
+ };
60
+ if (body.apiKey) {
61
+ headers["Authorization"] = `Bearer ${body.apiKey}`;
62
+ }
63
+ const controller = new AbortController();
64
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
65
+ try {
66
+ const agentResponse = await fetch(normalizedUrl, {
67
+ method: "POST",
68
+ headers,
69
+ body: JSON.stringify(payload),
70
+ signal: controller.signal
71
+ });
72
+ clearTimeout(timeoutId);
73
+ if (!agentResponse.ok) {
74
+ const errorText = await agentResponse.text();
75
+ return new Response(
76
+ JSON.stringify({
77
+ error: `Agent error: ${agentResponse.status}`,
78
+ details: errorText
79
+ }),
80
+ { status: agentResponse.status, headers: { "Content-Type": "application/json" } }
81
+ );
82
+ }
83
+ if (!agentResponse.body) {
84
+ return new Response(
85
+ JSON.stringify({ error: "Agent response body is null" }),
86
+ { status: 500, headers: { "Content-Type": "application/json" } }
87
+ );
88
+ }
89
+ return new Response(agentResponse.body, {
90
+ headers: {
91
+ "Content-Type": "text/event-stream",
92
+ "Cache-Control": "no-cache",
93
+ Connection: "keep-alive"
94
+ }
95
+ });
96
+ } finally {
97
+ clearTimeout(timeoutId);
98
+ }
99
+ } catch (error) {
100
+ onError?.(error);
101
+ if (error.name === "AbortError") {
102
+ return new Response(
103
+ JSON.stringify({ error: "Request timeout" }),
104
+ { status: 504, headers: { "Content-Type": "application/json" } }
105
+ );
106
+ }
107
+ return new Response(
108
+ JSON.stringify({
109
+ error: "Internal server error",
110
+ message: error.message
111
+ }),
112
+ { status: 500, headers: { "Content-Type": "application/json" } }
113
+ );
114
+ }
115
+ };
116
+ }
117
+ export {
118
+ createA2AHandler
119
+ };
120
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/server/create-api-handler.ts"],"sourcesContent":["/**\n * Factory for creating Next.js API route handlers for A2A proxy\n *\n * @example\n * // app/api/agent/chat/route.ts\n * import { createA2AHandler } from '@kuntur/a2a-carbon-chat-adapter/server';\n *\n * export const POST = createA2AHandler({\n * allowedAgentUrls: ['https://trusted-agents.example.com']\n * });\n */\n\nexport interface A2AHandlerOptions {\n /**\n * Called before forwarding request to agent\n * Use for authentication, validation, rate limiting\n */\n onRequest?: (request: {\n agentUrl: string;\n apiKey?: string;\n message: string;\n extensions?: Record<string, unknown>;\n }) => Promise<typeof request> | typeof request;\n\n /**\n * Called on error\n */\n onError?: (error: Error) => void;\n\n /**\n * Request timeout in milliseconds\n * @default 120000 (2 minutes)\n */\n timeout?: number;\n\n /**\n * Allowed agent URL patterns (for security)\n * If provided, requests to non-matching URLs will be rejected\n */\n allowedAgentUrls?: (string | RegExp)[];\n}\n\nfunction generateUUID(): string {\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === 'x' ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n}\n\nexport function createA2AHandler(options: A2AHandlerOptions = {}) {\n const { onRequest, onError, timeout = 120000, allowedAgentUrls } = options;\n\n return async function handler(request: Request): Promise<Response> {\n try {\n let body = await request.json();\n\n // Validate required fields\n if (!body.agentUrl || !body.message) {\n return new Response(\n JSON.stringify({ error: 'Missing required fields: agentUrl, message' }),\n { status: 400, headers: { 'Content-Type': 'application/json' } }\n );\n }\n\n // Check allowed URLs\n if (allowedAgentUrls && allowedAgentUrls.length > 0) {\n const isAllowed = allowedAgentUrls.some((pattern) => {\n if (typeof pattern === 'string') {\n return body.agentUrl.startsWith(pattern);\n }\n return pattern.test(body.agentUrl);\n });\n\n if (!isAllowed) {\n return new Response(\n JSON.stringify({ error: 'Agent URL not allowed' }),\n { status: 403, headers: { 'Content-Type': 'application/json' } }\n );\n }\n }\n\n // Allow request transformation\n if (onRequest) {\n body = await onRequest(body);\n }\n\n // Normalize URL\n let normalizedUrl = body.agentUrl.replace(/\\/$/, '');\n if (!normalizedUrl.endsWith('/jsonrpc')) {\n normalizedUrl = `${normalizedUrl}/jsonrpc/`;\n } else {\n normalizedUrl = `${normalizedUrl}/`;\n }\n\n // Build A2A payload\n const payload = {\n jsonrpc: '2.0',\n method: 'message/stream',\n params: {\n message: {\n role: 'user',\n messageId: generateUUID(),\n parts: [{ kind: 'text', text: body.message }],\n },\n ...(body.extensions && { extensions: body.extensions }),\n },\n id: generateUUID(),\n };\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n Accept: 'text/event-stream',\n };\n\n if (body.apiKey) {\n headers['Authorization'] = `Bearer ${body.apiKey}`;\n }\n\n // Create abort controller for timeout\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n try {\n const agentResponse = await fetch(normalizedUrl, {\n method: 'POST',\n headers,\n body: JSON.stringify(payload),\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (!agentResponse.ok) {\n const errorText = await agentResponse.text();\n return new Response(\n JSON.stringify({\n error: `Agent error: ${agentResponse.status}`,\n details: errorText,\n }),\n { status: agentResponse.status, headers: { 'Content-Type': 'application/json' } }\n );\n }\n\n if (!agentResponse.body) {\n return new Response(\n JSON.stringify({ error: 'Agent response body is null' }),\n { status: 500, headers: { 'Content-Type': 'application/json' } }\n );\n }\n\n // Stream the response\n return new Response(agentResponse.body, {\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n },\n });\n } finally {\n clearTimeout(timeoutId);\n }\n } catch (error) {\n onError?.(error as Error);\n\n if ((error as Error).name === 'AbortError') {\n return new Response(\n JSON.stringify({ error: 'Request timeout' }),\n { status: 504, headers: { 'Content-Type': 'application/json' } }\n );\n }\n\n return new Response(\n JSON.stringify({\n error: 'Internal server error',\n message: (error as Error).message,\n }),\n { status: 500, headers: { 'Content-Type': 'application/json' } }\n );\n }\n };\n}\n\nexport default createA2AHandler;\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kuntur/a2a-carbon-chat-adapter",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "A2A protocol adapter for Carbon AI Chat - connect any A2A agent to Carbon Chat UI",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",