@gnomondigital/nebulas-kit-core 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/server/nebulas-proxy-handler.d.ts +39 -0
- package/dist/server/nebulas-proxy-handler.d.ts.map +1 -0
- package/dist/server/nebulas-proxy-handler.js +129 -0
- package/dist/server/nextjs.d.ts +26 -0
- package/dist/server/nextjs.d.ts.map +1 -1
- package/dist/server/nextjs.js +110 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -20,6 +20,7 @@ npm install @gnomondigital/nebulas-kit-core
|
|
|
20
20
|
| `AUTH0_CLIENT_SECRET` | For client_credentials, ROP | Auth0 client secret |
|
|
21
21
|
| `AUTH0_A2A_AUDIENCE` | For Auth0 strategies | API audience |
|
|
22
22
|
| `NEBULAS_API_URL` | For Nebulas | Conversation API base URL |
|
|
23
|
+
| `NEBULAS_API_KEY` | For Nebulas proxy | x-api-key fallback when no session token |
|
|
23
24
|
| `NEBULAS_PROJECT_ID` | For Nebulas | Project ID |
|
|
24
25
|
|
|
25
26
|
## Usage
|
|
@@ -69,6 +70,24 @@ export async function GET() {
|
|
|
69
70
|
}
|
|
70
71
|
```
|
|
71
72
|
|
|
73
|
+
```ts
|
|
74
|
+
// app/api/nebulas/[...path]/route.ts - proxies to Nebulas API (conversations, chat_messages)
|
|
75
|
+
import { createNebulasProxyHandler } from "@gnomondigital/nebulas-kit-core";
|
|
76
|
+
import { auth0 } from "@/lib/auth";
|
|
77
|
+
|
|
78
|
+
const handler = createNebulasProxyHandler({
|
|
79
|
+
auth0,
|
|
80
|
+
audience: process.env.AUTH0_A2A_AUDIENCE,
|
|
81
|
+
apiKey: process.env.NEBULAS_API_KEY || process.env.A2A_API_KEY,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
export const GET = handler;
|
|
85
|
+
export const POST = handler;
|
|
86
|
+
export const PUT = handler;
|
|
87
|
+
export const PATCH = handler;
|
|
88
|
+
export const DELETE = handler;
|
|
89
|
+
```
|
|
90
|
+
|
|
72
91
|
### Express
|
|
73
92
|
|
|
74
93
|
```ts
|
package/dist/index.d.ts
CHANGED
|
@@ -11,5 +11,6 @@ export { getAuthStrategy, getAuthHeaders, type AuthStrategy, } from "./server/au
|
|
|
11
11
|
export { exchangePasswordForToken, type ROPAuthRequest, type ROPAuthResponse, } from "./server/auth-handler.js";
|
|
12
12
|
export { createConfigResponse, type ConfigResponse, type AgentCard, type AgentCardCapabilities, } from "./server/config-handler.js";
|
|
13
13
|
export { createA2AExpressRouter, type A2AExpressRouterOptions, } from "./server/express.js";
|
|
14
|
-
export { handleA2ANextJsPOST, handleA2AAuthNextJsPOST, handleA2AConfigNextJsGET, createConfigGetHeadersForAuth0, type HandleA2AConfigOptions, type Auth0ClientForConfig, } from "./server/nextjs.js";
|
|
14
|
+
export { handleA2ANextJsPOST, handleA2AAuthNextJsPOST, handleA2AConfigNextJsGET, createConfigGetHeadersForAuth0, createNebulasProxyHandler, type HandleA2AConfigOptions, type Auth0ClientForConfig, type CreateNebulasProxyHandlerOptions, } from "./server/nextjs.js";
|
|
15
|
+
export { handleNebulasProxy, isRefreshTokenInvalid, type NebulasProxyRequest, type NebulasProxyResponse, type NebulasProxyHandlerOptions, } from "./server/nebulas-proxy-handler.js";
|
|
15
16
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,iBAAiB,GAClB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,mBAAmB,EACnB,aAAa,EACb,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,uBAAuB,EAC5B,KAAK,oBAAoB,GAC1B,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,wBAAwB,GACzB,MAAM,0BAA0B,CAAC;AAElC,YAAY,EAAE,WAAW,EAAE,kBAAkB,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAE5F,OAAO,EACL,cAAc,EACd,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,mBAAmB,GACzB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACL,eAAe,EACf,cAAc,EACd,KAAK,YAAY,GAClB,MAAM,6BAA6B,CAAC;AAErC,OAAO,EACL,wBAAwB,EACxB,KAAK,cAAc,EACnB,KAAK,eAAe,GACrB,MAAM,0BAA0B,CAAC;AAElC,OAAO,EACL,oBAAoB,EACpB,KAAK,cAAc,EACnB,KAAK,SAAS,EACd,KAAK,qBAAqB,GAC3B,MAAM,4BAA4B,CAAC;AAEpC,OAAO,EACL,sBAAsB,EACtB,KAAK,uBAAuB,GAC7B,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EACL,mBAAmB,EACnB,uBAAuB,EACvB,wBAAwB,EACxB,8BAA8B,EAC9B,KAAK,sBAAsB,EAC3B,KAAK,oBAAoB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,iBAAiB,GAClB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,mBAAmB,EACnB,aAAa,EACb,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,uBAAuB,EAC5B,KAAK,oBAAoB,GAC1B,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,wBAAwB,GACzB,MAAM,0BAA0B,CAAC;AAElC,YAAY,EAAE,WAAW,EAAE,kBAAkB,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAE5F,OAAO,EACL,cAAc,EACd,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,mBAAmB,GACzB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACL,eAAe,EACf,cAAc,EACd,KAAK,YAAY,GAClB,MAAM,6BAA6B,CAAC;AAErC,OAAO,EACL,wBAAwB,EACxB,KAAK,cAAc,EACnB,KAAK,eAAe,GACrB,MAAM,0BAA0B,CAAC;AAElC,OAAO,EACL,oBAAoB,EACpB,KAAK,cAAc,EACnB,KAAK,SAAS,EACd,KAAK,qBAAqB,GAC3B,MAAM,4BAA4B,CAAC;AAEpC,OAAO,EACL,sBAAsB,EACtB,KAAK,uBAAuB,GAC7B,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EACL,mBAAmB,EACnB,uBAAuB,EACvB,wBAAwB,EACxB,8BAA8B,EAC9B,yBAAyB,EACzB,KAAK,sBAAsB,EAC3B,KAAK,oBAAoB,EACzB,KAAK,gCAAgC,GACtC,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,kBAAkB,EAClB,qBAAqB,EACrB,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,0BAA0B,GAChC,MAAM,mCAAmC,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -11,4 +11,5 @@ export { exchangePasswordForToken, } from "./server/auth-handler.js";
|
|
|
11
11
|
export { createConfigResponse, } from "./server/config-handler.js";
|
|
12
12
|
export { createA2AExpressRouter, } from "./server/express.js";
|
|
13
13
|
// Usage: import express from "express"; app.use("/api/a2a", createA2AExpressRouter(express));
|
|
14
|
-
export { handleA2ANextJsPOST, handleA2AAuthNextJsPOST, handleA2AConfigNextJsGET, createConfigGetHeadersForAuth0, } from "./server/nextjs.js";
|
|
14
|
+
export { handleA2ANextJsPOST, handleA2AAuthNextJsPOST, handleA2AConfigNextJsGET, createConfigGetHeadersForAuth0, createNebulasProxyHandler, } from "./server/nextjs.js";
|
|
15
|
+
export { handleNebulasProxy, isRefreshTokenInvalid, } from "./server/nebulas-proxy-handler.js";
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Framework-agnostic Nebulas API proxy handler.
|
|
3
|
+
* Proxies requests to the Nebulas API (conversations, chat_messages).
|
|
4
|
+
*/
|
|
5
|
+
export interface NebulasProxyRequest {
|
|
6
|
+
method: string;
|
|
7
|
+
path: string[];
|
|
8
|
+
headers: Record<string, string>;
|
|
9
|
+
body?: string;
|
|
10
|
+
searchParams: Record<string, string>;
|
|
11
|
+
}
|
|
12
|
+
export interface NebulasProxyResponse {
|
|
13
|
+
status: number;
|
|
14
|
+
statusText?: string;
|
|
15
|
+
headers: Record<string, string>;
|
|
16
|
+
body?: string | ReadableStream<Uint8Array> | ArrayBuffer;
|
|
17
|
+
}
|
|
18
|
+
export interface NebulasProxyHandlerOptions {
|
|
19
|
+
/**
|
|
20
|
+
* When provided, fetches the access token from session (e.g. Auth0).
|
|
21
|
+
* Should throw or return NextResponse for 401 when session invalid.
|
|
22
|
+
*/
|
|
23
|
+
getHeaders?: (req: NebulasProxyRequest) => Promise<Record<string, string>>;
|
|
24
|
+
/**
|
|
25
|
+
* Pre-resolved auth headers. Use when getHeaders was already called
|
|
26
|
+
* (e.g. before consuming request body, which getAccessToken may need).
|
|
27
|
+
*/
|
|
28
|
+
headers?: Record<string, string>;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Check if an error indicates invalid refresh token (for session re-auth).
|
|
32
|
+
*/
|
|
33
|
+
export declare function isRefreshTokenInvalid(err: unknown): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Handle Nebulas API proxy request.
|
|
36
|
+
* Proxies to NEBULAS_API_URL with auth headers from getHeaders or API keys.
|
|
37
|
+
*/
|
|
38
|
+
export declare function handleNebulasProxy(req: NebulasProxyRequest, options?: NebulasProxyHandlerOptions): Promise<NebulasProxyResponse>;
|
|
39
|
+
//# sourceMappingURL=nebulas-proxy-handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nebulas-proxy-handler.d.ts","sourceRoot":"","sources":["../../src/server/nebulas-proxy-handler.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAQH,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACtC;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,IAAI,CAAC,EAAE,MAAM,GAAG,cAAc,CAAC,UAAU,CAAC,GAAG,WAAW,CAAC;CAC1D;AAED,MAAM,WAAW,0BAA0B;IACzC;;;OAGG;IACH,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,mBAAmB,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC3E;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAS3D;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,mBAAmB,EACxB,OAAO,GAAE,0BAA+B,GACvC,OAAO,CAAC,oBAAoB,CAAC,CAwH/B"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Framework-agnostic Nebulas API proxy handler.
|
|
3
|
+
* Proxies requests to the Nebulas API (conversations, chat_messages).
|
|
4
|
+
*/
|
|
5
|
+
const NEBULAS_API_URL = process.env.NEBULAS_API_URL || "http://localhost:8020";
|
|
6
|
+
const A2A_AUDIENCE = process.env.AUTH0_A2A_AUDIENCE;
|
|
7
|
+
const A2A_API_KEY = process.env.A2A_API_KEY;
|
|
8
|
+
const NEBULAS_API_KEY = process.env.NEBULAS_API_KEY;
|
|
9
|
+
/**
|
|
10
|
+
* Check if an error indicates invalid refresh token (for session re-auth).
|
|
11
|
+
*/
|
|
12
|
+
export function isRefreshTokenInvalid(err) {
|
|
13
|
+
if (typeof err === "object" && err !== null) {
|
|
14
|
+
const e = err;
|
|
15
|
+
return (e?.code === "failed_to_refresh_token" ||
|
|
16
|
+
e?.cause?.code === "invalid_grant");
|
|
17
|
+
}
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Handle Nebulas API proxy request.
|
|
22
|
+
* Proxies to NEBULAS_API_URL with auth headers from getHeaders or API keys.
|
|
23
|
+
*/
|
|
24
|
+
export async function handleNebulasProxy(req, options = {}) {
|
|
25
|
+
const backendPath = `/${req.path.join("/")}`;
|
|
26
|
+
const url = new URL(backendPath, NEBULAS_API_URL.replace(/\/$/, "") + "/");
|
|
27
|
+
for (const [key, value] of Object.entries(req.searchParams)) {
|
|
28
|
+
url.searchParams.set(key, value);
|
|
29
|
+
}
|
|
30
|
+
const headers = {};
|
|
31
|
+
const contentType = req.headers["content-type"];
|
|
32
|
+
if (contentType) {
|
|
33
|
+
headers["Content-Type"] = contentType;
|
|
34
|
+
}
|
|
35
|
+
if (NEBULAS_API_KEY) {
|
|
36
|
+
headers["x-api-key"] = NEBULAS_API_KEY;
|
|
37
|
+
}
|
|
38
|
+
else if (A2A_API_KEY) {
|
|
39
|
+
headers["x-api-key"] = A2A_API_KEY;
|
|
40
|
+
}
|
|
41
|
+
if (options.headers) {
|
|
42
|
+
Object.assign(headers, options.headers);
|
|
43
|
+
}
|
|
44
|
+
else if (options.getHeaders) {
|
|
45
|
+
try {
|
|
46
|
+
const authHeaders = await options.getHeaders(req);
|
|
47
|
+
Object.assign(headers, authHeaders);
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
if (isRefreshTokenInvalid(err)) {
|
|
51
|
+
return {
|
|
52
|
+
status: 401,
|
|
53
|
+
headers: { "Content-Type": "application/json" },
|
|
54
|
+
body: JSON.stringify({
|
|
55
|
+
error: "Your session has expired. Please log out and log back in.",
|
|
56
|
+
code: "refresh_token_invalid",
|
|
57
|
+
requireReauth: true,
|
|
58
|
+
}),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
if (!A2A_API_KEY && !NEBULAS_API_KEY) {
|
|
62
|
+
return {
|
|
63
|
+
status: 401,
|
|
64
|
+
headers: { "Content-Type": "application/json" },
|
|
65
|
+
body: JSON.stringify({
|
|
66
|
+
error: "Unauthorized",
|
|
67
|
+
hint: "Ensure Auth0 API has 'Allow Offline Access' enabled, MRRT is configured for the A2A audience, and you have logged out and back in to get a refresh token.",
|
|
68
|
+
}),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
console.error("[Nebulas Proxy] Failed to get access token:", err);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const hasAuth = headers["Authorization"] || headers["x-api-key"];
|
|
75
|
+
if (!hasAuth && (A2A_AUDIENCE || options.getHeaders)) {
|
|
76
|
+
return {
|
|
77
|
+
status: 401,
|
|
78
|
+
headers: { "Content-Type": "application/json" },
|
|
79
|
+
body: JSON.stringify({ error: "Unauthorized" }),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
try {
|
|
83
|
+
const res = await fetch(url.toString(), {
|
|
84
|
+
method: req.method,
|
|
85
|
+
headers,
|
|
86
|
+
body: req.method !== "GET" && req.method !== "HEAD"
|
|
87
|
+
? req.body
|
|
88
|
+
: undefined,
|
|
89
|
+
});
|
|
90
|
+
const responseHeaders = {};
|
|
91
|
+
res.headers.forEach((value, key) => {
|
|
92
|
+
if (!["transfer-encoding", "connection", "keep-alive"].includes(key.toLowerCase())) {
|
|
93
|
+
responseHeaders[key] = value;
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
const noContent = res.status === 204 || res.status === 205;
|
|
97
|
+
if (noContent) {
|
|
98
|
+
return {
|
|
99
|
+
status: res.status,
|
|
100
|
+
statusText: res.statusText,
|
|
101
|
+
headers: responseHeaders,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
const resContentType = res.headers.get("content-type") ?? "";
|
|
105
|
+
if (resContentType.includes("text/event-stream") && res.body) {
|
|
106
|
+
return {
|
|
107
|
+
status: res.status,
|
|
108
|
+
statusText: res.statusText,
|
|
109
|
+
headers: responseHeaders,
|
|
110
|
+
body: res.body,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
const responseBody = await res.arrayBuffer();
|
|
114
|
+
return {
|
|
115
|
+
status: res.status,
|
|
116
|
+
statusText: res.statusText,
|
|
117
|
+
headers: responseHeaders,
|
|
118
|
+
body: responseBody,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
console.error("[Nebulas Proxy] Request failed:", error);
|
|
123
|
+
return {
|
|
124
|
+
status: 502,
|
|
125
|
+
headers: { "Content-Type": "application/json" },
|
|
126
|
+
body: JSON.stringify({ error: "Nebulas service unavailable" }),
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
}
|
package/dist/server/nextjs.d.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Use in App Router: app/api/a2a/route.ts
|
|
4
4
|
*/
|
|
5
5
|
import type { NextRequest } from "next/server";
|
|
6
|
+
import { NextResponse } from "next/server";
|
|
6
7
|
import { type ProxyHandlerOptions } from "./proxy-handler.js";
|
|
7
8
|
/**
|
|
8
9
|
* Next.js App Router POST handler for A2A proxy.
|
|
@@ -51,4 +52,29 @@ export declare function createConfigGetHeadersForAuth0(options: {
|
|
|
51
52
|
audience?: string;
|
|
52
53
|
apiKey?: string;
|
|
53
54
|
}): (request: NextRequest) => Promise<Record<string, string>>;
|
|
55
|
+
/**
|
|
56
|
+
* Options for the Nebulas API proxy handler.
|
|
57
|
+
*/
|
|
58
|
+
export interface CreateNebulasProxyHandlerOptions {
|
|
59
|
+
auth0: Auth0ClientForConfig;
|
|
60
|
+
audience?: string;
|
|
61
|
+
apiKey?: string;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Creates a Next.js App Router handler for the Nebulas API proxy (catch-all [...path]).
|
|
65
|
+
* Proxies requests to NEBULAS_API_URL with Auth0 session token or API key.
|
|
66
|
+
*
|
|
67
|
+
* Use in app/api/nebulas/[...path]/route.ts:
|
|
68
|
+
* const handler = createNebulasProxyHandler({ auth0, audience: process.env.AUTH0_A2A_AUDIENCE, apiKey: process.env.NEBULAS_API_KEY });
|
|
69
|
+
* export const GET = handler;
|
|
70
|
+
* export const POST = handler;
|
|
71
|
+
* export const PUT = handler;
|
|
72
|
+
* export const PATCH = handler;
|
|
73
|
+
* export const DELETE = handler;
|
|
74
|
+
*/
|
|
75
|
+
export declare function createNebulasProxyHandler(options: CreateNebulasProxyHandlerOptions): (request: NextRequest, ctx: {
|
|
76
|
+
params: Promise<{
|
|
77
|
+
path: string[];
|
|
78
|
+
}>;
|
|
79
|
+
}) => Promise<NextResponse<unknown>>;
|
|
54
80
|
//# sourceMappingURL=nextjs.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nextjs.d.ts","sourceRoot":"","sources":["../../src/server/nextjs.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"nextjs.d.ts","sourceRoot":"","sources":["../../src/server/nextjs.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAkB,KAAK,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAiB9E;;GAEG;AACH,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,WAAW,EACpB,OAAO,CAAC,EAAE,mBAAmB,qBA6B9B;AAED;;GAEG;AACH,wBAAsB,uBAAuB,CAAC,OAAO,EAAE,WAAW,qBAyBjE;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC;;;OAGG;IACH,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACxE;AAED;;;GAGG;AACH,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,WAAW,EACpB,OAAO,CAAC,EAAE,sBAAsB,qBAcjC;AAED;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACnC,UAAU,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC;QAAE,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;IAC7D,cAAc,CACZ,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,OAAO,EACZ,IAAI,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,GACzB,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC/B;AAED;;;;GAIG;AACH,wBAAgB,8BAA8B,CAAC,OAAO,EAAE;IACtD,KAAK,EAAE,oBAAoB,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,CAAC,OAAO,EAAE,WAAW,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAyC5D;AAED;;GAEG;AACH,MAAM,WAAW,gCAAgC;IAC/C,KAAK,EAAE,oBAAoB,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,gCAAgC,IAE/E,SAAS,WAAW,EACpB,KAAK;IAAE,MAAM,EAAE,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAA;CAAE,oCAkH/C"}
|
package/dist/server/nextjs.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { NextResponse } from "next/server";
|
|
6
6
|
import { handleA2AProxy } from "./proxy-handler.js";
|
|
7
|
+
import { handleNebulasProxy, isRefreshTokenInvalid, } from "./nebulas-proxy-handler.js";
|
|
7
8
|
import { exchangePasswordForToken } from "./auth-handler.js";
|
|
8
9
|
import { createConfigResponse } from "./config-handler.js";
|
|
9
10
|
function headersFromRequest(req) {
|
|
@@ -17,7 +18,8 @@ function headersFromRequest(req) {
|
|
|
17
18
|
* Next.js App Router POST handler for A2A proxy.
|
|
18
19
|
*/
|
|
19
20
|
export async function handleA2ANextJsPOST(request, options) {
|
|
20
|
-
const
|
|
21
|
+
const requestForBody = request.clone();
|
|
22
|
+
const body = await requestForBody.text();
|
|
21
23
|
const proxyReq = {
|
|
22
24
|
method: request.method,
|
|
23
25
|
headers: headersFromRequest(request),
|
|
@@ -123,3 +125,110 @@ export function createConfigGetHeadersForAuth0(options) {
|
|
|
123
125
|
return headers;
|
|
124
126
|
};
|
|
125
127
|
}
|
|
128
|
+
/**
|
|
129
|
+
* Creates a Next.js App Router handler for the Nebulas API proxy (catch-all [...path]).
|
|
130
|
+
* Proxies requests to NEBULAS_API_URL with Auth0 session token or API key.
|
|
131
|
+
*
|
|
132
|
+
* Use in app/api/nebulas/[...path]/route.ts:
|
|
133
|
+
* const handler = createNebulasProxyHandler({ auth0, audience: process.env.AUTH0_A2A_AUDIENCE, apiKey: process.env.NEBULAS_API_KEY });
|
|
134
|
+
* export const GET = handler;
|
|
135
|
+
* export const POST = handler;
|
|
136
|
+
* export const PUT = handler;
|
|
137
|
+
* export const PATCH = handler;
|
|
138
|
+
* export const DELETE = handler;
|
|
139
|
+
*/
|
|
140
|
+
export function createNebulasProxyHandler(options) {
|
|
141
|
+
return async function handler(request, ctx) {
|
|
142
|
+
const { path } = await ctx.params;
|
|
143
|
+
const session = await options.auth0.getSession(request);
|
|
144
|
+
if (!session) {
|
|
145
|
+
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
|
146
|
+
}
|
|
147
|
+
const authResponse = new NextResponse();
|
|
148
|
+
let authHeaders = {};
|
|
149
|
+
if (options.apiKey) {
|
|
150
|
+
authHeaders["x-api-key"] = options.apiKey;
|
|
151
|
+
}
|
|
152
|
+
if (options.audience) {
|
|
153
|
+
try {
|
|
154
|
+
const { token } = await options.auth0.getAccessToken(request, authResponse, { audience: options.audience });
|
|
155
|
+
if (token) {
|
|
156
|
+
authHeaders["Authorization"] = `Bearer ${token}`;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
catch (err) {
|
|
160
|
+
if (isRefreshTokenInvalid(err)) {
|
|
161
|
+
return NextResponse.json({
|
|
162
|
+
error: "Your session has expired. Please log out and log back in.",
|
|
163
|
+
code: "refresh_token_invalid",
|
|
164
|
+
requireReauth: true,
|
|
165
|
+
}, { status: 401 });
|
|
166
|
+
}
|
|
167
|
+
if (!options.apiKey) {
|
|
168
|
+
return NextResponse.json({
|
|
169
|
+
error: "Unauthorized",
|
|
170
|
+
hint: "Ensure Auth0 API has 'Allow Offline Access' enabled, MRRT is configured for the A2A audience, and you have logged out and back in to get a refresh token.",
|
|
171
|
+
}, { status: 401 });
|
|
172
|
+
}
|
|
173
|
+
console.error("[Nebulas Proxy] Failed to get access token:", err);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
const searchParams = {};
|
|
177
|
+
request.nextUrl.searchParams.forEach((value, key) => {
|
|
178
|
+
searchParams[key] = value;
|
|
179
|
+
});
|
|
180
|
+
let bodyForFetch;
|
|
181
|
+
if (request.method !== "GET" && request.method !== "HEAD") {
|
|
182
|
+
try {
|
|
183
|
+
const text = await request.text();
|
|
184
|
+
bodyForFetch = text.length > 0 ? text : undefined;
|
|
185
|
+
}
|
|
186
|
+
catch {
|
|
187
|
+
return NextResponse.json({ error: "Invalid request body" }, { status: 400 });
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
const proxyReq = {
|
|
191
|
+
method: request.method,
|
|
192
|
+
path,
|
|
193
|
+
headers: headersFromRequest(request),
|
|
194
|
+
body: bodyForFetch,
|
|
195
|
+
searchParams,
|
|
196
|
+
};
|
|
197
|
+
const result = await handleNebulasProxy(proxyReq, { headers: authHeaders });
|
|
198
|
+
const responseHeaders = new Headers();
|
|
199
|
+
for (const [k, v] of Object.entries(result.headers)) {
|
|
200
|
+
responseHeaders.set(k, v);
|
|
201
|
+
}
|
|
202
|
+
authResponse.headers.forEach((value, key) => {
|
|
203
|
+
if (key.toLowerCase() === "set-cookie") {
|
|
204
|
+
responseHeaders.append(key, value);
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
if (typeof result.body === "string") {
|
|
208
|
+
return new NextResponse(result.body, {
|
|
209
|
+
status: result.status,
|
|
210
|
+
statusText: result.statusText,
|
|
211
|
+
headers: responseHeaders,
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
if (result.body instanceof ReadableStream) {
|
|
215
|
+
return new NextResponse(result.body, {
|
|
216
|
+
status: result.status,
|
|
217
|
+
statusText: result.statusText,
|
|
218
|
+
headers: responseHeaders,
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
if (result.body instanceof ArrayBuffer) {
|
|
222
|
+
return new NextResponse(result.body, {
|
|
223
|
+
status: result.status,
|
|
224
|
+
statusText: result.statusText,
|
|
225
|
+
headers: responseHeaders,
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
return new NextResponse(null, {
|
|
229
|
+
status: result.status,
|
|
230
|
+
statusText: result.statusText,
|
|
231
|
+
headers: responseHeaders,
|
|
232
|
+
});
|
|
233
|
+
};
|
|
234
|
+
}
|