npm package: @gnomondigital/nebulas-kit-core
Description: Framework-agnostic A2A chat client, Nebulas client, and server handlers.
Latest version: 0.4.1

# @gnomondigital/nebulas-kit-core

Framework-agnostic A2A chat client, Nebulas client, and server handlers.

## Install

```bash
npm install @gnomondigital/nebulas-kit-core
```

### Subpath exports

| Import path | Use |
|-------------|-----|
| `@gnomondigital/nebulas-kit-core` | Full API (Next.js, Express, proxy handlers). |
| `@gnomondigital/nebulas-kit-core/client` | Browser-only: `sendMessageStream`, Nebulas client, types. **Use from React/Vite** so `next/server` is never bundled. |
| `@gnomondigital/nebulas-kit-core/express` | Express router only; no Next.js. |

`@gnomondigital/nebulas-kit-react` imports from `/client` internally.

## Environment Variables

| Variable | Required | Description |
|----------|----------|-------------|
| `A2A_URL` | Yes | A2A agent base URL |
| `A2A_AUTH_STRATEGY` | Yes | `session` \| `client_credentials` \| `resource_owner_password` \| `api_key_only` |
| `A2A_API_KEY` | For api_key_only, or fallback | x-api-key header |
| `AUTH0_DOMAIN` | For Auth0 strategies | Auth0 tenant domain |
| `AUTH0_CLIENT_ID` | For client_credentials, ROP | Auth0 client ID |
| `AUTH0_CLIENT_SECRET` | For client_credentials, ROP | Auth0 client secret |
| `AUTH0_A2A_AUDIENCE` | For Auth0 strategies | API audience |
| `AUTH0_ROP_USERNAME` | For env ROP (`createA2AProxyHandlerFromEnvROP`) | Service user username (server secret) |
| `AUTH0_ROP_PASSWORD` | For env ROP | Service user password (server secret) |
| `NEBULAS_API_URL` | For Nebulas | Conversation API base URL |
| `NEBULAS_API_KEY` | For Nebulas proxy | x-api-key fallback when no session token |
| `NEBULAS_PROJECT_ID` | For Nebulas | Project ID |

## A2A proxy auth modes (Next.js)

| Mode | When to use | Exports |
|------|-------------|---------|
| **Auth0 session** | User signs in with `@auth0/nextjs-auth0`; access token for `AUTH0_A2A_AUDIENCE` comes from the session. | `createA2AProxyHandler`, `createGetHeadersForAuth0`, `handleA2AAuthNextJsPOST` (body ROP, optional) |
| **Env ROP** | No browser Auth0; a fixed **service user** in env performs Resource Owner Password; token is cached per process. | `createA2AProxyHandlerFromEnvROP`, `createGetHeadersFromEnvROP`, `getCachedAccessTokenFromEnvROP` |

Env ROP example:

```ts
// app/api/a2a/route.ts
import { createA2AProxyHandlerFromEnvROP } from "@gnomondigital/nebulas-kit-core";

export const POST = createA2AProxyHandlerFromEnvROP({
  apiKey: process.env.A2A_API_KEY,
});
```

```ts
// app/api/a2a/config/route.ts
import type { NextRequest } from "next/server";
import {
  handleA2AConfigNextJsGET,
  createGetHeadersFromEnvROP,
} from "@gnomondigital/nebulas-kit-core";

const getHeaders = createGetHeadersFromEnvROP({
  apiKey: process.env.A2A_API_KEY,
});

export async function GET(request: NextRequest) {
  return handleA2AConfigNextJsGET(request, { getHeaders });
}
```

See `apps/demo_rop` in the monorepo for a full app using env ROP.

## Usage

### Client: sendMessageStream

```ts
import { sendMessageStream } from "@gnomondigital/nebulas-kit-core";

const content = await sendMessageStream(
  "Hello",
  (chunk) => console.log(chunk),
  signal,
  files,
  true,
  sessionId,
  { endpoint: "/api/a2a", getHeaders: async () => ({ Authorization: `Bearer ${token}` }) }
);
```

### Next.js App Router

```ts
// app/api/a2a/route.ts
import { handleA2ANextJsPOST } from "@nebulas-kit/core";

export async function POST(req: NextRequest) {
  return handleA2ANextJsPOST(req);
}
```

```ts
// app/api/a2a/auth/route.ts
import { handleA2AAuthNextJsPOST } from "@gnomondigital/nebulas-kit-core";

export async function POST(req: NextRequest) {
  return handleA2AAuthNextJsPOST(req);
}
```

```ts
// app/api/a2a/config/route.ts
import { handleA2AConfigNextJsGET } from "@gnomondigital/nebulas-kit-core";

export async function GET() {
  return handleA2AConfigNextJsGET();
}
```

```ts
// app/api/nebulas/[...path]/route.ts - proxies to Nebulas API (conversations, chat_messages)
import { createNebulasProxyHandler } from "@gnomondigital/nebulas-kit-core";
import { auth0 } from "@/lib/auth";

const handler = createNebulasProxyHandler({
  auth0,
  audience: process.env.AUTH0_A2A_AUDIENCE,
  apiKey: process.env.NEBULAS_API_KEY || process.env.A2A_API_KEY,
});

export const GET = handler;
export const POST = handler;
export const PUT = handler;
export const PATCH = handler;
export const DELETE = handler;
```

### Express

```ts
import express from "express";
import { createA2AExpressRouter } from "@gnomondigital/nebulas-kit-core/express";

const app = express();
app.use(express.json());
app.use("/api/a2a", createA2AExpressRouter(express));
app.listen(3000);
```

Use **`@gnomondigital/nebulas-kit-core/express`** so the bundle does not load Next.js adapters (`next/server`).

**Env ROP** (service user in `AUTH0_ROP_USERNAME` / `AUTH0_ROP_PASSWORD`, same as Next.js `createA2AProxyHandlerFromEnvROP`):

```ts
app.use(
  "/api/a2a",
  createA2AExpressRouter(express, {
    useEnvRop: true,
    envRopApiKey: process.env.A2A_API_KEY,
  })
);
```

See `apps/demo_express_rop` in the monorepo for a full Express + Vite example.