lyzr-cortex-sdk 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.
- package/README.md +445 -0
- package/dist/index.d.mts +438 -0
- package/dist/index.d.ts +438 -0
- package/dist/index.js +445 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +434 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +59 -0
package/README.md
ADDED
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
# Cortex SDK for TypeScript
|
|
2
|
+
|
|
3
|
+
Build tools that integrate with the Cortex Platform for bidirectional knowledge flow.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @cortex/sdk
|
|
9
|
+
# or
|
|
10
|
+
yarn add @cortex/sdk
|
|
11
|
+
# or
|
|
12
|
+
pnpm add @cortex/sdk
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Quick Start
|
|
16
|
+
|
|
17
|
+
### React Integration
|
|
18
|
+
|
|
19
|
+
```tsx
|
|
20
|
+
import { CortexProvider, useCortex } from '@cortex/sdk';
|
|
21
|
+
|
|
22
|
+
// 1. Wrap your app with CortexProvider
|
|
23
|
+
function App() {
|
|
24
|
+
return (
|
|
25
|
+
<CortexProvider
|
|
26
|
+
apiUrl={process.env.REACT_APP_CORTEX_API_URL}
|
|
27
|
+
apiKey={process.env.REACT_APP_CORTEX_API_KEY}
|
|
28
|
+
>
|
|
29
|
+
<YourApp />
|
|
30
|
+
</CortexProvider>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// 2. Use the hook in your components
|
|
35
|
+
function MeetingCard({ meeting }) {
|
|
36
|
+
const { isEmbedded, openChat, showNotification, push } = useCortex();
|
|
37
|
+
|
|
38
|
+
const handleSave = async () => {
|
|
39
|
+
await push({
|
|
40
|
+
id: meeting.id,
|
|
41
|
+
type: 'meeting_transcript',
|
|
42
|
+
content: meeting.transcript,
|
|
43
|
+
name: meeting.title,
|
|
44
|
+
scope: 'team',
|
|
45
|
+
});
|
|
46
|
+
showNotification('Saved to Cortex', 'success');
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<div>
|
|
51
|
+
<h3>{meeting.title}</h3>
|
|
52
|
+
<button onClick={handleSave}>Save to Cortex</button>
|
|
53
|
+
|
|
54
|
+
{/* Only show when embedded in Cortex */}
|
|
55
|
+
{isEmbedded && (
|
|
56
|
+
<button onClick={() => openChat(`What action items from "${meeting.title}"?`)}>
|
|
57
|
+
Ask Cortex
|
|
58
|
+
</button>
|
|
59
|
+
)}
|
|
60
|
+
</div>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Direct API Client (without React)
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
import { CortexClient } from '@cortex/sdk';
|
|
69
|
+
|
|
70
|
+
const client = new CortexClient({
|
|
71
|
+
apiUrl: 'https://api.cortex.ai',
|
|
72
|
+
apiKey: 'your-api-key',
|
|
73
|
+
toolId: 'your-tool',
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// Push a document
|
|
77
|
+
await client.push({
|
|
78
|
+
id: 'doc_123',
|
|
79
|
+
type: 'meeting_transcript',
|
|
80
|
+
content: 'Full transcript...',
|
|
81
|
+
name: 'Product Sync',
|
|
82
|
+
scope: 'team',
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// Query
|
|
86
|
+
const result = await client.query('What decisions were made about Q1?');
|
|
87
|
+
console.log(result.answer);
|
|
88
|
+
console.log(result.sources);
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### PostMessage Bridge (Low-level)
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
import { CortexBridge, isEmbeddedInCortex } from '@cortex/sdk';
|
|
95
|
+
|
|
96
|
+
// Check if embedded
|
|
97
|
+
if (isEmbeddedInCortex()) {
|
|
98
|
+
const bridge = new CortexBridge();
|
|
99
|
+
|
|
100
|
+
// Wait for initialization
|
|
101
|
+
const user = await bridge.waitForReady();
|
|
102
|
+
console.log('Cortex user:', user?.email);
|
|
103
|
+
|
|
104
|
+
// Send messages
|
|
105
|
+
bridge.openChat('Hello from my tool!');
|
|
106
|
+
bridge.showNotification('Tool loaded', 'success');
|
|
107
|
+
|
|
108
|
+
// Listen for messages
|
|
109
|
+
bridge.on('CORTEX_THEME', (payload) => {
|
|
110
|
+
document.body.className = payload.theme;
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Frontend Integration Guide
|
|
116
|
+
|
|
117
|
+
Step-by-step guide for integrating Cortex into your React/TypeScript frontend.
|
|
118
|
+
|
|
119
|
+
### 1. Create Cortex Provider
|
|
120
|
+
|
|
121
|
+
File: `frontend/lib/cortex.tsx`
|
|
122
|
+
```tsx
|
|
123
|
+
'use client';
|
|
124
|
+
|
|
125
|
+
import { createContext, useContext, useEffect, useState, useCallback, useMemo, ReactNode } from 'react';
|
|
126
|
+
|
|
127
|
+
interface CortexUser {
|
|
128
|
+
id: string;
|
|
129
|
+
email: string;
|
|
130
|
+
name?: string;
|
|
131
|
+
orgId: string;
|
|
132
|
+
teams: Array<{ id: string; name: string }>;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
interface CortexContextValue {
|
|
136
|
+
user: CortexUser | null;
|
|
137
|
+
isEmbedded: boolean;
|
|
138
|
+
isReady: boolean;
|
|
139
|
+
theme: 'light' | 'dark';
|
|
140
|
+
openChat: (query?: string) => void;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const CortexContext = createContext<CortexContextValue | null>(null);
|
|
144
|
+
|
|
145
|
+
function isInIframe(): boolean {
|
|
146
|
+
if (typeof window === 'undefined') return false;
|
|
147
|
+
try {
|
|
148
|
+
return window.self !== window.top;
|
|
149
|
+
} catch {
|
|
150
|
+
return true;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export function CortexProvider({ children }: { children: ReactNode }) {
|
|
155
|
+
const [isEmbedded] = useState(() => isInIframe());
|
|
156
|
+
const [isReady, setIsReady] = useState(!isInIframe());
|
|
157
|
+
const [user, setUser] = useState<CortexUser | null>(null);
|
|
158
|
+
const [theme, setTheme] = useState<'light' | 'dark'>('light');
|
|
159
|
+
|
|
160
|
+
useEffect(() => {
|
|
161
|
+
if (!isEmbedded) return;
|
|
162
|
+
|
|
163
|
+
const handleMessage = (event: MessageEvent) => {
|
|
164
|
+
const message = event.data;
|
|
165
|
+
if (!message?.type) return;
|
|
166
|
+
|
|
167
|
+
if (message.type === 'CORTEX_INIT') {
|
|
168
|
+
setUser(message.payload?.user || null);
|
|
169
|
+
setTheme(message.payload?.theme || 'light');
|
|
170
|
+
setIsReady(true);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (message.type === 'CORTEX_THEME') {
|
|
174
|
+
setTheme(message.payload?.theme || 'light');
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
window.addEventListener('message', handleMessage);
|
|
179
|
+
window.parent.postMessage({ type: 'TOOL_READY' }, '*');
|
|
180
|
+
|
|
181
|
+
const timeout = setTimeout(() => setIsReady(true), 5000);
|
|
182
|
+
|
|
183
|
+
return () => {
|
|
184
|
+
window.removeEventListener('message', handleMessage);
|
|
185
|
+
clearTimeout(timeout);
|
|
186
|
+
};
|
|
187
|
+
}, [isEmbedded]);
|
|
188
|
+
|
|
189
|
+
const openChat = useCallback((query?: string) => {
|
|
190
|
+
if (isEmbedded) {
|
|
191
|
+
window.parent.postMessage({ type: 'OPEN_CHAT', payload: { query } }, '*');
|
|
192
|
+
}
|
|
193
|
+
}, [isEmbedded]);
|
|
194
|
+
|
|
195
|
+
const value = useMemo(() => ({
|
|
196
|
+
user, isEmbedded, isReady, theme, openChat
|
|
197
|
+
}), [user, isEmbedded, isReady, theme, openChat]);
|
|
198
|
+
|
|
199
|
+
return (
|
|
200
|
+
<CortexContext.Provider value={value}>
|
|
201
|
+
{children}
|
|
202
|
+
</CortexContext.Provider>
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export function useCortex(): CortexContextValue {
|
|
207
|
+
const context = useContext(CortexContext);
|
|
208
|
+
if (!context) {
|
|
209
|
+
return {
|
|
210
|
+
user: null,
|
|
211
|
+
isEmbedded: false,
|
|
212
|
+
isReady: true,
|
|
213
|
+
theme: 'light',
|
|
214
|
+
openChat: () => {},
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
return context;
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### 2. Wrap App with Provider
|
|
222
|
+
|
|
223
|
+
File: `frontend/app/providers.tsx`
|
|
224
|
+
```tsx
|
|
225
|
+
'use client';
|
|
226
|
+
|
|
227
|
+
import { CortexProvider } from '@/lib/cortex';
|
|
228
|
+
|
|
229
|
+
export function Providers({ children }: { children: React.ReactNode }) {
|
|
230
|
+
return <CortexProvider>{children}</CortexProvider>;
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
File: `frontend/app/layout.tsx`
|
|
235
|
+
```tsx
|
|
236
|
+
import { Providers } from './providers';
|
|
237
|
+
|
|
238
|
+
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
239
|
+
return (
|
|
240
|
+
<html lang="en">
|
|
241
|
+
<body>
|
|
242
|
+
<Providers>{children}</Providers>
|
|
243
|
+
</body>
|
|
244
|
+
</html>
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### 3. Use in Components
|
|
250
|
+
|
|
251
|
+
```tsx
|
|
252
|
+
import { useCortex } from '@/lib/cortex';
|
|
253
|
+
|
|
254
|
+
function TranscriptCard({ meeting }) {
|
|
255
|
+
const { isEmbedded, openChat } = useCortex();
|
|
256
|
+
|
|
257
|
+
return (
|
|
258
|
+
<div>
|
|
259
|
+
<h3>{meeting.title}</h3>
|
|
260
|
+
<p>{meeting.transcript}</p>
|
|
261
|
+
|
|
262
|
+
{isEmbedded && (
|
|
263
|
+
<button onClick={() => openChat(`Summarize meeting: ${meeting.title}`)}>
|
|
264
|
+
Ask Cortex
|
|
265
|
+
</button>
|
|
266
|
+
)}
|
|
267
|
+
</div>
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
### 4. Embedded Mode Authentication
|
|
273
|
+
|
|
274
|
+
When your tool is embedded in Cortex, the frontend receives the user's OGI JWT via `CORTEX_INIT` postMessage. Always send **both** custom headers and the standard `Authorization` header to survive production reverse proxies:
|
|
275
|
+
|
|
276
|
+
```typescript
|
|
277
|
+
// In your tool's frontend
|
|
278
|
+
const headers = {
|
|
279
|
+
'X-Cortex-Embedded': 'true',
|
|
280
|
+
'X-Cortex-User-Email': user.email,
|
|
281
|
+
'Authorization': `Bearer ${ogiJwtToken}`, // Standard header — proxy-safe
|
|
282
|
+
};
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
Your tool's backend should accept both modes:
|
|
286
|
+
1. Custom `X-Cortex-Embedded` + `X-Cortex-User-Email` headers (fast path, works locally)
|
|
287
|
+
2. `Authorization: Bearer {ogiJWT}` decoded with `CORTEX_JWT_PUBLIC_KEY` (works through all proxies)
|
|
288
|
+
|
|
289
|
+
> **Production Note:** Reverse proxies, CDNs, and load balancers may strip custom `X-Cortex-*` headers. Always send the OGI JWT as `Authorization: Bearer` alongside custom headers. The tool backend should accept both auth modes.
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
## API Reference
|
|
294
|
+
|
|
295
|
+
### CortexProvider
|
|
296
|
+
|
|
297
|
+
React context provider for Cortex integration.
|
|
298
|
+
|
|
299
|
+
```tsx
|
|
300
|
+
<CortexProvider
|
|
301
|
+
apiUrl="https://api.cortex.ai" // Cortex gateway URL
|
|
302
|
+
apiKey="your-api-key" // Tool's API key
|
|
303
|
+
toolId="your-tool" // Tool identifier (optional)
|
|
304
|
+
debug={false} // Enable debug logging (optional)
|
|
305
|
+
>
|
|
306
|
+
{children}
|
|
307
|
+
</CortexProvider>
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### useCortex()
|
|
311
|
+
|
|
312
|
+
React hook returning Cortex context:
|
|
313
|
+
|
|
314
|
+
```typescript
|
|
315
|
+
interface CortexContextValue {
|
|
316
|
+
user: CortexUser | null; // Current user (null if standalone)
|
|
317
|
+
isEmbedded: boolean; // Whether in Cortex iframe
|
|
318
|
+
isReady: boolean; // Whether SDK is initialized
|
|
319
|
+
theme: 'light' | 'dark'; // Current theme
|
|
320
|
+
|
|
321
|
+
push(doc): Promise<void>; // Push document to Knowledge Graph
|
|
322
|
+
query(question, options?): Promise<CortexQueryResult>; // Query
|
|
323
|
+
openChat(query?): void; // Open Cortex chat
|
|
324
|
+
showNotification(message, type?): void; // Show notification
|
|
325
|
+
}
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
### Additional Hooks
|
|
329
|
+
|
|
330
|
+
```typescript
|
|
331
|
+
// More efficient if you only need specific values
|
|
332
|
+
const isEmbedded = useIsEmbedded();
|
|
333
|
+
const user = useCortexUser();
|
|
334
|
+
const theme = useCortexTheme();
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
### CortexClient
|
|
338
|
+
|
|
339
|
+
HTTP client for direct API calls:
|
|
340
|
+
|
|
341
|
+
```typescript
|
|
342
|
+
const client = new CortexClient(config);
|
|
343
|
+
|
|
344
|
+
// Check if configured
|
|
345
|
+
client.isConfigured; // boolean
|
|
346
|
+
|
|
347
|
+
// Methods
|
|
348
|
+
await client.push(document);
|
|
349
|
+
await client.query(question, options);
|
|
350
|
+
await client.getUserContext(email);
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
### CortexBridge
|
|
354
|
+
|
|
355
|
+
Low-level postMessage bridge:
|
|
356
|
+
|
|
357
|
+
```typescript
|
|
358
|
+
const bridge = new CortexBridge();
|
|
359
|
+
|
|
360
|
+
// Properties
|
|
361
|
+
bridge.isEmbedded; // boolean
|
|
362
|
+
bridge.isReady; // boolean
|
|
363
|
+
bridge.user; // CortexUser | null
|
|
364
|
+
bridge.theme; // 'light' | 'dark'
|
|
365
|
+
|
|
366
|
+
// Methods
|
|
367
|
+
await bridge.waitForReady();
|
|
368
|
+
bridge.openChat(query?);
|
|
369
|
+
bridge.showNotification(message, type);
|
|
370
|
+
bridge.navigate(path);
|
|
371
|
+
bridge.on(messageType, handler);
|
|
372
|
+
bridge.destroy();
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
## Types
|
|
376
|
+
|
|
377
|
+
```typescript
|
|
378
|
+
interface CortexUser {
|
|
379
|
+
id: string;
|
|
380
|
+
email: string;
|
|
381
|
+
name?: string;
|
|
382
|
+
orgId: string;
|
|
383
|
+
orgName?: string;
|
|
384
|
+
teams: Array<{ id: string; name: string }>;
|
|
385
|
+
role: string;
|
|
386
|
+
permissions: string[];
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
interface CortexDocument {
|
|
390
|
+
id: string; // External ID
|
|
391
|
+
type: string; // Document type
|
|
392
|
+
content: string; // Text content
|
|
393
|
+
name?: string; // Display name
|
|
394
|
+
externalUrl?: string;
|
|
395
|
+
scope?: 'global' | 'team' | 'personal';
|
|
396
|
+
teamIds?: string[];
|
|
397
|
+
metadata?: Record<string, unknown>;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
interface CortexQueryResult {
|
|
401
|
+
answer: string;
|
|
402
|
+
sources: CortexSource[];
|
|
403
|
+
query: string;
|
|
404
|
+
}
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
## PostMessage Protocol
|
|
408
|
+
|
|
409
|
+
When embedded in Cortex, the SDK uses postMessage for communication:
|
|
410
|
+
|
|
411
|
+
| Direction | Message Type | Payload |
|
|
412
|
+
|-----------|--------------|---------|
|
|
413
|
+
| Tool → Cortex | `TOOL_READY` | - |
|
|
414
|
+
| Cortex → Tool | `CORTEX_INIT` | `{ user, theme }` |
|
|
415
|
+
| Cortex → Tool | `CORTEX_THEME` | `{ theme }` |
|
|
416
|
+
| Tool → Cortex | `OPEN_CHAT` | `{ query? }` |
|
|
417
|
+
| Tool → Cortex | `SHOW_NOTIFICATION` | `{ message, type }` |
|
|
418
|
+
| Tool → Cortex | `NAVIGATE` | `{ path }` |
|
|
419
|
+
|
|
420
|
+
## Graceful Degradation
|
|
421
|
+
|
|
422
|
+
The SDK works in both embedded and standalone modes:
|
|
423
|
+
|
|
424
|
+
```tsx
|
|
425
|
+
function MyComponent() {
|
|
426
|
+
const { isEmbedded, push, openChat } = useCortex();
|
|
427
|
+
|
|
428
|
+
// Push always works (no-op if not configured)
|
|
429
|
+
await push({ id: '1', type: 'doc', content: '...' });
|
|
430
|
+
|
|
431
|
+
// openChat is a no-op if not embedded
|
|
432
|
+
openChat('Hello');
|
|
433
|
+
|
|
434
|
+
// Conditionally render embedded-only features
|
|
435
|
+
return (
|
|
436
|
+
<div>
|
|
437
|
+
{isEmbedded && <CortexOnlyFeature />}
|
|
438
|
+
</div>
|
|
439
|
+
);
|
|
440
|
+
}
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
## License
|
|
444
|
+
|
|
445
|
+
MIT
|