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/onairos.d.ts CHANGED
@@ -21,188 +21,6 @@ declare module 'onairos' {
21
21
  */
22
22
  export function Onairos(props: OnairosProps): JSX.Element;
23
23
 
24
- // SDK Interfaces
25
- export interface OnairosClientConfig {
26
- openaiApiKey?: string;
27
- anthropicApiKey?: string;
28
- googleApiKey?: string;
29
- pineconeApiKey?: string;
30
- pineconeEnvironment?: string;
31
- jwtSecret: string;
32
- indexName?: string;
33
- }
34
-
35
- export interface CompletionMessage {
36
- role: 'system' | 'user' | 'assistant';
37
- content: string;
38
- }
39
-
40
- export interface CompletionOptions {
41
- temperature?: number;
42
- max_tokens?: number;
43
- top_p?: number;
44
- frequency_penalty?: number;
45
- presence_penalty?: number;
46
- }
47
-
48
- export interface CompletionParams {
49
- model: string;
50
- messages: CompletionMessage[];
51
- userId: string;
52
- sessionToken: string;
53
- options?: CompletionOptions;
54
- }
55
-
56
- export interface CompletionResponse {
57
- id: string;
58
- object: string;
59
- created: number;
60
- model: string;
61
- choices: Array<{
62
- index: number;
63
- message: {
64
- role: string;
65
- content: string;
66
- };
67
- finish_reason: string;
68
- }>;
69
- usage: {
70
- prompt_tokens: number;
71
- completion_tokens: number;
72
- total_tokens: number;
73
- };
74
- }
75
-
76
- export interface MemoryEntry {
77
- id: string;
78
- data: string;
79
- timestamp: string;
80
- query?: string;
81
- }
82
-
83
- export interface SessionTokenOptions {
84
- expiresIn?: string;
85
- additionalClaims?: Record<string, any>;
86
- }
87
-
88
- /**
89
- * Main Onairos SDK Client for RAG-enhanced completions
90
- */
91
- export class OnairosClient {
92
- constructor(config: OnairosClientConfig);
93
-
94
- /**
95
- * Initialize the client (sets up vector store)
96
- */
97
- initialize(): Promise<void>;
98
-
99
- /**
100
- * Create a completion with RAG enhancement
101
- */
102
- completions(params: CompletionParams): Promise<CompletionResponse>;
103
-
104
- /**
105
- * Create a completion (alias for completions)
106
- */
107
- create(params: CompletionParams): Promise<CompletionResponse>;
108
-
109
- /**
110
- * Generate a session token for a user
111
- */
112
- generateSessionToken(userId: string, options?: SessionTokenOptions): string;
113
-
114
- /**
115
- * Clear memory for a specific user
116
- */
117
- clearUserMemory(userId: string): Promise<void>;
118
-
119
- /**
120
- * Get user memory summary
121
- */
122
- getUserMemory(userId: string): Promise<MemoryEntry[]>;
123
- }
124
-
125
- /**
126
- * LLM Wrapper for multiple providers
127
- */
128
- export class LLMWrapper {
129
- constructor(config: {
130
- openaiApiKey?: string;
131
- anthropicApiKey?: string;
132
- googleApiKey?: string;
133
- });
134
-
135
- createCompletion(params: {
136
- model: string;
137
- messages: CompletionMessage[];
138
- options?: CompletionOptions;
139
- }): Promise<{
140
- id: string;
141
- content: string;
142
- finish_reason: string;
143
- usage?: any;
144
- }>;
145
-
146
- getAvailableModels(): {
147
- openai: string[];
148
- anthropic: string[];
149
- google: string[];
150
- };
151
- }
152
-
153
- /**
154
- * Memory Manager for RAG functionality
155
- */
156
- export class MemoryManager {
157
- constructor(config: {
158
- pineconeApiKey: string;
159
- pineconeEnvironment: string;
160
- indexName?: string;
161
- openaiApiKey: string;
162
- });
163
-
164
- initialize(): Promise<void>;
165
- storeInteraction(userId: string, query: string, response: string): Promise<void>;
166
- retrieveMemory(userId: string, query: string): Promise<string[]>;
167
- clearUserMemory(userId: string): Promise<void>;
168
- getUserMemory(userId: string): Promise<MemoryEntry[]>;
169
- storeCustomMemory(userId: string, memoryData: string, category?: string): Promise<void>;
170
- }
171
-
172
- /**
173
- * Session Manager for authentication
174
- */
175
- export class SessionManager {
176
- constructor(config: { jwtSecret: string });
177
-
178
- generateSessionToken(userId: string, options?: SessionTokenOptions): string;
179
- validateSession(token: string): Promise<any>;
180
- refreshSession(token: string, options?: any): Promise<string>;
181
- extractUserId(token: string): string | null;
182
- isTokenExpired(token: string): boolean;
183
- getTokenExpiration(token: string): Date | null;
184
- generateGuestToken(options?: any): string;
185
- validateUserAccess(token: string, expectedUserId: string): Promise<any>;
186
- generateApiKey(clientId: string, scopes?: string[]): string;
187
- validateApiKey(apiKey: string): Promise<any>;
188
- }
189
-
190
- // Utility functions
191
- export function extractMemory(params: { query: string; response: string }): string | null;
192
- export function hasMeaningfulMemory(query: string, response: string): boolean;
193
- export function cleanMemoryData(memoryData: string): string;
194
-
195
- export interface DataRequestResult {
196
- approved: boolean | string[];
197
- dataTypes?: string[];
198
- timestamp: string;
199
- userEmail?: string;
200
- appName?: string;
201
- apiResponse?: any; // Present when autoFetch is true and API call succeeds
202
- apiError?: string; // Present when autoFetch is true and API call fails
203
- apiUrl?: string; // The API endpoint used for the request
204
- }
205
-
206
24
  export interface PopupHandlerOptions {
207
25
  autoFetch?: boolean;
208
26
  onApiResponse?: (response: any) => void;
package/package.json CHANGED
@@ -1,10 +1,7 @@
1
1
  {
2
2
  "name": "onairos",
3
- "version": "2.0.7",
3
+ "version": "2.0.9",
4
4
  "dependencies": {
5
- "@anthropic-ai/sdk": "^0.24.3",
6
- "@google/generative-ai": "^0.15.0",
7
- "@pinecone-database/pinecone": "^2.2.2",
8
5
  "@react-oauth/google": "^0.12.1",
9
6
  "@telegram-apps/sdk-react": "^2.0.25",
10
7
  "@testing-library/jest-dom": "^5.17.0",
@@ -13,12 +10,9 @@
13
10
  "axios": "^1.6.5",
14
11
  "buffer": "^6.0.3",
15
12
  "caniuse-lite": "^1.0.30001718",
16
- "jsonwebtoken": "^9.0.2",
17
- "openai": "^4.52.7",
18
13
  "process": "^0.11.10",
19
14
  "react-scripts": "^5.0.1",
20
15
  "stream-browserify": "^3.0.0",
21
- "uuid": "^9.0.1",
22
16
  "web-vitals": "^2.1.4"
23
17
  },
24
18
  "sideEffects": false,
@@ -28,10 +22,7 @@
28
22
  "copy-assets": "echo 'Assets copied by webpack'",
29
23
  "dev": "webpack --mode development --watch",
30
24
  "test": "react-scripts test",
31
- "eject": "react-scripts eject",
32
- "test:sdk": "node test-sdk.js",
33
- "example": "node example-usage.js",
34
- "test:all": "npm run test:sdk && npm run example"
25
+ "eject": "react-scripts eject"
35
26
  },
36
27
  "eslintConfig": {
37
28
  "extends": [
@@ -96,13 +87,9 @@
96
87
  "react-dom": "^18.2.0",
97
88
  "react-native": ">=0.69.0",
98
89
  "tailwindcss": "^3.3.5",
99
- "ajv": "^8.12.0",
100
- "@anthropic-ai/sdk": "^0.24.3",
101
- "@google/generative-ai": "^0.15.0",
102
- "@pinecone-database/pinecone": "^2.2.2",
103
- "openai": "^4.52.7"
90
+ "ajv": "^8.12.0"
104
91
  },
105
- "description": "The Onairos Library is a collection of functions that enable Applications to connect and communicate data with Onairos Identities via User Authorization. Integration for developers is designed to be seamless, simple and effective for all applications",
92
+ "description": "The Onairos Library is a collection of functions that enable Applications to connect and communicate data with Onairos Identities via User Authorization. Integration for developers is seamless, simple and effective for all applications. LLM SDK capabilities have been migrated to backend for enhanced security and performance.",
106
93
  "main": "dist/onairos.bundle.js",
107
94
  "module": "dist/onairos.esm.js",
108
95
  "types": "onairos.d.ts",
@@ -114,7 +101,10 @@
114
101
  },
115
102
  "keywords": [
116
103
  "Onairos",
117
- "Identity"
104
+ "Identity",
105
+ "Authentication",
106
+ "Data-Sharing",
107
+ "Privacy"
118
108
  ],
119
109
  "author": "Zion Darko",
120
110
  "license": "Apache-2.0",
@@ -1,43 +1,66 @@
1
1
  import React, { useState } from 'react';
2
2
 
3
- const defaultDataTypes = [
4
- { id: 'email', name: 'Email Address', description: 'Your email for account identification', icon: '📧' },
5
- { id: 'profile', name: 'Profile Information', description: 'Basic profile data and preferences', icon: '👤' },
6
- { id: 'social', name: 'Social Connections', description: 'Connected social media accounts', icon: '🌐' },
7
- { id: 'activity', name: 'Activity Data', description: 'Usage patterns and interactions', icon: '📊' },
8
- { id: 'preferences', name: 'User Preferences', description: 'Settings and customization choices', icon: '⚙️' }
3
+ const dataTypes = [
4
+ {
5
+ id: 'basic',
6
+ name: 'Basic Information',
7
+ description: 'Name, email, and essential account details',
8
+ icon: '👤',
9
+ required: true // Cannot be deselected
10
+ },
11
+ {
12
+ id: 'memories',
13
+ name: 'Memories',
14
+ description: 'Preferences and interests',
15
+ icon: '🧠',
16
+ required: false
17
+ }
9
18
  ];
10
19
 
11
20
  export default function DataRequest({
12
21
  onComplete,
13
22
  userEmail,
14
- requestData,
15
23
  appName = 'App',
16
24
  autoFetch = true
17
25
  }) {
18
- const [selectedData, setSelectedData] = useState({});
26
+ const [selectedData, setSelectedData] = useState({
27
+ basic: true, // Always selected by default
28
+ memories: false
29
+ });
19
30
  const [isSubmitting, setIsSubmitting] = useState(false);
20
31
  const [isLoadingApi, setIsLoadingApi] = useState(false);
21
32
  const [apiResponse, setApiResponse] = useState(null);
22
33
  const [apiError, setApiError] = useState(null);
23
34
 
24
- // Use provided requestData or default data types
25
- const dataTypes = Array.isArray(requestData)
26
- ? requestData.map(id => defaultDataTypes.find(dt => dt.id === id) || { id, name: id, description: `${id} data`, icon: '📋' })
27
- : Object.values(defaultDataTypes);
28
-
29
35
  const handleDataToggle = (dataId) => {
36
+ // Don't allow toggling basic information (it's required)
37
+ if (dataId === 'basic') return;
38
+
30
39
  setSelectedData(prev => ({
31
40
  ...prev,
32
41
  [dataId]: !prev[dataId]
33
42
  }));
34
43
  };
35
44
 
45
+ const generateUserHash = (email) => {
46
+ // Simple hash function for user identification
47
+ let hash = 0;
48
+ const str = email + Date.now().toString();
49
+ for (let i = 0; i < str.length; i++) {
50
+ const char = str.charCodeAt(i);
51
+ hash = ((hash << 5) - hash) + char;
52
+ hash = hash & hash; // Convert to 32-bit integer
53
+ }
54
+ return `user_${Math.abs(hash).toString(36)}`;
55
+ };
56
+
36
57
  const makeApiCall = async (approvedData) => {
37
58
  try {
38
59
  setIsLoadingApi(true);
39
60
  setApiError(null);
40
61
 
62
+ const userHash = generateUserHash(userEmail);
63
+
41
64
  const response = await fetch('https://api2.onairos.uk/inferenceTest', {
42
65
  method: 'POST',
43
66
  headers: {
@@ -46,6 +69,7 @@ export default function DataRequest({
46
69
  body: JSON.stringify({
47
70
  approvedData,
48
71
  userEmail,
72
+ userHash, // Add user hash for backend LLM SDK
49
73
  appName,
50
74
  timestamp: new Date().toISOString()
51
75
  })
@@ -56,8 +80,15 @@ export default function DataRequest({
56
80
  }
57
81
 
58
82
  const data = await response.json();
59
- setApiResponse(data);
60
- return data;
83
+
84
+ // Ensure user hash is included in response
85
+ const responseWithHash = {
86
+ ...data,
87
+ userHash
88
+ };
89
+
90
+ setApiResponse(responseWithHash);
91
+ return responseWithHash;
61
92
  } catch (error) {
62
93
  console.error('API call error:', error);
63
94
  setApiError(error.message);
@@ -77,17 +108,14 @@ export default function DataRequest({
77
108
  .filter(([_, isSelected]) => isSelected)
78
109
  .map(([dataId]) => dataId);
79
110
 
80
- if (approved.length === 0) {
81
- alert('Please select at least one data type to continue.');
82
- setIsSubmitting(false);
83
- return;
84
- }
111
+ const userHash = generateUserHash(userEmail);
85
112
 
86
113
  const baseResult = {
87
114
  approved: true,
88
115
  dataTypes: approved,
89
116
  timestamp: new Date().toISOString(),
90
117
  userEmail: userEmail,
118
+ userHash: userHash, // Include user hash in response
91
119
  appName: appName
92
120
  };
93
121
 
@@ -129,6 +157,7 @@ export default function DataRequest({
129
157
  dataTypes: [],
130
158
  timestamp: new Date().toISOString(),
131
159
  userEmail: userEmail,
160
+ userHash: generateUserHash(userEmail),
132
161
  appName: appName
133
162
  });
134
163
  };
@@ -136,127 +165,109 @@ export default function DataRequest({
136
165
  const selectedCount = Object.values(selectedData).filter(Boolean).length;
137
166
 
138
167
  return (
139
- <div className="p-6 max-w-md mx-auto">
140
- {/* Header */}
141
- <div className="text-center mb-6">
142
- <div className="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mx-auto mb-4">
143
- <svg className="w-8 h-8 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
144
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" />
145
- </svg>
168
+ <div className="w-full max-w-md mx-auto bg-white rounded-lg shadow-xl overflow-hidden" style={{ maxHeight: '90vh', height: 'auto' }}>
169
+ <div className="p-4 sm:p-6 overflow-y-auto" style={{ maxHeight: 'calc(90vh - 4rem)' }}>
170
+ {/* Header */}
171
+ <div className="text-center mb-4 sm:mb-6">
172
+ <div className="w-12 h-12 sm:w-16 sm:h-16 bg-blue-100 rounded-full flex items-center justify-center mx-auto mb-3 sm:mb-4">
173
+ <svg className="w-6 h-6 sm:w-8 sm:h-8 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
174
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" />
175
+ </svg>
176
+ </div>
177
+ <h2 className="text-lg sm:text-xl font-semibold text-gray-900 mb-2">Data Access Request</h2>
178
+ <p className="text-gray-600 text-xs sm:text-sm">
179
+ <span className="font-medium">{appName}</span> would like to access some of your data.
180
+ </p>
146
181
  </div>
147
- <h2 className="text-xl font-semibold text-gray-900 mb-2">Data Access Request</h2>
148
- <p className="text-gray-600 text-sm">
149
- <span className="font-medium">{appName}</span> would like to access some of your data.
150
- {autoFetch && " Your data will be processed automatically after approval."}
151
- </p>
152
- </div>
153
182
 
154
- {/* Data Types Selection */}
155
- <div className="space-y-3 mb-6">
156
- {dataTypes.map((dataType) => {
157
- const isSelected = selectedData[dataType.id] || false;
158
-
159
- return (
160
- <div
161
- key={dataType.id}
162
- className="flex items-center justify-between p-4 border rounded-lg hover:bg-gray-50 transition-colors"
163
- >
164
- <div className="flex items-center space-x-3">
165
- <div className="text-2xl">
166
- {dataType.icon}
167
- </div>
168
- <div>
169
- <h3 className="font-medium text-gray-900">{dataType.name}</h3>
170
- <p className="text-sm text-gray-500">{dataType.description}</p>
171
- </div>
172
- </div>
173
-
174
- {/* Toggle Switch */}
175
- <button
176
- onClick={() => handleDataToggle(dataType.id)}
177
- className={`relative inline-flex h-6 w-11 items-center rounded-full transition-colors focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 ${
178
- isSelected ? 'bg-blue-600' : 'bg-gray-200'
183
+ {/* Data Types Selection */}
184
+ <div className="space-y-2 sm:space-y-3 mb-4 sm:mb-6">
185
+ {dataTypes.map((dataType) => {
186
+ const isSelected = selectedData[dataType.id] || false;
187
+ const isRequired = dataType.required;
188
+
189
+ return (
190
+ <div
191
+ key={dataType.id}
192
+ className={`flex items-center justify-between p-3 sm:p-4 border rounded-lg transition-colors ${
193
+ isRequired ? 'bg-blue-50 border-blue-200' : 'hover:bg-gray-50'
179
194
  }`}
180
195
  >
181
- <span
182
- className={`inline-block h-4 w-4 transform rounded-full bg-white transition-transform ${
183
- isSelected ? 'translate-x-6' : 'translate-x-1'
184
- }`}
185
- />
186
- </button>
187
- </div>
188
- );
189
- })}
190
- </div>
196
+ <div className="flex items-center space-x-3">
197
+ <div className="text-xl sm:text-2xl">
198
+ {dataType.icon}
199
+ </div>
200
+ <div>
201
+ <h3 className="font-medium text-gray-900 text-sm sm:text-base">
202
+ {dataType.name}
203
+ {isRequired && <span className="text-blue-600 ml-1">*</span>}
204
+ </h3>
205
+ <p className="text-xs sm:text-sm text-gray-500">{dataType.description}</p>
206
+ </div>
207
+ </div>
208
+
209
+ {/* Toggle Switch or Required Badge */}
210
+ {isRequired ? (
211
+ <div className="px-2 py-1 bg-blue-600 text-white text-xs rounded-full">
212
+ Required
213
+ </div>
214
+ ) : (
215
+ <button
216
+ onClick={() => handleDataToggle(dataType.id)}
217
+ className={`relative inline-flex h-5 sm:h-6 w-9 sm:w-11 items-center rounded-full transition-colors focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 ${
218
+ isSelected ? 'bg-blue-600' : 'bg-gray-200'
219
+ }`}
220
+ >
221
+ <span
222
+ className={`inline-block h-3 sm:h-4 w-3 sm:w-4 transform rounded-full bg-white transition-transform ${
223
+ isSelected ? 'translate-x-5 sm:translate-x-6' : 'translate-x-1'
224
+ }`}
225
+ />
226
+ </button>
227
+ )}
228
+ </div>
229
+ );
230
+ })}
231
+ </div>
191
232
 
192
- {/* Selection Summary */}
193
- {selectedCount > 0 && (
194
- <div className="mb-4 p-3 bg-green-50 border border-green-200 rounded-lg">
195
- <p className="text-green-800 text-sm">
233
+ {/* Selection Summary */}
234
+ <div className="mb-3 sm:mb-4 p-2 sm:p-3 bg-green-50 border border-green-200 rounded-lg">
235
+ <p className="text-green-800 text-xs sm:text-sm">
196
236
  ✅ {selectedCount} data type{selectedCount > 1 ? 's' : ''} selected for sharing
197
237
  </p>
198
238
  </div>
199
- )}
200
239
 
201
- {/* API Status */}
202
- {autoFetch && isLoadingApi && (
203
- <div className="mb-4 p-3 bg-blue-50 border border-blue-200 rounded-lg">
204
- <div className="flex items-center space-x-2">
205
- <div className="animate-spin h-4 w-4 border-2 border-blue-600 rounded-full border-t-transparent"></div>
206
- <p className="text-blue-800 text-sm">Processing your data...</p>
240
+ {/* API Status */}
241
+ {isLoadingApi && (
242
+ <div className="mb-3 sm:mb-4 p-2 sm:p-3 bg-blue-50 border border-blue-200 rounded-lg">
243
+ <p className="text-blue-800 text-xs sm:text-sm">🔄 Processing your data request...</p>
207
244
  </div>
208
- </div>
209
- )}
245
+ )}
210
246
 
211
- {autoFetch && apiResponse && (
212
- <div className="mb-4 p-3 bg-green-50 border border-green-200 rounded-lg">
213
- <p className="text-green-800 text-sm">
214
- ✅ Data processed successfully
215
- </p>
216
- </div>
217
- )}
247
+ {apiError && (
248
+ <div className="mb-3 sm:mb-4 p-2 sm:p-3 bg-red-50 border border-red-200 rounded-lg">
249
+ <p className="text-red-800 text-xs sm:text-sm">❌ {apiError}</p>
250
+ </div>
251
+ )}
218
252
 
219
- {autoFetch && apiError && (
220
- <div className="mb-4 p-3 bg-red-50 border border-red-200 rounded-lg">
221
- <p className="text-red-800 text-sm">
222
- ❌ Error processing data: {apiError}
223
- </p>
253
+ {/* Action Buttons */}
254
+ <div className="flex space-x-3">
255
+ <button
256
+ onClick={handleReject}
257
+ disabled={isSubmitting}
258
+ className="flex-1 py-2 sm:py-3 px-4 border border-gray-300 rounded-lg text-gray-700 hover:bg-gray-50 transition-colors disabled:opacity-50 text-sm sm:text-base"
259
+ >
260
+ Deny
261
+ </button>
262
+ <button
263
+ onClick={handleApprove}
264
+ disabled={isSubmitting}
265
+ className="flex-1 py-2 sm:py-3 px-4 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors disabled:opacity-50 text-sm sm:text-base"
266
+ >
267
+ {isSubmitting ? 'Processing...' : 'Approve'}
268
+ </button>
224
269
  </div>
225
- )}
226
-
227
- {/* Action Buttons */}
228
- <div className="flex space-x-3">
229
- <button
230
- onClick={handleReject}
231
- disabled={isSubmitting || isLoadingApi}
232
- className="flex-1 px-4 py-2 text-gray-700 bg-gray-100 hover:bg-gray-200 rounded-lg font-medium transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
233
- >
234
- Deny
235
- </button>
236
- <button
237
- onClick={handleApprove}
238
- disabled={isSubmitting || isLoadingApi || selectedCount === 0}
239
- className="flex-1 px-4 py-2 text-white bg-blue-600 hover:bg-blue-700 rounded-lg font-medium transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center"
240
- >
241
- {isSubmitting || isLoadingApi ? (
242
- <>
243
- <div className="animate-spin h-4 w-4 border-2 border-white rounded-full border-t-transparent mr-2"></div>
244
- {autoFetch ? 'Processing...' : 'Approving...'}
245
- </>
246
- ) : (
247
- autoFetch ? 'Approve & Process' : 'Approve'
248
- )}
249
- </button>
250
270
  </div>
251
-
252
- {/* Auto-fetch info */}
253
- {autoFetch && (
254
- <div className="mt-4 p-3 bg-blue-50 border border-blue-200 rounded-lg">
255
- <p className="text-blue-800 text-xs">
256
- 🔄 Auto-fetch enabled: Your approved data will be automatically processed and sent to {appName}.
257
- </p>
258
- </div>
259
- )}
260
271
  </div>
261
272
  );
262
273
  }