@groo.dev/cortex-sdk
v1.1.4
Published
React widget components and Hono proxy middleware for integrating Cortex AI agents.
Readme
@groo.dev/cortex-sdk
Embed AI-powered support into any React application. Users submit tickets, chat with AI agents, and get real-time help — all through an embeddable widget.
Pairs with a Hono route handler that keeps your API key server-side, handles auth, and injects user identity automatically.
Install
npm install @groo.dev/cortex-sdkQuick Start
1. Add the proxy route to your Hono API worker
import { cortexRoute } from '@groo.dev/cortex-sdk/hono'
app.route('/api/@groo.dev/cortex', cortexRoute({
getApiKey: (env) => env.CORTEX_API_KEY,
getUser: async (c) => {
const session = await getSession(c) // your auth
if (!session) return null
return { id: session.user.id, name: session.user.name, email: session.user.email }
},
}))2. Add a widget to your React app
import { CortexFloating } from '@groo.dev/cortex-sdk/react'
function App() {
return (
<>
{/* your app */}
<CortexFloating />
</>
)
}A floating button appears in the bottom-right corner. Users can submit tickets, reply to agents, and track their submissions.
Proxy Route
cortexRoute() creates a complete Hono sub-app that handles auth, user identity injection, and request forwarding.
import { cortexRoute } from '@groo.dev/cortex-sdk/hono'
app.route('/api/@groo.dev/cortex', cortexRoute({
apiBase: 'https://cortex.miranet.work/v1/sdk',
getApiKey: (env) => env.CORTEX_API_KEY,
getUser: async (c) => { ... },
}))| Option | Type | Description |
|--------|------|-------------|
| apiBase | string | Cortex API URL (defaults to https://cortex.miranet.work/v1/sdk) |
| getApiKey | (env) => string | Extracts API key from worker env |
| getUser | (c) => Promise<User \| null> | Extracts authenticated user from request. Return null to reject. |
The User object must have id, name, and email fields.
Routes handled
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | /health | No | Health check |
| POST | /tickets | Yes | Submit ticket |
| GET | /tickets | Yes | List user's own tickets |
| GET | /tickets/:id | Yes | Ticket detail with comments |
| POST | /tickets/:id/comments | Yes | Reply to a ticket |
| POST | /attachments | Yes | Upload file attachment |
| GET | /attachments/:id | Yes | Download attachment |
| POST | /agents/:agentId/chat | Yes | Send message, receive streamed response |
| GET | /agents/:agentId/conversations | Yes | List user's conversations |
| GET | /agents/:agentId/conversations/:id | Yes | Get conversation messages |
React Components
All components accept an optional apiBase prop and prefill for pre-populating the form.
<CortexFloating />
Fixed floating button in the bottom-right corner. Drop it in your root layout.
<CortexFloating /><CortexButton />
Inline button you place anywhere — nav bar, help menu, footer.
<CortexButton className="btn btn-secondary">
Get Help
</CortexButton><CortexContextual />
Controlled modal for programmatic triggers (error boundaries, catch blocks).
<CortexContextual
prefill={{
type: 'bug',
title: `Error on ${window.location.pathname}`,
description: error.message,
}}
open={open}
onClose={() => setOpen(false)}
/><CortexModal />
Headless controlled modal.
<CortexModal open={open} onOpenChange={setOpen} />Auth Examples
Clerk
import { getAuth, clerkClient } from '@clerk/backend'
app.route('/api/@groo.dev/cortex', cortexRoute({
getApiKey: (env) => env.CORTEX_API_KEY,
getUser: async (c) => {
const auth = getAuth(c)
if (!auth.userId) return null
const user = await clerkClient(c.env).users.getUser(auth.userId)
return {
id: auth.userId,
name: [user.firstName, user.lastName].filter(Boolean).join(' '),
email: user.emailAddresses[0]?.emailAddress ?? '',
}
},
}))Better Auth
import { createAuth } from './auth'
app.route('/api/@groo.dev/cortex', cortexRoute({
getApiKey: (env) => env.CORTEX_API_KEY,
getUser: async (c) => {
const auth = createAuth(c.env)
const session = await auth.api.getSession({ headers: c.req.raw.headers })
if (!session) return null
return { id: session.user.id, name: session.user.name, email: session.user.email }
},
}))Theming
The widget uses CSS custom properties with a --ctx- prefix. Override in your CSS:
:root {
--ctx-accent: #6366f1;
--ctx-bg: #0f0f12;
--ctx-fontBody: 'Inter', sans-serif;
}License
MIT
