@bytexbyte/nxtlinq-ai-agent-sdk 1.2.0 â 1.2.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 +14 -0
- package/dist/components/ChatBot.d.ts.map +1 -1
- package/dist/components/ChatBot.js +331 -91
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -17,6 +17,7 @@ A powerful SDK for building intelligent conversation applications with Nxtlinq A
|
|
|
17
17
|
- đ Permission-based access control
|
|
18
18
|
- đ Enhanced error handling and debugging
|
|
19
19
|
- ⥠Improved async operation handling
|
|
20
|
+
- đ Unified notification system with Modal prompts
|
|
20
21
|
|
|
21
22
|
## Installation
|
|
22
23
|
|
|
@@ -70,6 +71,8 @@ function App() {
|
|
|
70
71
|
}
|
|
71
72
|
```
|
|
72
73
|
|
|
74
|
+
> **Note**: The SDK now includes a unified notification system. All success, error, warning, and info messages are displayed as Modal prompts in the top-right corner, providing a consistent and non-intrusive user experience.
|
|
75
|
+
|
|
73
76
|
## Berify.me Integration
|
|
74
77
|
|
|
75
78
|
The SDK supports Berify.me wallet verification for enhanced security:
|
|
@@ -130,6 +133,17 @@ function App() {
|
|
|
130
133
|
| retryDelay | number | No | Delay between retries in milliseconds (default: 1000) |
|
|
131
134
|
| permissionGroup | string | No | Permission group name for filtering permissions |
|
|
132
135
|
|
|
136
|
+
### Notification System
|
|
137
|
+
|
|
138
|
+
The SDK includes a unified notification system that displays Modal prompts for various operations:
|
|
139
|
+
|
|
140
|
+
- **Success Notifications**: Green modal for successful operations (auto-hide after 3 seconds)
|
|
141
|
+
- **Error Notifications**: Red modal for error messages (auto-hide after 5 seconds)
|
|
142
|
+
- **Warning Notifications**: Orange modal for warnings (auto-hide after 4 seconds)
|
|
143
|
+
- **Info Notifications**: Blue modal for informational messages (auto-hide after 3 seconds)
|
|
144
|
+
|
|
145
|
+
All notifications appear in the top-right corner and can be manually dismissed by clicking the à button.
|
|
146
|
+
|
|
133
147
|
## Types
|
|
134
148
|
|
|
135
149
|
### Message
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChatBot.d.ts","sourceRoot":"","sources":["../../src/components/ChatBot.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAM/B,OAAO,EAAE,OAAO,EAAkC,MAAM,kBAAkB,CAAC;AAE3E,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC5B;AAED,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/B,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB;AAED,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IACvC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;IAC1D,cAAc,CAAC,EAAE,aAAa,EAAE,CAAC;IACjC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,OAAO,CAAC;QAC7B,KAAK,EAAE,MAAM,CAAC;KACf,GAAG,SAAS,CAAC,CAAC;IACf,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;
|
|
1
|
+
{"version":3,"file":"ChatBot.d.ts","sourceRoot":"","sources":["../../src/components/ChatBot.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAM/B,OAAO,EAAE,OAAO,EAAkC,MAAM,kBAAkB,CAAC;AAE3E,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC5B;AAED,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/B,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB;AAED,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IACvC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;IAC1D,cAAc,CAAC,EAAE,aAAa,EAAE,CAAC;IACjC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,OAAO,CAAC;QAC7B,KAAK,EAAE,MAAM,CAAC;KACf,GAAG,SAAS,CAAC,CAAC;IACf,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AA8hBD,eAAO,MAAM,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC,YAAY,CAmjC1C,CAAC"}
|
|
@@ -5,8 +5,64 @@ import { createNxtlinqApi } from '../api/nxtlinq-api';
|
|
|
5
5
|
import stringify from 'fast-json-stable-stringify';
|
|
6
6
|
import metakeepClient from '../core/metakeepClient';
|
|
7
7
|
import useLocalStorage from '../core/lib/useLocalStorage';
|
|
8
|
-
const
|
|
8
|
+
const NotificationModal = ({ notification, onClose }) => {
|
|
9
|
+
const getStyles = () => {
|
|
10
|
+
const baseStyles = {
|
|
11
|
+
position: 'fixed',
|
|
12
|
+
bottom: 20,
|
|
13
|
+
right: 20,
|
|
14
|
+
padding: '12px 24px',
|
|
15
|
+
borderRadius: 8,
|
|
16
|
+
zIndex: 2000,
|
|
17
|
+
fontWeight: 600,
|
|
18
|
+
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)',
|
|
19
|
+
transition: 'all 0.3s ease',
|
|
20
|
+
display: 'flex',
|
|
21
|
+
alignItems: 'center',
|
|
22
|
+
gap: '12px',
|
|
23
|
+
minWidth: '300px',
|
|
24
|
+
maxWidth: '500px'
|
|
25
|
+
};
|
|
26
|
+
switch (notification.type) {
|
|
27
|
+
case 'success':
|
|
28
|
+
return { ...baseStyles, background: '#4caf50', color: 'white' };
|
|
29
|
+
case 'error':
|
|
30
|
+
return { ...baseStyles, background: '#f44336', color: 'white' };
|
|
31
|
+
case 'warning':
|
|
32
|
+
return { ...baseStyles, background: '#ff9800', color: 'white' };
|
|
33
|
+
default:
|
|
34
|
+
return { ...baseStyles, background: '#2196f3', color: 'white' };
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
const getIcon = () => {
|
|
38
|
+
switch (notification.type) {
|
|
39
|
+
case 'success':
|
|
40
|
+
return 'â
';
|
|
41
|
+
case 'error':
|
|
42
|
+
return 'â';
|
|
43
|
+
case 'warning':
|
|
44
|
+
return 'â ī¸';
|
|
45
|
+
default:
|
|
46
|
+
return 'âšī¸';
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
return (notification.show && (_jsxs("div", { style: getStyles(), children: [_jsx("span", { style: { fontSize: '18px' }, children: getIcon() }), _jsx("span", { style: { flex: 1 }, children: notification.message }), _jsx("button", { onClick: onClose, style: {
|
|
50
|
+
background: 'none',
|
|
51
|
+
border: 'none',
|
|
52
|
+
color: 'white',
|
|
53
|
+
fontSize: '18px',
|
|
54
|
+
cursor: 'pointer',
|
|
55
|
+
padding: '4px',
|
|
56
|
+
display: 'flex',
|
|
57
|
+
alignItems: 'center',
|
|
58
|
+
justifyContent: 'center',
|
|
59
|
+
borderRadius: '4px',
|
|
60
|
+
transition: 'background-color 0.2s'
|
|
61
|
+
}, onMouseOver: (e) => e.currentTarget.style.backgroundColor = 'rgba(255, 255, 255, 0.2)', onMouseOut: (e) => e.currentTarget.style.backgroundColor = 'transparent', children: "\u00D7" })] })));
|
|
62
|
+
};
|
|
63
|
+
const PermissionForm = ({ hitAddress, permissions, setPermissions, setIsDisabled, onClose, onSave, onConnectWallet, onSignIn, isNeedSignInWithWallet, walletInfo, onVerifyWallet, serviceId, nxtlinqApi, permissionGroup, isAITLoading, isWalletLoading = false }) => {
|
|
9
64
|
const [availablePermissions, setAvailablePermissions] = React.useState([]);
|
|
65
|
+
const [isSaving, setIsSaving] = React.useState(false);
|
|
10
66
|
const fetchAvailablePermissions = async () => {
|
|
11
67
|
if (!serviceId)
|
|
12
68
|
return;
|
|
@@ -28,7 +84,72 @@ const PermissionForm = ({ hitAddress, permissions, setPermissions, setIsDisabled
|
|
|
28
84
|
React.useEffect(() => {
|
|
29
85
|
fetchAvailablePermissions();
|
|
30
86
|
}, [serviceId, nxtlinqApi, permissionGroup]);
|
|
31
|
-
const isWalletVerified = walletInfo?.id
|
|
87
|
+
const isWalletVerified = Boolean(walletInfo?.id);
|
|
88
|
+
const handleSave = async () => {
|
|
89
|
+
setIsSaving(true);
|
|
90
|
+
try {
|
|
91
|
+
await onSave();
|
|
92
|
+
}
|
|
93
|
+
finally {
|
|
94
|
+
setIsSaving(false);
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
// Show loading state while checking wallet status
|
|
98
|
+
if (isWalletLoading) {
|
|
99
|
+
return (_jsxs("div", { style: {
|
|
100
|
+
backgroundColor: 'white',
|
|
101
|
+
padding: '24px',
|
|
102
|
+
borderRadius: '12px',
|
|
103
|
+
width: '480px',
|
|
104
|
+
maxWidth: '90%',
|
|
105
|
+
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.1)'
|
|
106
|
+
}, children: [_jsxs("div", { style: {
|
|
107
|
+
display: 'flex',
|
|
108
|
+
justifyContent: 'space-between',
|
|
109
|
+
alignItems: 'center',
|
|
110
|
+
marginBottom: '24px'
|
|
111
|
+
}, children: [_jsx("h3", { style: {
|
|
112
|
+
margin: 0,
|
|
113
|
+
fontSize: '20px',
|
|
114
|
+
fontWeight: '600',
|
|
115
|
+
color: '#1a1a1a'
|
|
116
|
+
}, children: "AIT Settings" }), _jsx("button", { onClick: onClose, style: {
|
|
117
|
+
background: 'none',
|
|
118
|
+
border: 'none',
|
|
119
|
+
fontSize: '24px',
|
|
120
|
+
cursor: 'pointer',
|
|
121
|
+
color: '#666',
|
|
122
|
+
padding: '4px',
|
|
123
|
+
display: 'flex',
|
|
124
|
+
alignItems: 'center',
|
|
125
|
+
justifyContent: 'center'
|
|
126
|
+
}, children: "\u00D7" })] }), _jsxs("div", { style: { textAlign: 'center', padding: '32px 0' }, children: [_jsx("div", { style: {
|
|
127
|
+
width: '64px',
|
|
128
|
+
height: '64px',
|
|
129
|
+
margin: '0 auto 16px',
|
|
130
|
+
backgroundColor: '#f5f5f5',
|
|
131
|
+
borderRadius: '50%',
|
|
132
|
+
display: 'flex',
|
|
133
|
+
alignItems: 'center',
|
|
134
|
+
justifyContent: 'center'
|
|
135
|
+
}, children: _jsx("div", { style: {
|
|
136
|
+
width: '32px',
|
|
137
|
+
height: '32px',
|
|
138
|
+
border: '3px solid #e3e3e3',
|
|
139
|
+
borderTop: '3px solid #007bff',
|
|
140
|
+
borderRadius: '50%',
|
|
141
|
+
animation: 'spin 1s linear infinite'
|
|
142
|
+
} }) }), _jsx("p", { style: {
|
|
143
|
+
marginBottom: '24px',
|
|
144
|
+
fontSize: '16px',
|
|
145
|
+
color: '#666'
|
|
146
|
+
}, children: "Checking wallet status..." })] }), _jsx("style", { children: `
|
|
147
|
+
@keyframes spin {
|
|
148
|
+
0% { transform: rotate(0deg); }
|
|
149
|
+
100% { transform: rotate(360deg); }
|
|
150
|
+
}
|
|
151
|
+
` })] }));
|
|
152
|
+
}
|
|
32
153
|
return (_jsxs("div", { style: {
|
|
33
154
|
backgroundColor: 'white',
|
|
34
155
|
padding: '24px',
|
|
@@ -147,7 +268,14 @@ const PermissionForm = ({ hitAddress, permissions, setPermissions, setIsDisabled
|
|
|
147
268
|
marginBottom: '12px',
|
|
148
269
|
fontSize: '16px',
|
|
149
270
|
color: '#666'
|
|
150
|
-
}, children: "Permissions" }), _jsx("div", { style: {
|
|
271
|
+
}, children: "Permissions" }), isAITLoading ? (_jsx("div", { style: {
|
|
272
|
+
backgroundColor: '#f8f9fa',
|
|
273
|
+
padding: '16px',
|
|
274
|
+
borderRadius: '8px',
|
|
275
|
+
border: '1px solid #e9ecef',
|
|
276
|
+
textAlign: 'center',
|
|
277
|
+
color: '#666'
|
|
278
|
+
}, children: "Loading permissions..." })) : (_jsx("div", { style: {
|
|
151
279
|
backgroundColor: '#f8f9fa',
|
|
152
280
|
padding: '16px',
|
|
153
281
|
borderRadius: '8px',
|
|
@@ -156,25 +284,36 @@ const PermissionForm = ({ hitAddress, permissions, setPermissions, setIsDisabled
|
|
|
156
284
|
display: 'flex',
|
|
157
285
|
alignItems: 'center',
|
|
158
286
|
gap: '12px',
|
|
159
|
-
cursor: 'pointer',
|
|
287
|
+
cursor: isAITLoading ? 'not-allowed' : 'pointer',
|
|
160
288
|
padding: '8px',
|
|
161
289
|
borderRadius: '6px',
|
|
162
|
-
transition: 'background-color 0.2s'
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
290
|
+
transition: 'background-color 0.2s',
|
|
291
|
+
opacity: isAITLoading ? 0.6 : 1
|
|
292
|
+
}, onMouseOver: (e) => {
|
|
293
|
+
if (!isAITLoading) {
|
|
294
|
+
e.currentTarget.style.backgroundColor = '#e9ecef';
|
|
295
|
+
}
|
|
296
|
+
}, onMouseOut: (e) => {
|
|
297
|
+
if (!isAITLoading) {
|
|
298
|
+
e.currentTarget.style.backgroundColor = 'transparent';
|
|
299
|
+
}
|
|
300
|
+
}, children: [_jsx("input", { type: "checkbox", checked: permissions.includes(permission.label), onChange: () => {
|
|
301
|
+
if (!isAITLoading) {
|
|
302
|
+
const newPermissions = permissions.includes(permission.label)
|
|
303
|
+
? permissions.filter(p => p !== permission.label)
|
|
304
|
+
: [...permissions, permission.label].sort();
|
|
305
|
+
setPermissions(newPermissions);
|
|
306
|
+
setIsDisabled(false);
|
|
307
|
+
}
|
|
308
|
+
}, disabled: isAITLoading, style: {
|
|
170
309
|
margin: 0,
|
|
171
310
|
width: '18px',
|
|
172
311
|
height: '18px',
|
|
173
|
-
cursor: 'pointer'
|
|
312
|
+
cursor: isAITLoading ? 'not-allowed' : 'pointer'
|
|
174
313
|
} }), _jsx("span", { style: {
|
|
175
314
|
fontSize: '14px',
|
|
176
315
|
color: '#333'
|
|
177
|
-
}, children: permission.label })] }) }, permission.id))) })] }), _jsxs("div", { style: {
|
|
316
|
+
}, children: permission.label })] }) }, permission.id))) }))] }), _jsxs("div", { style: {
|
|
178
317
|
display: 'flex',
|
|
179
318
|
justifyContent: 'flex-end',
|
|
180
319
|
gap: '12px',
|
|
@@ -196,25 +335,25 @@ const PermissionForm = ({ hitAddress, permissions, setPermissions, setIsDisabled
|
|
|
196
335
|
}, onMouseOut: (e) => {
|
|
197
336
|
e.currentTarget.style.backgroundColor = '#f8f9fa';
|
|
198
337
|
e.currentTarget.style.borderColor = '#dee2e6';
|
|
199
|
-
}, children: "Cancel" }), _jsx("button", { onClick:
|
|
338
|
+
}, children: "Cancel" }), _jsx("button", { onClick: handleSave, disabled: permissions.length === 0 || isSaving || isAITLoading, style: {
|
|
200
339
|
padding: '10px 20px',
|
|
201
|
-
backgroundColor: permissions.length === 0 ? '#e9ecef' : '#007bff',
|
|
202
|
-
color: 'white',
|
|
340
|
+
backgroundColor: permissions.length === 0 || isSaving || isAITLoading ? '#e9ecef' : '#007bff',
|
|
341
|
+
color: permissions.length === 0 || isSaving || isAITLoading ? '#6c757d' : 'white',
|
|
203
342
|
border: 'none',
|
|
204
343
|
borderRadius: '8px',
|
|
205
|
-
cursor: permissions.length === 0 ? 'not-allowed' : 'pointer',
|
|
344
|
+
cursor: permissions.length === 0 || isSaving || isAITLoading ? 'not-allowed' : 'pointer',
|
|
206
345
|
fontSize: '14px',
|
|
207
346
|
fontWeight: '500',
|
|
208
347
|
transition: 'background-color 0.2s'
|
|
209
348
|
}, onMouseOver: (e) => {
|
|
210
|
-
if (permissions.length > 0) {
|
|
349
|
+
if (permissions.length > 0 && !isSaving && !isAITLoading) {
|
|
211
350
|
e.currentTarget.style.backgroundColor = '#0056b3';
|
|
212
351
|
}
|
|
213
352
|
}, onMouseOut: (e) => {
|
|
214
|
-
if (permissions.length > 0) {
|
|
353
|
+
if (permissions.length > 0 && !isSaving && !isAITLoading) {
|
|
215
354
|
e.currentTarget.style.backgroundColor = '#007bff';
|
|
216
355
|
}
|
|
217
|
-
}, children:
|
|
356
|
+
}, children: isSaving ? 'Saving...' : 'Save' })] })] }))] }));
|
|
218
357
|
};
|
|
219
358
|
export const ChatBot = ({ onMessage, onError, onToolUse, presetMessages = [], placeholder = 'Type a message...', className = '', maxRetries = 3, retryDelay = 1000, serviceId, apiKey, apiSecret, onVerifyWallet, permissionGroup }) => {
|
|
220
359
|
const [messages, setMessages] = React.useState([]);
|
|
@@ -226,14 +365,43 @@ export const ChatBot = ({ onMessage, onError, onToolUse, presetMessages = [], pl
|
|
|
226
365
|
const [permissions, setPermissions] = React.useState([]);
|
|
227
366
|
const [availablePermissions, setAvailablePermissions] = React.useState([]);
|
|
228
367
|
const [showPermissionForm, setShowPermissionForm] = React.useState(false);
|
|
229
|
-
const [
|
|
368
|
+
const [isPermissionFormOpen, setIsPermissionFormOpen] = React.useState(false);
|
|
369
|
+
const [isAITLoading, setIsAITLoading] = React.useState(false);
|
|
230
370
|
const messagesEndRef = React.useRef(null);
|
|
231
371
|
const [isDisabled, setIsDisabled] = React.useState(true);
|
|
232
372
|
const [signer, setSigner] = React.useState(null);
|
|
233
373
|
const [walletInfo, setWalletInfo] = React.useState(null);
|
|
234
374
|
const verifyWalletBtnRef = React.useRef(null);
|
|
235
375
|
const [nxtlinqAITServiceAccessToken, setNxtlinqAITServiceAccessToken] = useLocalStorage('nxtlinqAITServiceAccessToken', '');
|
|
376
|
+
const [isWalletLoading, setIsWalletLoading] = React.useState(false);
|
|
377
|
+
const [notification, setNotification] = React.useState({
|
|
378
|
+
show: false,
|
|
379
|
+
type: 'info',
|
|
380
|
+
message: '',
|
|
381
|
+
autoHide: true,
|
|
382
|
+
duration: 5000
|
|
383
|
+
});
|
|
236
384
|
const nxtlinqApi = React.useMemo(() => createNxtlinqApi(apiKey, apiSecret), [apiKey, apiSecret]);
|
|
385
|
+
// Unified notification functions
|
|
386
|
+
const showNotification = (type, message, duration = 5000) => {
|
|
387
|
+
setNotification({
|
|
388
|
+
show: true,
|
|
389
|
+
type,
|
|
390
|
+
message,
|
|
391
|
+
autoHide: true,
|
|
392
|
+
duration
|
|
393
|
+
});
|
|
394
|
+
// Auto-hide
|
|
395
|
+
if (duration > 0) {
|
|
396
|
+
setTimeout(() => {
|
|
397
|
+
setNotification(prev => ({ ...prev, show: false }));
|
|
398
|
+
}, duration);
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
const showSuccess = (message) => showNotification('success', message, 3000);
|
|
402
|
+
const showError = (message) => showNotification('error', message, 5000);
|
|
403
|
+
const showWarning = (message) => showNotification('warning', message, 4000);
|
|
404
|
+
const showInfo = (message) => showNotification('info', message, 3000);
|
|
237
405
|
const fetchAvailablePermissions = async () => {
|
|
238
406
|
if (!serviceId)
|
|
239
407
|
return;
|
|
@@ -252,7 +420,7 @@ export const ChatBot = ({ onMessage, onError, onToolUse, presetMessages = [], pl
|
|
|
252
420
|
console.error('Error fetching permissions:', error);
|
|
253
421
|
}
|
|
254
422
|
};
|
|
255
|
-
const refreshAIT = async () => {
|
|
423
|
+
const refreshAIT = async (forceUpdatePermissions = false) => {
|
|
256
424
|
console.log('refreshAIT called');
|
|
257
425
|
console.log('hitAddress:', hitAddress);
|
|
258
426
|
console.log('nxtlinqAITServiceAccessToken:', nxtlinqAITServiceAccessToken);
|
|
@@ -261,6 +429,7 @@ export const ChatBot = ({ onMessage, onError, onToolUse, presetMessages = [], pl
|
|
|
261
429
|
setPermissions([]);
|
|
262
430
|
return;
|
|
263
431
|
}
|
|
432
|
+
setIsAITLoading(true);
|
|
264
433
|
try {
|
|
265
434
|
const response = await nxtlinqApi.ait.getAITByServiceIdAndController({
|
|
266
435
|
serviceId,
|
|
@@ -274,13 +443,19 @@ export const ChatBot = ({ onMessage, onError, onToolUse, presetMessages = [], pl
|
|
|
274
443
|
}
|
|
275
444
|
console.log('AIT response:', response);
|
|
276
445
|
setAit(response);
|
|
277
|
-
|
|
446
|
+
// Update permissions if form is not open OR if force update is requested
|
|
447
|
+
if (!isPermissionFormOpen || forceUpdatePermissions) {
|
|
448
|
+
setPermissions(response.metadata?.permissions || []);
|
|
449
|
+
}
|
|
278
450
|
}
|
|
279
451
|
catch (error) {
|
|
280
452
|
console.error('Failed to fetch AIT:', error);
|
|
281
453
|
setAit(null);
|
|
282
454
|
setPermissions([]);
|
|
283
455
|
}
|
|
456
|
+
finally {
|
|
457
|
+
setIsAITLoading(false);
|
|
458
|
+
}
|
|
284
459
|
};
|
|
285
460
|
React.useEffect(() => {
|
|
286
461
|
console.log('hitAddress:', hitAddress);
|
|
@@ -325,6 +500,7 @@ export const ChatBot = ({ onMessage, onError, onToolUse, presetMessages = [], pl
|
|
|
325
500
|
const handleVerifySuccess = (address) => {
|
|
326
501
|
localStorage.setItem(`wallet_verified_${address}`, 'true');
|
|
327
502
|
const getWalletInfo = async () => {
|
|
503
|
+
setIsWalletLoading(true);
|
|
328
504
|
try {
|
|
329
505
|
const token = JSON.parse(localStorage.getItem('nxtlinqAITServiceAccessToken') || '');
|
|
330
506
|
const walletResponse = await nxtlinqApi.wallet.getWallet({ address }, token);
|
|
@@ -344,6 +520,9 @@ export const ChatBot = ({ onMessage, onError, onToolUse, presetMessages = [], pl
|
|
|
344
520
|
catch (error) {
|
|
345
521
|
console.error('Failed to update wallet info after verification:', error);
|
|
346
522
|
}
|
|
523
|
+
finally {
|
|
524
|
+
setIsWalletLoading(false);
|
|
525
|
+
}
|
|
347
526
|
};
|
|
348
527
|
getWalletInfo();
|
|
349
528
|
};
|
|
@@ -355,16 +534,25 @@ export const ChatBot = ({ onMessage, onError, onToolUse, presetMessages = [], pl
|
|
|
355
534
|
if (isNeedSignInWithWallet) {
|
|
356
535
|
return;
|
|
357
536
|
}
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
537
|
+
setIsWalletLoading(true);
|
|
538
|
+
try {
|
|
539
|
+
const getWalletResponse = await nxtlinqApi.wallet.getWallet({ address: hitAddress }, nxtlinqAITServiceAccessToken);
|
|
540
|
+
if ('error' in getWalletResponse) {
|
|
541
|
+
if (getWalletResponse.error === 'Wallet not found') {
|
|
542
|
+
console.log('Wallet not found - this is expected for new users');
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
545
|
+
console.error(getWalletResponse.error);
|
|
362
546
|
return;
|
|
363
547
|
}
|
|
364
|
-
|
|
365
|
-
|
|
548
|
+
setWalletInfo(getWalletResponse);
|
|
549
|
+
}
|
|
550
|
+
catch (error) {
|
|
551
|
+
console.error('Error getting wallet info:', error);
|
|
552
|
+
}
|
|
553
|
+
finally {
|
|
554
|
+
setIsWalletLoading(false);
|
|
366
555
|
}
|
|
367
|
-
setWalletInfo(getWalletResponse);
|
|
368
556
|
};
|
|
369
557
|
getWalletInfo();
|
|
370
558
|
}, [hitAddress, isNeedSignInWithWallet]);
|
|
@@ -377,7 +565,7 @@ export const ChatBot = ({ onMessage, onError, onToolUse, presetMessages = [], pl
|
|
|
377
565
|
signer: !!signer
|
|
378
566
|
});
|
|
379
567
|
if (!hitAddress) {
|
|
380
|
-
|
|
568
|
+
showError('Please connect your wallet first.');
|
|
381
569
|
return;
|
|
382
570
|
}
|
|
383
571
|
try {
|
|
@@ -403,19 +591,29 @@ export const ChatBot = ({ onMessage, onError, onToolUse, presetMessages = [], pl
|
|
|
403
591
|
if ('error' in verifyWalletResponse) {
|
|
404
592
|
if (verifyWalletResponse.error === 'Wallet already exists') {
|
|
405
593
|
// If wallet exists, get wallet info directly
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
594
|
+
setIsWalletLoading(true);
|
|
595
|
+
try {
|
|
596
|
+
const walletResponse = await nxtlinqApi.wallet.getWallet({ address }, token);
|
|
597
|
+
if (!('error' in walletResponse)) {
|
|
598
|
+
setWalletInfo(walletResponse);
|
|
599
|
+
const aitResponse = await nxtlinqApi.ait.getAITByServiceIdAndController({ serviceId, controller: address }, token);
|
|
600
|
+
if (!('error' in aitResponse)) {
|
|
601
|
+
setAit(aitResponse);
|
|
602
|
+
}
|
|
412
603
|
}
|
|
413
604
|
}
|
|
414
|
-
|
|
605
|
+
finally {
|
|
606
|
+
setIsWalletLoading(false);
|
|
607
|
+
}
|
|
608
|
+
// Clean up URL parameters after wallet verification
|
|
415
609
|
if (typeof window !== 'undefined') {
|
|
416
610
|
try {
|
|
417
611
|
const url = new URL(window.location.href);
|
|
612
|
+
// Clean up all Berify.me related parameters
|
|
418
613
|
url.searchParams.delete('token');
|
|
614
|
+
url.searchParams.delete('isAutoConnect');
|
|
615
|
+
url.searchParams.delete('method');
|
|
616
|
+
url.searchParams.delete('returnUrl');
|
|
419
617
|
window.history.replaceState({}, '', url.toString());
|
|
420
618
|
}
|
|
421
619
|
catch (e) {
|
|
@@ -423,28 +621,40 @@ export const ChatBot = ({ onMessage, onError, onToolUse, presetMessages = [], pl
|
|
|
423
621
|
}
|
|
424
622
|
}
|
|
425
623
|
setIsLoading(false);
|
|
624
|
+
// Use Modal notification instead of ChatBot message
|
|
625
|
+
showSuccess('Wallet verification completed successfully! Your wallet is now verified and ready to use.');
|
|
426
626
|
// Refresh AIT after wallet verification
|
|
427
627
|
refreshAIT();
|
|
428
628
|
return { token, hitAddress: address };
|
|
429
629
|
}
|
|
430
|
-
|
|
630
|
+
showError(verifyWalletResponse.error);
|
|
431
631
|
setIsLoading(false);
|
|
432
632
|
return;
|
|
433
633
|
}
|
|
434
634
|
// Verification successful, get wallet info
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
635
|
+
setIsWalletLoading(true);
|
|
636
|
+
try {
|
|
637
|
+
const walletResponse = await nxtlinqApi.wallet.getWallet({ address }, token);
|
|
638
|
+
if (!('error' in walletResponse)) {
|
|
639
|
+
setWalletInfo(walletResponse);
|
|
640
|
+
const aitResponse = await nxtlinqApi.ait.getAITByServiceIdAndController({ serviceId, controller: address }, token);
|
|
641
|
+
if (!('error' in aitResponse)) {
|
|
642
|
+
setAit(aitResponse);
|
|
643
|
+
}
|
|
441
644
|
}
|
|
442
645
|
}
|
|
443
|
-
|
|
646
|
+
finally {
|
|
647
|
+
setIsWalletLoading(false);
|
|
648
|
+
}
|
|
649
|
+
// Clean up URL parameters after wallet verification
|
|
444
650
|
if (typeof window !== 'undefined') {
|
|
445
651
|
try {
|
|
446
652
|
const url = new URL(window.location.href);
|
|
653
|
+
// Clean up all Berify.me related parameters
|
|
447
654
|
url.searchParams.delete('token');
|
|
655
|
+
url.searchParams.delete('isAutoConnect');
|
|
656
|
+
url.searchParams.delete('method');
|
|
657
|
+
url.searchParams.delete('returnUrl');
|
|
448
658
|
window.history.replaceState({}, '', url.toString());
|
|
449
659
|
}
|
|
450
660
|
catch (e) {
|
|
@@ -452,6 +662,8 @@ export const ChatBot = ({ onMessage, onError, onToolUse, presetMessages = [], pl
|
|
|
452
662
|
}
|
|
453
663
|
}
|
|
454
664
|
setIsLoading(false);
|
|
665
|
+
// Use Modal notification instead of ChatBot message
|
|
666
|
+
showSuccess('Wallet verification completed successfully! Your wallet is now verified and ready to use.');
|
|
455
667
|
// Refresh AIT after wallet verification
|
|
456
668
|
refreshAIT();
|
|
457
669
|
return { token, hitAddress: address };
|
|
@@ -466,7 +678,7 @@ export const ChatBot = ({ onMessage, onError, onToolUse, presetMessages = [], pl
|
|
|
466
678
|
msg = error.message;
|
|
467
679
|
}
|
|
468
680
|
console.error('Wallet verification failed:', error);
|
|
469
|
-
|
|
681
|
+
showError(msg);
|
|
470
682
|
setIsLoading(false);
|
|
471
683
|
throw error;
|
|
472
684
|
}
|
|
@@ -478,7 +690,7 @@ export const ChatBot = ({ onMessage, onError, onToolUse, presetMessages = [], pl
|
|
|
478
690
|
catch (error) {
|
|
479
691
|
console.error('Failed to verify wallet:', error);
|
|
480
692
|
setIsLoading(false);
|
|
481
|
-
|
|
693
|
+
showError('Failed to verify wallet. Please try again.');
|
|
482
694
|
throw error;
|
|
483
695
|
}
|
|
484
696
|
};
|
|
@@ -559,12 +771,7 @@ export const ChatBot = ({ onMessage, onError, onToolUse, presetMessages = [], pl
|
|
|
559
771
|
}
|
|
560
772
|
else {
|
|
561
773
|
// User is already signed in
|
|
562
|
-
|
|
563
|
-
id: Date.now().toString(),
|
|
564
|
-
content: 'Successfully connected your HIT wallet. You are already signed in and can use the AI agent.',
|
|
565
|
-
role: 'assistant',
|
|
566
|
-
timestamp: new Date().toISOString()
|
|
567
|
-
}]);
|
|
774
|
+
showSuccess('Successfully connected your HIT wallet. You are already signed in and can use the AI agent.');
|
|
568
775
|
}
|
|
569
776
|
}
|
|
570
777
|
return userAddress;
|
|
@@ -579,17 +786,17 @@ export const ChatBot = ({ onMessage, onError, onToolUse, presetMessages = [], pl
|
|
|
579
786
|
}, [nxtlinqAITServiceAccessToken, refreshAIT, isNeedSignInWithWallet]);
|
|
580
787
|
const signInWallet = async (autoShowSuccessMessage = true) => {
|
|
581
788
|
if (!hitAddress) {
|
|
582
|
-
|
|
789
|
+
showError('Please connect your wallet first.');
|
|
583
790
|
return;
|
|
584
791
|
}
|
|
585
792
|
if (!signer) {
|
|
586
|
-
|
|
793
|
+
showError('Please connect your wallet first.');
|
|
587
794
|
return;
|
|
588
795
|
}
|
|
589
796
|
try {
|
|
590
797
|
const nonceResponse = await nxtlinqApi.auth.getNonce({ address: hitAddress });
|
|
591
798
|
if ('error' in nonceResponse) {
|
|
592
|
-
|
|
799
|
+
showError(nonceResponse.error);
|
|
593
800
|
return;
|
|
594
801
|
}
|
|
595
802
|
const payload = {
|
|
@@ -604,26 +811,21 @@ export const ChatBot = ({ onMessage, onError, onToolUse, presetMessages = [], pl
|
|
|
604
811
|
signature
|
|
605
812
|
});
|
|
606
813
|
if ('error' in response) {
|
|
607
|
-
|
|
814
|
+
showError(response.error);
|
|
608
815
|
return;
|
|
609
816
|
}
|
|
610
817
|
const { accessToken } = response;
|
|
611
818
|
setNxtlinqAITServiceAccessToken(accessToken);
|
|
612
819
|
// Auto-show connected message after signing in
|
|
613
820
|
if (autoShowSuccessMessage) {
|
|
614
|
-
|
|
615
|
-
id: Date.now().toString(),
|
|
616
|
-
content: 'Successfully signed in with your HIT wallet. You can now use the AI agent.',
|
|
617
|
-
role: 'assistant',
|
|
618
|
-
timestamp: new Date().toISOString()
|
|
619
|
-
}]);
|
|
821
|
+
showSuccess('Successfully signed in with your HIT wallet. You can now use the AI agent.');
|
|
620
822
|
}
|
|
621
823
|
// Refresh AIT after signing in
|
|
622
824
|
refreshAIT();
|
|
623
825
|
}
|
|
624
826
|
catch (error) {
|
|
625
827
|
console.error('Failed to sign in:', error);
|
|
626
|
-
|
|
828
|
+
showError('Failed to sign in. Please try again.');
|
|
627
829
|
}
|
|
628
830
|
};
|
|
629
831
|
const hasPermission = async (toolName) => {
|
|
@@ -637,7 +839,7 @@ export const ChatBot = ({ onMessage, onError, onToolUse, presetMessages = [], pl
|
|
|
637
839
|
}]);
|
|
638
840
|
return false;
|
|
639
841
|
}
|
|
640
|
-
// Check if user has signed in with wallet
|
|
842
|
+
// Check if user has signed in with wallet and token is valid
|
|
641
843
|
if (!nxtlinqAITServiceAccessToken) {
|
|
642
844
|
setMessages(prev => [...prev, {
|
|
643
845
|
id: Date.now().toString(),
|
|
@@ -648,6 +850,51 @@ export const ChatBot = ({ onMessage, onError, onToolUse, presetMessages = [], pl
|
|
|
648
850
|
}]);
|
|
649
851
|
return false;
|
|
650
852
|
}
|
|
853
|
+
// Validate token - check if it's expired or for different address
|
|
854
|
+
try {
|
|
855
|
+
const payload = JSON.parse(atob(nxtlinqAITServiceAccessToken.split('.')[1]));
|
|
856
|
+
const exp = payload.exp * 1000; // Convert to milliseconds
|
|
857
|
+
const now = Date.now();
|
|
858
|
+
if (exp < now) {
|
|
859
|
+
// Clear invalid token
|
|
860
|
+
setNxtlinqAITServiceAccessToken('');
|
|
861
|
+
setMessages(prev => [...prev, {
|
|
862
|
+
id: Date.now().toString(),
|
|
863
|
+
content: 'Your wallet session has expired. Please sign in again.',
|
|
864
|
+
role: 'assistant',
|
|
865
|
+
timestamp: new Date().toISOString(),
|
|
866
|
+
button: 'signIn'
|
|
867
|
+
}]);
|
|
868
|
+
return false;
|
|
869
|
+
}
|
|
870
|
+
// Check if the token's payload has the same address as the wallet address
|
|
871
|
+
const address = payload.address;
|
|
872
|
+
if (address !== hitAddress) {
|
|
873
|
+
// Clear mismatched token
|
|
874
|
+
setNxtlinqAITServiceAccessToken('');
|
|
875
|
+
setMessages(prev => [...prev, {
|
|
876
|
+
id: Date.now().toString(),
|
|
877
|
+
content: 'Wallet address mismatch. Please sign in with the correct wallet.',
|
|
878
|
+
role: 'assistant',
|
|
879
|
+
timestamp: new Date().toISOString(),
|
|
880
|
+
button: 'signIn'
|
|
881
|
+
}]);
|
|
882
|
+
return false;
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
catch (error) {
|
|
886
|
+
console.error('Error parsing token:', error);
|
|
887
|
+
// Clear invalid token
|
|
888
|
+
setNxtlinqAITServiceAccessToken('');
|
|
889
|
+
setMessages(prev => [...prev, {
|
|
890
|
+
id: Date.now().toString(),
|
|
891
|
+
content: 'Invalid wallet session. Please sign in again.',
|
|
892
|
+
role: 'assistant',
|
|
893
|
+
timestamp: new Date().toISOString(),
|
|
894
|
+
button: 'signIn'
|
|
895
|
+
}]);
|
|
896
|
+
return false;
|
|
897
|
+
}
|
|
651
898
|
if (!ait) {
|
|
652
899
|
setMessages(prev => [...prev, {
|
|
653
900
|
id: Date.now().toString(),
|
|
@@ -813,20 +1060,22 @@ export const ChatBot = ({ onMessage, onError, onToolUse, presetMessages = [], pl
|
|
|
813
1060
|
setAit(aitInfo);
|
|
814
1061
|
};
|
|
815
1062
|
const savePermissions = async () => {
|
|
816
|
-
setIsLoading(true);
|
|
817
1063
|
setIsDisabled(true);
|
|
818
1064
|
try {
|
|
819
1065
|
await generateAndRegisterAIT();
|
|
820
|
-
|
|
821
|
-
|
|
1066
|
+
// Use Modal notification instead of ChatBot message
|
|
1067
|
+
showSuccess('AIT permissions saved successfully! You can now use the AI agent with your configured permissions.');
|
|
822
1068
|
setShowPermissionForm(false);
|
|
1069
|
+
setIsPermissionFormOpen(false);
|
|
823
1070
|
}
|
|
824
1071
|
catch (error) {
|
|
825
1072
|
console.error('Failed to generate AIT:', error);
|
|
826
|
-
setIsLoading(false);
|
|
827
1073
|
setIsDisabled(false);
|
|
828
1074
|
if (error instanceof Error) {
|
|
829
|
-
|
|
1075
|
+
showError(error.message);
|
|
1076
|
+
}
|
|
1077
|
+
else {
|
|
1078
|
+
showError('Failed to save permissions. Please try again.');
|
|
830
1079
|
}
|
|
831
1080
|
}
|
|
832
1081
|
};
|
|
@@ -872,7 +1121,12 @@ export const ChatBot = ({ onMessage, onError, onToolUse, presetMessages = [], pl
|
|
|
872
1121
|
display: 'flex',
|
|
873
1122
|
justifyContent: 'space-between',
|
|
874
1123
|
alignItems: 'center'
|
|
875
|
-
}, children: [_jsx("h3", { style: { margin: 0 }, children: "AI Agent" }), _jsxs("div", { style: { display: 'flex', gap: '10px' }, children: [_jsx("button", { onClick: () =>
|
|
1124
|
+
}, children: [_jsx("h3", { style: { margin: 0 }, children: "AI Agent" }), _jsxs("div", { style: { display: 'flex', gap: '10px' }, children: [_jsx("button", { onClick: async () => {
|
|
1125
|
+
setShowPermissionForm(true);
|
|
1126
|
+
setIsPermissionFormOpen(true);
|
|
1127
|
+
// Force refresh AIT data when opening the form to show latest permissions
|
|
1128
|
+
await refreshAIT(true);
|
|
1129
|
+
}, style: {
|
|
876
1130
|
background: 'none',
|
|
877
1131
|
border: 'none',
|
|
878
1132
|
color: 'white',
|
|
@@ -981,22 +1235,8 @@ export const ChatBot = ({ onMessage, onError, onToolUse, presetMessages = [], pl
|
|
|
981
1235
|
alignItems: 'center',
|
|
982
1236
|
justifyContent: 'center',
|
|
983
1237
|
zIndex: 2000
|
|
984
|
-
}, children: _jsx(PermissionForm, { hitAddress: hitAddress, permissions: permissions, setPermissions: setPermissions, setIsDisabled: setIsDisabled, onClose: () =>
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
background: '#4caf50',
|
|
989
|
-
color: 'white',
|
|
990
|
-
padding: '12px 24px',
|
|
991
|
-
borderRadius: 8,
|
|
992
|
-
zIndex: 2000,
|
|
993
|
-
fontWeight: 600,
|
|
994
|
-
}, children: ["Saved successfully!", _jsx("button", { style: {
|
|
995
|
-
background: 'none',
|
|
996
|
-
border: 'none',
|
|
997
|
-
color: 'white',
|
|
998
|
-
marginLeft: 16,
|
|
999
|
-
fontSize: 18,
|
|
1000
|
-
cursor: 'pointer'
|
|
1001
|
-
}, onClick: () => setSuccess(false), children: "\u00D7" })] }))] }));
|
|
1238
|
+
}, children: _jsx(PermissionForm, { hitAddress: hitAddress, permissions: permissions, setPermissions: setPermissions, setIsDisabled: setIsDisabled, onClose: () => {
|
|
1239
|
+
setShowPermissionForm(false);
|
|
1240
|
+
setIsPermissionFormOpen(false);
|
|
1241
|
+
}, onConnectWallet: () => connectWallet(false), onSignIn: () => signInWallet(false), onSave: savePermissions, isNeedSignInWithWallet: isNeedSignInWithWallet, walletInfo: walletInfo, onVerifyWallet: handleVerifyWalletClick, serviceId: serviceId, nxtlinqApi: nxtlinqApi, permissionGroup: permissionGroup, isAITLoading: isAITLoading, isWalletLoading: isWalletLoading }) })), notification.show && (_jsx(NotificationModal, { notification: notification, onClose: () => setNotification({ ...notification, show: false }) }))] }));
|
|
1002
1242
|
};
|
package/package.json
CHANGED