onairos 2.0.7 → 2.0.9
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/SDK_MIGRATION_SUMMARY.md +140 -0
- package/dist/iframe.bundle.js +1 -1
- package/dist/iframe.bundle.js.map +1 -1
- package/dist/onairos.bundle.js +1 -1
- package/dist/onairos.bundle.js.map +1 -1
- package/dist/onairos.esm.js +1 -1
- package/dist/onairos.esm.js.map +1 -1
- package/onairos.d.ts +0 -182
- package/package.json +8 -18
- package/src/components/DataRequest.js +138 -127
- package/src/components/UniversalOnboarding.js +72 -18
- package/src/components/connectors/GmailConnector.js +177 -0
- package/src/components/connectors/InstagramConnector.js +171 -0
- package/src/components/connectors/LinkedInConnector.js +168 -0
- package/src/components/connectors/PinterestConnector.js +168 -0
- package/src/components/connectors/README.md +292 -0
- package/src/components/connectors/RedditConnector.js +168 -0
- package/src/components/connectors/YoutubeConnector.js +172 -0
- package/src/components/connectors/index.js +21 -0
- package/src/components/utils/UpdateConnections.js +43 -0
- package/src/iframe/DataRequestPage.jsx +5 -1
- package/src/index.js +0 -9
- package/src/onairos.jsx +0 -16
- package/src/onairosButton.jsx +2 -1
- package/src/overlay/overlay.js +1 -1
- package/src/sdk/LLMWrapper.js +0 -221
- package/src/sdk/MemoryManager.js +0 -290
- package/src/sdk/OnairosClient.js +0 -152
- package/src/sdk/SessionManager.js +0 -257
package/src/sdk/MemoryManager.js
DELETED
|
@@ -1,290 +0,0 @@
|
|
|
1
|
-
import { Pinecone } from '@pinecone-database/pinecone';
|
|
2
|
-
import OpenAI from 'openai';
|
|
3
|
-
import { extractMemory } from '../utils/extractMemory.js';
|
|
4
|
-
import { v4 as uuidv4 } from 'uuid';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Memory Manager for RAG functionality
|
|
8
|
-
* Handles vector storage, memory extraction, and retrieval
|
|
9
|
-
*/
|
|
10
|
-
export class MemoryManager {
|
|
11
|
-
constructor({ pineconeApiKey, pineconeEnvironment, indexName, openaiApiKey }) {
|
|
12
|
-
this.pinecone = new Pinecone({
|
|
13
|
-
apiKey: pineconeApiKey,
|
|
14
|
-
environment: pineconeEnvironment
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
this.openai = new OpenAI({ apiKey: openaiApiKey });
|
|
18
|
-
this.indexName = indexName || 'onairos-memory';
|
|
19
|
-
this.index = null;
|
|
20
|
-
this.embeddingModel = 'text-embedding-3-small';
|
|
21
|
-
this.embeddingDimension = 1536;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Initialize the memory manager and create index if needed
|
|
26
|
-
*/
|
|
27
|
-
async initialize() {
|
|
28
|
-
try {
|
|
29
|
-
// Check if index exists
|
|
30
|
-
const indexList = await this.pinecone.listIndexes();
|
|
31
|
-
const indexExists = indexList.indexes?.some(index => index.name === this.indexName);
|
|
32
|
-
|
|
33
|
-
if (!indexExists) {
|
|
34
|
-
// Create index if it doesn't exist
|
|
35
|
-
await this.pinecone.createIndex({
|
|
36
|
-
name: this.indexName,
|
|
37
|
-
dimension: this.embeddingDimension,
|
|
38
|
-
metric: 'cosine',
|
|
39
|
-
spec: {
|
|
40
|
-
serverless: {
|
|
41
|
-
cloud: 'aws',
|
|
42
|
-
region: 'us-east-1'
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
// Wait for index to be ready
|
|
48
|
-
await this._waitForIndexReady();
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
this.index = this.pinecone.index(this.indexName);
|
|
52
|
-
} catch (error) {
|
|
53
|
-
console.warn(`Failed to initialize Pinecone index: ${error.message}`);
|
|
54
|
-
// Continue without vector storage for development
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Wait for index to be ready
|
|
60
|
-
*/
|
|
61
|
-
async _waitForIndexReady() {
|
|
62
|
-
let attempts = 0;
|
|
63
|
-
const maxAttempts = 30;
|
|
64
|
-
|
|
65
|
-
while (attempts < maxAttempts) {
|
|
66
|
-
try {
|
|
67
|
-
const indexStats = await this.pinecone.describeIndex(this.indexName);
|
|
68
|
-
if (indexStats.status?.ready) {
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
} catch (error) {
|
|
72
|
-
// Index might not be ready yet
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
76
|
-
attempts++;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
throw new Error('Index failed to become ready within timeout');
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Generate embedding for text using OpenAI
|
|
84
|
-
* @param {string} text - Text to embed
|
|
85
|
-
* @returns {Promise<Array>} Embedding vector
|
|
86
|
-
*/
|
|
87
|
-
async getEmbedding(text) {
|
|
88
|
-
try {
|
|
89
|
-
const response = await this.openai.embeddings.create({
|
|
90
|
-
model: this.embeddingModel,
|
|
91
|
-
input: text.substring(0, 8000), // Limit text length
|
|
92
|
-
encoding_format: 'float'
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
return response.data[0].embedding;
|
|
96
|
-
} catch (error) {
|
|
97
|
-
console.error('Failed to generate embedding:', error.message);
|
|
98
|
-
return null;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Store interaction in vector database
|
|
104
|
-
* @param {string} userId - User identifier
|
|
105
|
-
* @param {string} query - User query
|
|
106
|
-
* @param {string} response - Assistant response
|
|
107
|
-
*/
|
|
108
|
-
async storeInteraction(userId, query, response) {
|
|
109
|
-
try {
|
|
110
|
-
// Extract selected memory data (not raw interactions)
|
|
111
|
-
const memoryData = extractMemory({ query, response });
|
|
112
|
-
|
|
113
|
-
if (!memoryData || !this.index) {
|
|
114
|
-
return; // Skip if no meaningful memory or no index
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// Generate embedding for the memory data
|
|
118
|
-
const embedding = await this.getEmbedding(memoryData);
|
|
119
|
-
|
|
120
|
-
if (!embedding) {
|
|
121
|
-
return; // Skip if embedding generation failed
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// Store in vector database
|
|
125
|
-
const vectorId = `${userId}-${uuidv4()}`;
|
|
126
|
-
|
|
127
|
-
await this.index.upsert([{
|
|
128
|
-
id: vectorId,
|
|
129
|
-
values: embedding,
|
|
130
|
-
metadata: {
|
|
131
|
-
userId,
|
|
132
|
-
data: memoryData,
|
|
133
|
-
timestamp: new Date().toISOString(),
|
|
134
|
-
query: query.substring(0, 500), // Store truncated query for context
|
|
135
|
-
type: 'memory'
|
|
136
|
-
}
|
|
137
|
-
}]);
|
|
138
|
-
|
|
139
|
-
console.log(`Stored memory for user ${userId}: ${memoryData.substring(0, 100)}...`);
|
|
140
|
-
} catch (error) {
|
|
141
|
-
console.error('Failed to store interaction:', error.message);
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* Retrieve relevant memory for a query using RAG
|
|
147
|
-
* @param {string} userId - User identifier
|
|
148
|
-
* @param {string} query - Current query
|
|
149
|
-
* @returns {Promise<Array>} Array of relevant memory data
|
|
150
|
-
*/
|
|
151
|
-
async retrieveMemory(userId, query) {
|
|
152
|
-
try {
|
|
153
|
-
if (!this.index || !query.trim()) {
|
|
154
|
-
return [];
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// Generate embedding for the query
|
|
158
|
-
const queryEmbedding = await this.getEmbedding(query);
|
|
159
|
-
|
|
160
|
-
if (!queryEmbedding) {
|
|
161
|
-
return [];
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// Search for similar memories
|
|
165
|
-
const searchResults = await this.index.query({
|
|
166
|
-
vector: queryEmbedding,
|
|
167
|
-
topK: 5,
|
|
168
|
-
filter: {
|
|
169
|
-
userId: { $eq: userId },
|
|
170
|
-
type: { $eq: 'memory' }
|
|
171
|
-
},
|
|
172
|
-
includeMetadata: true
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
// Extract and return relevant memory data
|
|
176
|
-
const memories = searchResults.matches
|
|
177
|
-
.filter(match => match.score > 0.7) // Only high-confidence matches
|
|
178
|
-
.map(match => match.metadata.data)
|
|
179
|
-
.filter(data => data && data.length > 0);
|
|
180
|
-
|
|
181
|
-
return [...new Set(memories)]; // Remove duplicates
|
|
182
|
-
} catch (error) {
|
|
183
|
-
console.error('Failed to retrieve memory:', error.message);
|
|
184
|
-
return [];
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* Clear all memory for a specific user
|
|
190
|
-
* @param {string} userId - User identifier
|
|
191
|
-
*/
|
|
192
|
-
async clearUserMemory(userId) {
|
|
193
|
-
try {
|
|
194
|
-
if (!this.index) {
|
|
195
|
-
return;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
// Query all vectors for the user
|
|
199
|
-
const queryResults = await this.index.query({
|
|
200
|
-
vector: new Array(this.embeddingDimension).fill(0),
|
|
201
|
-
topK: 10000,
|
|
202
|
-
filter: {
|
|
203
|
-
userId: { $eq: userId }
|
|
204
|
-
},
|
|
205
|
-
includeMetadata: false
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
// Delete all user vectors
|
|
209
|
-
const vectorIds = queryResults.matches.map(match => match.id);
|
|
210
|
-
|
|
211
|
-
if (vectorIds.length > 0) {
|
|
212
|
-
await this.index.deleteMany(vectorIds);
|
|
213
|
-
console.log(`Cleared ${vectorIds.length} memory entries for user ${userId}`);
|
|
214
|
-
}
|
|
215
|
-
} catch (error) {
|
|
216
|
-
console.error('Failed to clear user memory:', error.message);
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
* Get user memory summary
|
|
222
|
-
* @param {string} userId - User identifier
|
|
223
|
-
* @returns {Promise<Array>} Array of memory entries with metadata
|
|
224
|
-
*/
|
|
225
|
-
async getUserMemory(userId) {
|
|
226
|
-
try {
|
|
227
|
-
if (!this.index) {
|
|
228
|
-
return [];
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
const queryResults = await this.index.query({
|
|
232
|
-
vector: new Array(this.embeddingDimension).fill(0),
|
|
233
|
-
topK: 100,
|
|
234
|
-
filter: {
|
|
235
|
-
userId: { $eq: userId },
|
|
236
|
-
type: { $eq: 'memory' }
|
|
237
|
-
},
|
|
238
|
-
includeMetadata: true
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
return queryResults.matches.map(match => ({
|
|
242
|
-
id: match.id,
|
|
243
|
-
data: match.metadata.data,
|
|
244
|
-
timestamp: match.metadata.timestamp,
|
|
245
|
-
query: match.metadata.query
|
|
246
|
-
}));
|
|
247
|
-
} catch (error) {
|
|
248
|
-
console.error('Failed to get user memory:', error.message);
|
|
249
|
-
return [];
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
/**
|
|
254
|
-
* Store custom memory entry
|
|
255
|
-
* @param {string} userId - User identifier
|
|
256
|
-
* @param {string} memoryData - Memory data to store
|
|
257
|
-
* @param {string} category - Memory category (optional)
|
|
258
|
-
*/
|
|
259
|
-
async storeCustomMemory(userId, memoryData, category = 'custom') {
|
|
260
|
-
try {
|
|
261
|
-
if (!this.index || !memoryData.trim()) {
|
|
262
|
-
return;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
const embedding = await this.getEmbedding(memoryData);
|
|
266
|
-
|
|
267
|
-
if (!embedding) {
|
|
268
|
-
return;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
const vectorId = `${userId}-${category}-${uuidv4()}`;
|
|
272
|
-
|
|
273
|
-
await this.index.upsert([{
|
|
274
|
-
id: vectorId,
|
|
275
|
-
values: embedding,
|
|
276
|
-
metadata: {
|
|
277
|
-
userId,
|
|
278
|
-
data: memoryData,
|
|
279
|
-
timestamp: new Date().toISOString(),
|
|
280
|
-
category,
|
|
281
|
-
type: 'memory'
|
|
282
|
-
}
|
|
283
|
-
}]);
|
|
284
|
-
|
|
285
|
-
console.log(`Stored custom memory for user ${userId}: ${memoryData.substring(0, 100)}...`);
|
|
286
|
-
} catch (error) {
|
|
287
|
-
console.error('Failed to store custom memory:', error.message);
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
}
|
package/src/sdk/OnairosClient.js
DELETED
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
import { LLMWrapper } from './LLMWrapper.js';
|
|
2
|
-
import { MemoryManager } from './MemoryManager.js';
|
|
3
|
-
import { SessionManager } from './SessionManager.js';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Main Onairos SDK Client
|
|
7
|
-
* Provides a unified completion API with RAG-enhanced personalization
|
|
8
|
-
*/
|
|
9
|
-
export class OnairosClient {
|
|
10
|
-
constructor({
|
|
11
|
-
openaiApiKey,
|
|
12
|
-
anthropicApiKey,
|
|
13
|
-
googleApiKey,
|
|
14
|
-
pineconeApiKey,
|
|
15
|
-
pineconeEnvironment,
|
|
16
|
-
jwtSecret,
|
|
17
|
-
indexName = 'onairos-memory'
|
|
18
|
-
}) {
|
|
19
|
-
this.llmWrapper = new LLMWrapper({
|
|
20
|
-
openaiApiKey,
|
|
21
|
-
anthropicApiKey,
|
|
22
|
-
googleApiKey
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
this.memoryManager = new MemoryManager({
|
|
26
|
-
pineconeApiKey,
|
|
27
|
-
pineconeEnvironment,
|
|
28
|
-
indexName,
|
|
29
|
-
openaiApiKey // For embeddings
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
this.sessionManager = new SessionManager({ jwtSecret });
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Initialize the client (sets up vector store)
|
|
37
|
-
*/
|
|
38
|
-
async initialize() {
|
|
39
|
-
await this.memoryManager.initialize();
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Create a completion with RAG enhancement
|
|
44
|
-
* @param {Object} params - Completion parameters
|
|
45
|
-
* @param {string} params.model - Model name (e.g., 'gpt-4', 'claude-3-sonnet', 'gemini-pro')
|
|
46
|
-
* @param {Array} params.messages - Array of message objects
|
|
47
|
-
* @param {string} params.userId - User identifier
|
|
48
|
-
* @param {string} params.sessionToken - Session token for validation
|
|
49
|
-
* @param {Object} params.options - Additional options (temperature, max_tokens, etc.)
|
|
50
|
-
* @returns {Promise<Object>} Completion response
|
|
51
|
-
*/
|
|
52
|
-
async completions({ model, messages, userId, sessionToken, options = {} }) {
|
|
53
|
-
try {
|
|
54
|
-
// Validate session
|
|
55
|
-
const { userId: validatedUserId } = await this.sessionManager.validateSession(sessionToken);
|
|
56
|
-
if (validatedUserId !== userId) {
|
|
57
|
-
throw new Error('Invalid session: User ID mismatch');
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Extract query text from the last message
|
|
61
|
-
const lastMessage = messages[messages.length - 1];
|
|
62
|
-
const query = typeof lastMessage.content === 'string'
|
|
63
|
-
? lastMessage.content
|
|
64
|
-
: lastMessage.content[0]?.text || '';
|
|
65
|
-
|
|
66
|
-
// Retrieve relevant memory data using RAG
|
|
67
|
-
const memoryContext = await this.memoryManager.retrieveMemory(userId, query);
|
|
68
|
-
|
|
69
|
-
// Construct prompt with RAG context if memory exists
|
|
70
|
-
let augmentedMessages = [...messages];
|
|
71
|
-
if (memoryContext.length > 0) {
|
|
72
|
-
const contextMessage = {
|
|
73
|
-
role: 'system',
|
|
74
|
-
content: `Context from previous interactions: ${memoryContext.join(' | ')}`
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
// Insert context before the last user message
|
|
78
|
-
augmentedMessages = [
|
|
79
|
-
...messages.slice(0, -1),
|
|
80
|
-
contextMessage,
|
|
81
|
-
lastMessage
|
|
82
|
-
];
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Call LLM with augmented messages
|
|
86
|
-
const response = await this.llmWrapper.createCompletion({
|
|
87
|
-
model,
|
|
88
|
-
messages: augmentedMessages,
|
|
89
|
-
options
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
// Extract and store selected memory data
|
|
93
|
-
await this.memoryManager.storeInteraction(userId, query, response.content);
|
|
94
|
-
|
|
95
|
-
return {
|
|
96
|
-
id: response.id || `onairos-${Date.now()}`,
|
|
97
|
-
object: 'chat.completion',
|
|
98
|
-
created: Math.floor(Date.now() / 1000),
|
|
99
|
-
model: model,
|
|
100
|
-
choices: [{
|
|
101
|
-
index: 0,
|
|
102
|
-
message: {
|
|
103
|
-
role: 'assistant',
|
|
104
|
-
content: response.content
|
|
105
|
-
},
|
|
106
|
-
finish_reason: response.finish_reason || 'stop'
|
|
107
|
-
}],
|
|
108
|
-
usage: response.usage || {
|
|
109
|
-
prompt_tokens: 0,
|
|
110
|
-
completion_tokens: 0,
|
|
111
|
-
total_tokens: 0
|
|
112
|
-
}
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
} catch (error) {
|
|
116
|
-
throw new Error(`Onairos completion failed: ${error.message}`);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Create a completion (alias for completions for OpenAI compatibility)
|
|
122
|
-
*/
|
|
123
|
-
async create(params) {
|
|
124
|
-
return this.completions(params);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Generate a session token for a user
|
|
129
|
-
* @param {string} userId - User identifier
|
|
130
|
-
* @returns {string} JWT session token
|
|
131
|
-
*/
|
|
132
|
-
generateSessionToken(userId) {
|
|
133
|
-
return this.sessionManager.generateSessionToken(userId);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Clear memory for a specific user
|
|
138
|
-
* @param {string} userId - User identifier
|
|
139
|
-
*/
|
|
140
|
-
async clearUserMemory(userId) {
|
|
141
|
-
await this.memoryManager.clearUserMemory(userId);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Get user memory summary
|
|
146
|
-
* @param {string} userId - User identifier
|
|
147
|
-
* @returns {Promise<Array>} Array of memory entries
|
|
148
|
-
*/
|
|
149
|
-
async getUserMemory(userId) {
|
|
150
|
-
return this.memoryManager.getUserMemory(userId);
|
|
151
|
-
}
|
|
152
|
-
}
|
|
@@ -1,257 +0,0 @@
|
|
|
1
|
-
import jwt from 'jsonwebtoken';
|
|
2
|
-
import { v4 as uuidv4 } from 'uuid';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Session Manager for user authentication and isolation
|
|
6
|
-
* Handles JWT token generation and validation
|
|
7
|
-
*/
|
|
8
|
-
export class SessionManager {
|
|
9
|
-
constructor({ jwtSecret }) {
|
|
10
|
-
if (!jwtSecret) {
|
|
11
|
-
throw new Error('JWT secret is required for session management');
|
|
12
|
-
}
|
|
13
|
-
this.jwtSecret = jwtSecret;
|
|
14
|
-
this.defaultExpiration = '24h'; // Default session duration
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Generate a session token for a user
|
|
19
|
-
* @param {string} userId - User identifier
|
|
20
|
-
* @param {Object} options - Token options
|
|
21
|
-
* @param {string} options.expiresIn - Token expiration time
|
|
22
|
-
* @param {Object} options.additionalClaims - Additional claims to include
|
|
23
|
-
* @returns {string} JWT session token
|
|
24
|
-
*/
|
|
25
|
-
generateSessionToken(userId, options = {}) {
|
|
26
|
-
try {
|
|
27
|
-
const {
|
|
28
|
-
expiresIn = this.defaultExpiration,
|
|
29
|
-
additionalClaims = {}
|
|
30
|
-
} = options;
|
|
31
|
-
|
|
32
|
-
const sessionId = `session_${uuidv4()}`;
|
|
33
|
-
const issuedAt = Math.floor(Date.now() / 1000);
|
|
34
|
-
|
|
35
|
-
const payload = {
|
|
36
|
-
userId,
|
|
37
|
-
sessionId,
|
|
38
|
-
iat: issuedAt,
|
|
39
|
-
type: 'onairos_session',
|
|
40
|
-
...additionalClaims
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
const token = jwt.sign(payload, this.jwtSecret, {
|
|
44
|
-
expiresIn,
|
|
45
|
-
issuer: 'onairos-sdk',
|
|
46
|
-
audience: 'onairos-client'
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
return token;
|
|
50
|
-
} catch (error) {
|
|
51
|
-
throw new Error(`Failed to generate session token: ${error.message}`);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Validate a session token
|
|
57
|
-
* @param {string} token - JWT token to validate
|
|
58
|
-
* @returns {Promise<Object>} Decoded token payload
|
|
59
|
-
*/
|
|
60
|
-
async validateSession(token) {
|
|
61
|
-
return new Promise((resolve, reject) => {
|
|
62
|
-
if (!token) {
|
|
63
|
-
reject(new Error('No token provided'));
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
jwt.verify(token, this.jwtSecret, {
|
|
68
|
-
issuer: 'onairos-sdk',
|
|
69
|
-
audience: 'onairos-client'
|
|
70
|
-
}, (err, decoded) => {
|
|
71
|
-
if (err) {
|
|
72
|
-
if (err.name === 'TokenExpiredError') {
|
|
73
|
-
reject(new Error('Session token has expired'));
|
|
74
|
-
} else if (err.name === 'JsonWebTokenError') {
|
|
75
|
-
reject(new Error('Invalid session token'));
|
|
76
|
-
} else {
|
|
77
|
-
reject(new Error(`Token validation failed: ${err.message}`));
|
|
78
|
-
}
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Validate token type
|
|
83
|
-
if (decoded.type !== 'onairos_session') {
|
|
84
|
-
reject(new Error('Invalid token type'));
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
resolve(decoded);
|
|
89
|
-
});
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Refresh a session token
|
|
95
|
-
* @param {string} token - Current token to refresh
|
|
96
|
-
* @param {Object} options - Refresh options
|
|
97
|
-
* @returns {Promise<string>} New session token
|
|
98
|
-
*/
|
|
99
|
-
async refreshSession(token, options = {}) {
|
|
100
|
-
try {
|
|
101
|
-
const decoded = await this.validateSession(token);
|
|
102
|
-
|
|
103
|
-
// Generate new token with same user ID but new session ID
|
|
104
|
-
return this.generateSessionToken(decoded.userId, {
|
|
105
|
-
expiresIn: options.expiresIn || this.defaultExpiration,
|
|
106
|
-
additionalClaims: options.additionalClaims || {}
|
|
107
|
-
});
|
|
108
|
-
} catch (error) {
|
|
109
|
-
throw new Error(`Failed to refresh session: ${error.message}`);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Extract user ID from token without full validation
|
|
115
|
-
* @param {string} token - JWT token
|
|
116
|
-
* @returns {string|null} User ID or null if invalid
|
|
117
|
-
*/
|
|
118
|
-
extractUserId(token) {
|
|
119
|
-
try {
|
|
120
|
-
const decoded = jwt.decode(token);
|
|
121
|
-
return decoded?.userId || null;
|
|
122
|
-
} catch (error) {
|
|
123
|
-
return null;
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Check if token is expired
|
|
129
|
-
* @param {string} token - JWT token
|
|
130
|
-
* @returns {boolean} True if expired
|
|
131
|
-
*/
|
|
132
|
-
isTokenExpired(token) {
|
|
133
|
-
try {
|
|
134
|
-
const decoded = jwt.decode(token);
|
|
135
|
-
if (!decoded || !decoded.exp) {
|
|
136
|
-
return true;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
const currentTime = Math.floor(Date.now() / 1000);
|
|
140
|
-
return decoded.exp < currentTime;
|
|
141
|
-
} catch (error) {
|
|
142
|
-
return true;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Get token expiration time
|
|
148
|
-
* @param {string} token - JWT token
|
|
149
|
-
* @returns {Date|null} Expiration date or null if invalid
|
|
150
|
-
*/
|
|
151
|
-
getTokenExpiration(token) {
|
|
152
|
-
try {
|
|
153
|
-
const decoded = jwt.decode(token);
|
|
154
|
-
if (!decoded || !decoded.exp) {
|
|
155
|
-
return null;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
return new Date(decoded.exp * 1000);
|
|
159
|
-
} catch (error) {
|
|
160
|
-
return null;
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Create a guest session token (for anonymous users)
|
|
166
|
-
* @param {Object} options - Token options
|
|
167
|
-
* @returns {string} Guest session token
|
|
168
|
-
*/
|
|
169
|
-
generateGuestToken(options = {}) {
|
|
170
|
-
const guestId = `guest_${uuidv4()}`;
|
|
171
|
-
return this.generateSessionToken(guestId, {
|
|
172
|
-
expiresIn: options.expiresIn || '1h',
|
|
173
|
-
additionalClaims: {
|
|
174
|
-
type: 'guest',
|
|
175
|
-
...options.additionalClaims
|
|
176
|
-
}
|
|
177
|
-
});
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* Validate session and ensure user access
|
|
182
|
-
* @param {string} token - Session token
|
|
183
|
-
* @param {string} expectedUserId - Expected user ID
|
|
184
|
-
* @returns {Promise<Object>} Validation result
|
|
185
|
-
*/
|
|
186
|
-
async validateUserAccess(token, expectedUserId) {
|
|
187
|
-
try {
|
|
188
|
-
const decoded = await this.validateSession(token);
|
|
189
|
-
|
|
190
|
-
if (decoded.userId !== expectedUserId) {
|
|
191
|
-
throw new Error('User ID mismatch - access denied');
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
return {
|
|
195
|
-
valid: true,
|
|
196
|
-
userId: decoded.userId,
|
|
197
|
-
sessionId: decoded.sessionId,
|
|
198
|
-
isGuest: decoded.type === 'guest'
|
|
199
|
-
};
|
|
200
|
-
} catch (error) {
|
|
201
|
-
return {
|
|
202
|
-
valid: false,
|
|
203
|
-
error: error.message
|
|
204
|
-
};
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
* Generate API key for server-to-server communication
|
|
210
|
-
* @param {string} clientId - Client identifier
|
|
211
|
-
* @param {Array} scopes - API scopes
|
|
212
|
-
* @returns {string} API key token
|
|
213
|
-
*/
|
|
214
|
-
generateApiKey(clientId, scopes = []) {
|
|
215
|
-
try {
|
|
216
|
-
const payload = {
|
|
217
|
-
clientId,
|
|
218
|
-
scopes,
|
|
219
|
-
type: 'api_key',
|
|
220
|
-
iat: Math.floor(Date.now() / 1000)
|
|
221
|
-
};
|
|
222
|
-
|
|
223
|
-
return jwt.sign(payload, this.jwtSecret, {
|
|
224
|
-
issuer: 'onairos-sdk',
|
|
225
|
-
audience: 'onairos-api'
|
|
226
|
-
});
|
|
227
|
-
} catch (error) {
|
|
228
|
-
throw new Error(`Failed to generate API key: ${error.message}`);
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
/**
|
|
233
|
-
* Validate API key
|
|
234
|
-
* @param {string} apiKey - API key to validate
|
|
235
|
-
* @returns {Promise<Object>} Validation result
|
|
236
|
-
*/
|
|
237
|
-
async validateApiKey(apiKey) {
|
|
238
|
-
return new Promise((resolve, reject) => {
|
|
239
|
-
jwt.verify(apiKey, this.jwtSecret, {
|
|
240
|
-
issuer: 'onairos-sdk',
|
|
241
|
-
audience: 'onairos-api'
|
|
242
|
-
}, (err, decoded) => {
|
|
243
|
-
if (err) {
|
|
244
|
-
reject(new Error(`Invalid API key: ${err.message}`));
|
|
245
|
-
return;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
if (decoded.type !== 'api_key') {
|
|
249
|
-
reject(new Error('Invalid key type'));
|
|
250
|
-
return;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
resolve(decoded);
|
|
254
|
-
});
|
|
255
|
-
});
|
|
256
|
-
}
|
|
257
|
-
}
|