@trainly/react 1.0.2 → 1.1.0

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 CHANGED
@@ -1,10 +1,94 @@
1
1
  # @trainly/react
2
2
 
3
- **Dead simple RAG integration for React apps**
3
+ **Dead simple RAG integration for React apps with V1 OAuth Authentication**
4
4
 
5
- Go from `npm install` to working AI in under 5 minutes. No backend required, no complex setup, just install and use.
5
+ Go from `npm install` to working AI in under 5 minutes. Now supports direct OAuth integration with **permanent user subchats** and complete privacy protection.
6
6
 
7
- ## 🚀 Quick Start
7
+ ## 🆕 **NEW: V1 Trusted Issuer Authentication**
8
+
9
+ Use your existing OAuth provider (Clerk, Auth0, Cognito) directly with Trainly! Users get permanent private workspaces, and developers never see raw files or queries.
10
+
11
+ ### V1 Quick Start
12
+
13
+ #### 1. Install
14
+
15
+ ```bash
16
+ npm install @trainly/react
17
+ ```
18
+
19
+ #### 2. Register Your OAuth App (One-time)
20
+
21
+ ```bash
22
+ curl -X POST "http://localhost:8000/v1/console/apps/register" \
23
+ -H "X-Admin-Token: admin_dev_token_123" \
24
+ -F "app_name=My App" \
25
+ -F "issuer=https://clerk.myapp.com" \
26
+ -F 'allowed_audiences=["my-clerk-frontend-api"]'
27
+ ```
28
+
29
+ Save the `app_id` from the response!
30
+
31
+ #### 3. Setup with V1 (Clerk Example)
32
+
33
+ ```tsx
34
+ // app/layout.tsx
35
+ import { ClerkProvider } from "@clerk/nextjs";
36
+ import { TrainlyProvider } from "@trainly/react";
37
+
38
+ export default function RootLayout({ children }) {
39
+ return (
40
+ <html>
41
+ <body>
42
+ <ClerkProvider>
43
+ <TrainlyProvider appId="your_app_id_from_step_2">
44
+ {children}
45
+ </TrainlyProvider>
46
+ </ClerkProvider>
47
+ </body>
48
+ </html>
49
+ );
50
+ }
51
+ ```
52
+
53
+ #### 4. Use with OAuth Authentication
54
+
55
+ ```tsx
56
+ // Any component
57
+ import { useAuth } from "@clerk/nextjs";
58
+ import { useTrainly } from "@trainly/react";
59
+
60
+ function MyComponent() {
61
+ const { getToken } = useAuth();
62
+ const { ask, connectWithOAuthToken } = useTrainly();
63
+
64
+ React.useEffect(() => {
65
+ async function setupTrainly() {
66
+ const idToken = await getToken();
67
+ await connectWithOAuthToken(idToken);
68
+ }
69
+ setupTrainly();
70
+ }, []);
71
+
72
+ const handleClick = async () => {
73
+ const answer = await ask("What files do I have?");
74
+ console.log(answer); // AI response from user's permanent private subchat!
75
+ };
76
+
77
+ return <button onClick={handleClick}>Ask My AI</button>;
78
+ }
79
+ ```
80
+
81
+ ## 🔒 **V1 Benefits**
82
+
83
+ - ✅ **Permanent User Data**: Same user = same private subchat forever
84
+ - ✅ **Complete Privacy**: Developer never sees user files or queries
85
+ - ✅ **Any OAuth Provider**: Clerk, Auth0, Cognito, Firebase, custom OIDC
86
+ - ✅ **Zero Migration**: Works with your existing OAuth setup
87
+ - ✅ **Simple Integration**: Just add `appId` and use `connectWithOAuthToken()`
88
+
89
+ ---
90
+
91
+ ## 🚀 Original Quick Start (Legacy)
8
92
 
9
93
  ### 1. Install
10
94
 
@@ -111,20 +195,45 @@ function App() {
111
195
  ### Authentication Modes
112
196
 
113
197
  ```tsx
114
- // Mode 1: App Secret (recommended for multi-user apps)
198
+ // Mode 1: V1 Trusted Issuer (NEW - recommended for OAuth apps)
199
+ <TrainlyProvider appId="app_v1_12345" /> // Register via console API first
200
+
201
+ // Mode 2: App Secret (legacy - for multi-user apps)
115
202
  <TrainlyProvider appSecret="as_secret_123" />
116
203
 
117
- // Mode 2: With user context
204
+ // Mode 3: With user context (legacy)
118
205
  <TrainlyProvider
119
206
  appSecret="as_secret_123"
120
207
  userId="user_123"
121
208
  userEmail="user@example.com"
122
209
  />
123
210
 
124
- // Mode 3: Direct API key (simple apps)
211
+ // Mode 4: Direct API key (legacy - simple apps)
125
212
  <TrainlyProvider apiKey="tk_chat_id_key" />
126
213
  ```
127
214
 
215
+ ### V1 OAuth Provider Examples
216
+
217
+ ```tsx
218
+ // With Clerk
219
+ <TrainlyProvider
220
+ appId="app_v1_clerk_123"
221
+ baseUrl="https://api.trainly.com"
222
+ />
223
+
224
+ // With Auth0
225
+ <TrainlyProvider
226
+ appId="app_v1_auth0_456"
227
+ baseUrl="https://api.trainly.com"
228
+ />
229
+
230
+ // With AWS Cognito
231
+ <TrainlyProvider
232
+ appId="app_v1_cognito_789"
233
+ baseUrl="https://api.trainly.com"
234
+ />
235
+ ```
236
+
128
237
  ### Component Customization
129
238
 
130
239
  ```tsx
@@ -177,6 +286,9 @@ const {
177
286
  askWithCitations: (question: string) => Promise<{answer: string, citations: Citation[]}>,
178
287
  upload: (file: File) => Promise<UploadResult>,
179
288
 
289
+ // NEW: V1 Authentication
290
+ connectWithOAuthToken: (idToken: string) => Promise<void>,
291
+
180
292
  // State
181
293
  isLoading: boolean,
182
294
  isConnected: boolean,
@@ -198,22 +310,31 @@ const {
198
310
  ```tsx
199
311
  interface TrainlyProviderProps {
200
312
  children: React.ReactNode;
201
- appSecret?: string; // App secret from Trainly dashboard
202
- apiKey?: string; // Direct API key (alternative to appSecret)
313
+ appId?: string; // NEW: V1 app ID from console registration
314
+ appSecret?: string; // Legacy: App secret from Trainly dashboard
315
+ apiKey?: string; // Legacy: Direct API key (alternative to appSecret)
203
316
  baseUrl?: string; // Custom API URL (defaults to trainly.com)
204
- userId?: string; // Your app's user ID
205
- userEmail?: string; // Your app's user email
317
+ userId?: string; // Legacy: Your app's user ID
318
+ userEmail?: string; // Legacy: Your app's user email
206
319
  }
207
320
  ```
208
321
 
209
322
  ## 🔍 Examples
210
323
 
211
- Check out the `/examples` folder for complete implementations:
324
+ See complete implementation examples in the [API Documentation](https://trainly.com/docs/v1-authentication).
325
+
326
+ ## 🆚 **V1 vs Legacy Comparison**
327
+
328
+ | Feature | V1 Trusted Issuer | Legacy App Secret |
329
+ | -------------- | -------------------------------- | ------------------------- |
330
+ | **User Auth** | Your OAuth provider | Trainly OAuth flow |
331
+ | **User Data** | Permanent private subchat | Temporary or shared |
332
+ | **Privacy** | Complete (dev can't see files) | Limited |
333
+ | **Setup** | Register once, use OAuth tokens | Generate app secrets |
334
+ | **Migration** | Zero (uses existing OAuth) | Requires auth integration |
335
+ | **Permanence** | Same user = same subchat forever | Depends on implementation |
212
336
 
213
- - **Simple Chat App** - Drop-in components
214
- - **Custom Implementation** - Build your own UI
215
- - **Multi-user App** - User-specific workspaces
216
- - **File-focused App** - Document analysis focus
337
+ **Recommendation**: Use V1 for new apps and consider migrating existing apps for better privacy and user experience.
217
338
 
218
339
  ## 🛠️ Development
219
340
 
@@ -4,9 +4,11 @@ export interface TrainlyProviderProps {
4
4
  children: React.ReactNode;
5
5
  appSecret?: string;
6
6
  apiKey?: string;
7
+ appId?: string;
7
8
  baseUrl?: string;
8
9
  userId?: string;
9
10
  userEmail?: string;
10
11
  }
11
- export declare function TrainlyProvider({ children, appSecret, apiKey, baseUrl, userId, userEmail, }: TrainlyProviderProps): import("react/jsx-runtime").JSX.Element;
12
+ export declare function TrainlyProvider({ children, appSecret, apiKey, appId, // NEW: For V1 authentication
13
+ baseUrl, userId, userEmail, }: TrainlyProviderProps): import("react/jsx-runtime").JSX.Element;
12
14
  export declare function useTrainlyContext(): TrainlyContextValue;
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
3
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
4
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -49,11 +50,14 @@ import { TrainlyClient } from "./api/TrainlyClient";
49
50
  var TrainlyContext = React.createContext(undefined);
50
51
  export function TrainlyProvider(_a) {
51
52
  var _this = this;
52
- var children = _a.children, appSecret = _a.appSecret, apiKey = _a.apiKey, _b = _a.baseUrl, baseUrl = _b === void 0 ? "http://localhost:8000" : _b, userId = _a.userId, userEmail = _a.userEmail;
53
+ var children = _a.children, appSecret = _a.appSecret, apiKey = _a.apiKey, appId = _a.appId, // NEW: For V1 authentication
54
+ _b = _a.baseUrl, // NEW: For V1 authentication
55
+ baseUrl = _b === void 0 ? "http://localhost:8000" : _b, userId = _a.userId, userEmail = _a.userEmail;
53
56
  var client = React.useState(function () {
54
57
  return new TrainlyClient({
55
58
  appSecret: appSecret,
56
59
  apiKey: apiKey,
60
+ appId: appId, // NEW: Pass appId to client
57
61
  baseUrl: baseUrl,
58
62
  userId: userId,
59
63
  userEmail: userEmail,
@@ -96,8 +100,38 @@ export function TrainlyProvider(_a) {
96
100
  }
97
101
  });
98
102
  }); };
103
+ // NEW: V1 OAuth Token connection method
104
+ var connectWithOAuthToken = function (idToken) { return __awaiter(_this, void 0, void 0, function () {
105
+ var err_2;
106
+ return __generator(this, function (_a) {
107
+ switch (_a.label) {
108
+ case 0:
109
+ _a.trys.push([0, 2, 3, 4]);
110
+ setIsLoading(true);
111
+ setError(null);
112
+ return [4 /*yield*/, client.connectWithOAuthToken(idToken)];
113
+ case 1:
114
+ _a.sent();
115
+ setIsConnected(true);
116
+ return [3 /*break*/, 4];
117
+ case 2:
118
+ err_2 = _a.sent();
119
+ setError({
120
+ code: "V1_CONNECTION_FAILED",
121
+ message: "Failed to connect with OAuth token",
122
+ details: err_2,
123
+ });
124
+ setIsConnected(false);
125
+ throw err_2;
126
+ case 3:
127
+ setIsLoading(false);
128
+ return [7 /*endfinally*/];
129
+ case 4: return [2 /*return*/];
130
+ }
131
+ });
132
+ }); };
99
133
  var ask = function (question) { return __awaiter(_this, void 0, void 0, function () {
100
- var response, err_2, error_1;
134
+ var response, err_3, error_1;
101
135
  return __generator(this, function (_a) {
102
136
  switch (_a.label) {
103
137
  case 0:
@@ -109,11 +143,11 @@ export function TrainlyProvider(_a) {
109
143
  response = _a.sent();
110
144
  return [2 /*return*/, response.answer];
111
145
  case 2:
112
- err_2 = _a.sent();
146
+ err_3 = _a.sent();
113
147
  error_1 = {
114
148
  code: "QUERY_FAILED",
115
149
  message: "Failed to get answer",
116
- details: err_2,
150
+ details: err_3,
117
151
  };
118
152
  setError(error_1);
119
153
  throw error_1;
@@ -125,7 +159,7 @@ export function TrainlyProvider(_a) {
125
159
  });
126
160
  }); };
127
161
  var askWithCitations = function (question) { return __awaiter(_this, void 0, void 0, function () {
128
- var response, err_3, error_2;
162
+ var response, err_4, error_2;
129
163
  return __generator(this, function (_a) {
130
164
  switch (_a.label) {
131
165
  case 0:
@@ -140,11 +174,11 @@ export function TrainlyProvider(_a) {
140
174
  citations: response.citations || [],
141
175
  }];
142
176
  case 2:
143
- err_3 = _a.sent();
177
+ err_4 = _a.sent();
144
178
  error_2 = {
145
179
  code: "QUERY_FAILED",
146
180
  message: "Failed to get answer with citations",
147
- details: err_3,
181
+ details: err_4,
148
182
  };
149
183
  setError(error_2);
150
184
  throw error_2;
@@ -156,7 +190,7 @@ export function TrainlyProvider(_a) {
156
190
  });
157
191
  }); };
158
192
  var upload = function (file) { return __awaiter(_this, void 0, void 0, function () {
159
- var result, err_4, error_3;
193
+ var result, err_5, error_3;
160
194
  return __generator(this, function (_a) {
161
195
  switch (_a.label) {
162
196
  case 0:
@@ -168,11 +202,11 @@ export function TrainlyProvider(_a) {
168
202
  result = _a.sent();
169
203
  return [2 /*return*/, result];
170
204
  case 2:
171
- err_4 = _a.sent();
205
+ err_5 = _a.sent();
172
206
  error_3 = {
173
207
  code: "UPLOAD_FAILED",
174
208
  message: "Failed to upload file",
175
- details: err_4,
209
+ details: err_5,
176
210
  };
177
211
  setError(error_3);
178
212
  throw error_3;
@@ -184,7 +218,7 @@ export function TrainlyProvider(_a) {
184
218
  });
185
219
  }); };
186
220
  var sendMessage = function (content) { return __awaiter(_this, void 0, void 0, function () {
187
- var userMessage, response, assistantMessage_1, err_5;
221
+ var userMessage, response, assistantMessage_1, err_6;
188
222
  return __generator(this, function (_a) {
189
223
  switch (_a.label) {
190
224
  case 0:
@@ -211,9 +245,9 @@ export function TrainlyProvider(_a) {
211
245
  setMessages(function (prev) { return __spreadArray(__spreadArray([], prev, true), [assistantMessage_1], false); });
212
246
  return [3 /*break*/, 4];
213
247
  case 3:
214
- err_5 = _a.sent();
248
+ err_6 = _a.sent();
215
249
  // Error is already set by askWithCitations
216
- console.error("Failed to send message:", err_5);
250
+ console.error("Failed to send message:", err_6);
217
251
  return [3 /*break*/, 4];
218
252
  case 4: return [2 /*return*/];
219
253
  }
@@ -226,6 +260,7 @@ export function TrainlyProvider(_a) {
226
260
  ask: ask,
227
261
  askWithCitations: askWithCitations,
228
262
  upload: upload,
263
+ connectWithOAuthToken: connectWithOAuthToken, // NEW: V1 OAuth connection method
229
264
  isLoading: isLoading,
230
265
  isConnected: isConnected,
231
266
  error: error,
@@ -7,7 +7,13 @@ export declare class TrainlyClient {
7
7
  private config;
8
8
  private scopedToken;
9
9
  private currentUserId;
10
+ private isV1Mode;
10
11
  constructor(config: TrainlyConfig);
12
+ /**
13
+ * NEW: Connect using V1 Trusted Issuer authentication with OAuth ID token
14
+ * This method allows users to authenticate directly with their OAuth provider tokens
15
+ */
16
+ connectWithOAuthToken(idToken: string): Promise<void>;
11
17
  connect(): Promise<void>;
12
18
  ask(question: string, options?: {
13
19
  includeCitations?: boolean;
@@ -1,3 +1,14 @@
1
+ var __assign = (this && this.__assign) || function () {
2
+ __assign = Object.assign || function(t) {
3
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
4
+ s = arguments[i];
5
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
6
+ t[p] = s[p];
7
+ }
8
+ return t;
9
+ };
10
+ return __assign.apply(this, arguments);
11
+ };
1
12
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
13
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
14
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -38,8 +49,51 @@ var TrainlyClient = /** @class */ (function () {
38
49
  function TrainlyClient(config) {
39
50
  this.scopedToken = null;
40
51
  this.currentUserId = null;
52
+ this.isV1Mode = false;
41
53
  this.config = config;
42
54
  }
55
+ /**
56
+ * NEW: Connect using V1 Trusted Issuer authentication with OAuth ID token
57
+ * This method allows users to authenticate directly with their OAuth provider tokens
58
+ */
59
+ TrainlyClient.prototype.connectWithOAuthToken = function (idToken) {
60
+ return __awaiter(this, void 0, void 0, function () {
61
+ var response, error, profile;
62
+ return __generator(this, function (_a) {
63
+ switch (_a.label) {
64
+ case 0:
65
+ if (!this.config.appId) {
66
+ throw new Error("appId is required for V1 authentication.");
67
+ }
68
+ // For V1, we use the ID token directly - no need to provision
69
+ this.scopedToken = idToken;
70
+ this.isV1Mode = true;
71
+ return [4 /*yield*/, fetch("".concat(this.config.baseUrl, "/v1/me/profile"), {
72
+ headers: {
73
+ Authorization: "Bearer ".concat(idToken),
74
+ "X-App-ID": this.config.appId,
75
+ },
76
+ })];
77
+ case 1:
78
+ response = _a.sent();
79
+ if (!!response.ok) return [3 /*break*/, 3];
80
+ return [4 /*yield*/, response.json()];
81
+ case 2:
82
+ error = _a.sent();
83
+ throw new Error("V1 authentication failed: ".concat(error.detail || response.statusText));
84
+ case 3: return [4 /*yield*/, response.json()];
85
+ case 4:
86
+ profile = _a.sent();
87
+ this.currentUserId = profile.user_id;
88
+ console.log("✅ Connected to Trainly with V1 Trusted Issuer authentication");
89
+ console.log("\uD83D\uDCCB User ID: ".concat(profile.user_id));
90
+ console.log("\uD83D\uDCAC Chat ID: ".concat(profile.chat_id));
91
+ console.log("\uD83D\uDD12 OAuth Provider: ".concat(profile.issuer));
92
+ return [2 /*return*/];
93
+ }
94
+ });
95
+ });
96
+ };
43
97
  TrainlyClient.prototype.connect = function () {
44
98
  return __awaiter(this, void 0, void 0, function () {
45
99
  var response, error, data;
@@ -88,14 +142,42 @@ var TrainlyClient = /** @class */ (function () {
88
142
  };
89
143
  TrainlyClient.prototype.ask = function (question_1) {
90
144
  return __awaiter(this, arguments, void 0, function (question, options) {
91
- var url, headers, body, response, error, data;
145
+ var response_1, error, data_1, url, headers, body, response, error, data;
92
146
  if (options === void 0) { options = {}; }
93
147
  return __generator(this, function (_a) {
94
148
  switch (_a.label) {
95
149
  case 0:
96
150
  if (!this.scopedToken) {
97
- throw new Error("Not connected. Call connect() first.");
151
+ throw new Error("Not connected. Call connect() or connectWithOAuthToken() first.");
98
152
  }
153
+ if (!(this.isV1Mode && this.config.appId)) return [3 /*break*/, 5];
154
+ return [4 /*yield*/, fetch("".concat(this.config.baseUrl, "/v1/me/chats/query"), {
155
+ method: "POST",
156
+ headers: {
157
+ Authorization: "Bearer ".concat(this.scopedToken),
158
+ "X-App-ID": this.config.appId,
159
+ "Content-Type": "application/x-www-form-urlencoded",
160
+ },
161
+ body: new URLSearchParams({
162
+ messages: JSON.stringify([{ role: "user", content: question }]),
163
+ response_tokens: "150",
164
+ }),
165
+ })];
166
+ case 1:
167
+ response_1 = _a.sent();
168
+ if (!!response_1.ok) return [3 /*break*/, 3];
169
+ return [4 /*yield*/, response_1.json()];
170
+ case 2:
171
+ error = _a.sent();
172
+ throw new Error("V1 query failed: ".concat(error.detail || response_1.statusText));
173
+ case 3: return [4 /*yield*/, response_1.json()];
174
+ case 4:
175
+ data_1 = _a.sent();
176
+ return [2 /*return*/, {
177
+ answer: data_1.answer,
178
+ citations: data_1.citations || [],
179
+ }];
180
+ case 5:
99
181
  url = this.config.apiKey
100
182
  ? "".concat(this.config.baseUrl, "/v1/").concat(this.extractChatId(), "/answer_question")
101
183
  : "".concat(this.config.baseUrl, "/v1/privacy/query");
@@ -118,15 +200,15 @@ var TrainlyClient = /** @class */ (function () {
118
200
  headers: headers,
119
201
  body: JSON.stringify(body),
120
202
  })];
121
- case 1:
203
+ case 6:
122
204
  response = _a.sent();
123
- if (!!response.ok) return [3 /*break*/, 3];
205
+ if (!!response.ok) return [3 /*break*/, 8];
124
206
  return [4 /*yield*/, response.json()];
125
- case 2:
207
+ case 7:
126
208
  error = _a.sent();
127
209
  throw new Error("Query failed: ".concat(error.detail || response.statusText));
128
- case 3: return [4 /*yield*/, response.json()];
129
- case 4:
210
+ case 8: return [4 /*yield*/, response.json()];
211
+ case 9:
130
212
  data = _a.sent();
131
213
  return [2 /*return*/, {
132
214
  answer: data.answer,
@@ -138,37 +220,65 @@ var TrainlyClient = /** @class */ (function () {
138
220
  };
139
221
  TrainlyClient.prototype.upload = function (file) {
140
222
  return __awaiter(this, void 0, void 0, function () {
141
- var formData, response, error, presignedResponse, error, upload_url, uploadResponse;
142
- return __generator(this, function (_a) {
143
- switch (_a.label) {
223
+ var formData, response, error, data, formData, response, error, presignedResponse, error, _a, upload_url, upload_headers, formData, uploadResponse;
224
+ return __generator(this, function (_b) {
225
+ switch (_b.label) {
144
226
  case 0:
145
227
  if (!this.scopedToken) {
146
- throw new Error("Not connected. Call connect() first.");
228
+ throw new Error("Not connected. Call connect() or connectWithOAuthToken() first.");
147
229
  }
148
- if (!this.config.apiKey) return [3 /*break*/, 4];
230
+ if (!(this.isV1Mode && this.config.appId)) return [3 /*break*/, 5];
149
231
  formData = new FormData();
150
232
  formData.append("file", file);
151
- return [4 /*yield*/, fetch("".concat(this.config.baseUrl, "/v1/").concat(this.extractChatId(), "/upload_file"), {
233
+ return [4 /*yield*/, fetch("".concat(this.config.baseUrl, "/v1/me/chats/files/upload"), {
152
234
  method: "POST",
153
235
  headers: {
154
- Authorization: "Bearer ".concat(this.config.apiKey),
236
+ Authorization: "Bearer ".concat(this.scopedToken),
237
+ "X-App-ID": this.config.appId,
155
238
  },
156
239
  body: formData,
157
240
  })];
158
241
  case 1:
159
- response = _a.sent();
242
+ response = _b.sent();
160
243
  if (!!response.ok) return [3 /*break*/, 3];
161
244
  return [4 /*yield*/, response.json()];
162
245
  case 2:
163
- error = _a.sent();
246
+ error = _b.sent();
247
+ throw new Error("V1 upload failed: ".concat(error.detail || response.statusText));
248
+ case 3: return [4 /*yield*/, response.json()];
249
+ case 4:
250
+ data = _b.sent();
251
+ return [2 /*return*/, {
252
+ success: data.success,
253
+ filename: data.filename,
254
+ size: data.size_bytes,
255
+ message: data.message || "File uploaded to your permanent private subchat",
256
+ }];
257
+ case 5:
258
+ if (!this.config.apiKey) return [3 /*break*/, 9];
259
+ formData = new FormData();
260
+ formData.append("file", file);
261
+ return [4 /*yield*/, fetch("".concat(this.config.baseUrl, "/v1/").concat(this.extractChatId(), "/upload_file"), {
262
+ method: "POST",
263
+ headers: {
264
+ Authorization: "Bearer ".concat(this.config.apiKey),
265
+ },
266
+ body: formData,
267
+ })];
268
+ case 6:
269
+ response = _b.sent();
270
+ if (!!response.ok) return [3 /*break*/, 8];
271
+ return [4 /*yield*/, response.json()];
272
+ case 7:
273
+ error = _b.sent();
164
274
  throw new Error("Upload failed: ".concat(error.detail || response.statusText));
165
- case 3: return [2 /*return*/, {
275
+ case 8: return [2 /*return*/, {
166
276
  success: true,
167
277
  filename: file.name,
168
278
  size: file.size,
169
279
  message: "File uploaded successfully",
170
280
  }];
171
- case 4: return [4 /*yield*/, fetch("".concat(this.config.baseUrl, "/v1/privacy/upload/presigned-url"), {
281
+ case 9: return [4 /*yield*/, fetch("".concat(this.config.baseUrl, "/v1/privacy/upload/presigned-url"), {
172
282
  method: "POST",
173
283
  headers: {
174
284
  "Content-Type": "application/json",
@@ -182,25 +292,25 @@ var TrainlyClient = /** @class */ (function () {
182
292
  file_type: file.type,
183
293
  }),
184
294
  })];
185
- case 5:
186
- presignedResponse = _a.sent();
187
- if (!!presignedResponse.ok) return [3 /*break*/, 7];
295
+ case 10:
296
+ presignedResponse = _b.sent();
297
+ if (!!presignedResponse.ok) return [3 /*break*/, 12];
188
298
  return [4 /*yield*/, presignedResponse.json()];
189
- case 6:
190
- error = _a.sent();
299
+ case 11:
300
+ error = _b.sent();
191
301
  throw new Error("Failed to get upload URL: ".concat(error.detail || presignedResponse.statusText));
192
- case 7: return [4 /*yield*/, presignedResponse.json()];
193
- case 8:
194
- upload_url = (_a.sent()).upload_url;
302
+ case 12: return [4 /*yield*/, presignedResponse.json()];
303
+ case 13:
304
+ _a = _b.sent(), upload_url = _a.upload_url, upload_headers = _a.upload_headers;
305
+ formData = new FormData();
306
+ formData.append("file", file);
195
307
  return [4 /*yield*/, fetch(upload_url, {
196
- method: "PUT",
197
- body: file,
198
- headers: {
199
- "Content-Type": file.type,
200
- },
308
+ method: "POST",
309
+ body: formData,
310
+ headers: __assign({}, upload_headers),
201
311
  })];
202
- case 9:
203
- uploadResponse = _a.sent();
312
+ case 14:
313
+ uploadResponse = _b.sent();
204
314
  if (!uploadResponse.ok) {
205
315
  throw new Error("Failed to upload file");
206
316
  }
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
3
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
4
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -86,7 +87,7 @@ export function TrainlyChat(_a) {
86
87
  var file = (_a = e.target.files) === null || _a === void 0 ? void 0 : _a[0];
87
88
  if (file) {
88
89
  // This would trigger upload and add a system message
89
- console.log("File selected:", file.name);
90
+ // File selected for upload
90
91
  }
91
92
  };
92
93
  var baseClasses = "\n flex flex-col border border-gray-200 rounded-lg overflow-hidden\n ".concat(theme === "dark" ? "bg-gray-900 text-white border-gray-700" : "bg-white text-gray-900", "\n ").concat(className, "\n ");
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
3
  import { useTrainly } from "../useTrainly";
3
4
  export function TrainlyStatus(_a) {
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
3
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
4
  return new (P || (P = Promise))(function (resolve, reject) {
package/dist/types.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export interface TrainlyConfig {
2
2
  appSecret?: string;
3
3
  apiKey?: string;
4
+ appId?: string;
4
5
  baseUrl?: string;
5
6
  userId?: string;
6
7
  userEmail?: string;
@@ -9,6 +10,7 @@ export interface TrainlyProviderProps {
9
10
  children: React.ReactNode;
10
11
  appSecret?: string;
11
12
  apiKey?: string;
13
+ appId?: string;
12
14
  baseUrl?: string;
13
15
  userId?: string;
14
16
  userEmail?: string;
@@ -44,6 +46,7 @@ export interface TrainlyContextValue {
44
46
  citations: Citation[];
45
47
  }>;
46
48
  upload: (file: File) => Promise<UploadResult>;
49
+ connectWithOAuthToken: (idToken: string) => Promise<void>;
47
50
  isLoading: boolean;
48
51
  isConnected: boolean;
49
52
  error: TrainlyError | null;
@@ -8,7 +8,7 @@
8
8
  *
9
9
  * const handleQuestion = async () => {
10
10
  * const answer = await ask("What is photosynthesis?");
11
- * console.log(answer);
11
+ * // Handle the answer response
12
12
  * };
13
13
  *
14
14
  * return <button onClick={handleQuestion}>Ask AI</button>;
@@ -9,7 +9,7 @@ import { useTrainlyContext } from "./TrainlyProvider";
9
9
  *
10
10
  * const handleQuestion = async () => {
11
11
  * const answer = await ask("What is photosynthesis?");
12
- * console.log(answer);
12
+ * // Handle the answer response
13
13
  * };
14
14
  *
15
15
  * return <button onClick={handleQuestion}>Ask AI</button>;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@trainly/react",
3
- "version": "1.0.2",
4
- "description": "Dead simple RAG integration for React apps",
3
+ "version": "1.1.0",
4
+ "description": "Dead simple RAG integration for React apps with OAuth authentication",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "scripts": {
@@ -18,6 +18,11 @@
18
18
  "documents",
19
19
  "react",
20
20
  "trainly",
21
+ "oauth",
22
+ "authentication",
23
+ "privacy",
24
+ "clerk",
25
+ "auth0",
21
26
  "retrieval-augmented-generation",
22
27
  "semantic-search",
23
28
  "document-chat"