agentdev-webui 1.0.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.
Files changed (39) hide show
  1. package/lib/agent-api.js +530 -0
  2. package/lib/auth.js +127 -0
  3. package/lib/config.js +53 -0
  4. package/lib/database.js +762 -0
  5. package/lib/device-flow.js +257 -0
  6. package/lib/email.js +420 -0
  7. package/lib/encryption.js +112 -0
  8. package/lib/github.js +339 -0
  9. package/lib/history.js +143 -0
  10. package/lib/pwa.js +107 -0
  11. package/lib/redis-logs.js +226 -0
  12. package/lib/routes.js +680 -0
  13. package/migrations/000_create_database.sql +33 -0
  14. package/migrations/001_create_agentdev_schema.sql +135 -0
  15. package/migrations/001_create_agentdev_schema.sql.old +100 -0
  16. package/migrations/001_create_agentdev_schema_fixed.sql +135 -0
  17. package/migrations/002_add_github_token.sql +17 -0
  18. package/migrations/003_add_agent_logs_table.sql +23 -0
  19. package/migrations/004_remove_oauth_columns.sql +11 -0
  20. package/migrations/005_add_projects.sql +44 -0
  21. package/migrations/006_project_github_token.sql +7 -0
  22. package/migrations/007_project_repositories.sql +12 -0
  23. package/migrations/008_add_notifications.sql +20 -0
  24. package/migrations/009_unified_oauth.sql +153 -0
  25. package/migrations/README.md +97 -0
  26. package/package.json +37 -0
  27. package/public/css/styles.css +1140 -0
  28. package/public/device.html +384 -0
  29. package/public/docs.html +862 -0
  30. package/public/docs.md +697 -0
  31. package/public/favicon.svg +5 -0
  32. package/public/index.html +271 -0
  33. package/public/js/app.js +2379 -0
  34. package/public/login.html +224 -0
  35. package/public/profile.html +394 -0
  36. package/public/register.html +392 -0
  37. package/public/reset-password.html +349 -0
  38. package/public/verify-email.html +177 -0
  39. package/server.js +1450 -0
@@ -0,0 +1,349 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
6
+ <meta name="theme-color" content="#1a1a2e">
7
+ <meta name="apple-mobile-web-app-capable" content="yes">
8
+ <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
9
+ <meta name="description" content="Reset your Agent Dev password">
10
+ <meta property="og:title" content="Reset Password - Agent Dev">
11
+ <meta property="og:description" content="Reset your Agent Dev password">
12
+ <meta property="og:type" content="website">
13
+ <meta property="og:image" content="/og-image.svg">
14
+ <meta name="twitter:card" content="summary_large_image">
15
+ <meta name="twitter:title" content="Reset Password - Agent Dev">
16
+ <meta name="twitter:description" content="Reset your Agent Dev password">
17
+ <meta name="twitter:image" content="/og-image.svg">
18
+ <title>Reset Password - Agent Dev</title>
19
+ <link rel="icon" type="image/svg+xml" href="/favicon.svg">
20
+ <link rel="apple-touch-icon" href="/icon-192.png">
21
+ <link rel="manifest" href="/manifest.json">
22
+ <link rel="stylesheet" href="/css/styles.css">
23
+ <style>
24
+ .reset-container {
25
+ min-height: 100vh;
26
+ display: flex;
27
+ align-items: center;
28
+ justify-content: center;
29
+ padding: 20px;
30
+ background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
31
+ }
32
+
33
+ .reset-box {
34
+ background: rgba(255, 255, 255, 0.05);
35
+ backdrop-filter: blur(10px);
36
+ border: 1px solid rgba(255, 255, 255, 0.1);
37
+ border-radius: 16px;
38
+ padding: 40px;
39
+ width: 100%;
40
+ max-width: 450px;
41
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
42
+ }
43
+
44
+ .reset-header {
45
+ text-align: center;
46
+ margin-bottom: 32px;
47
+ }
48
+
49
+ .reset-header h1 {
50
+ color: #ffffff;
51
+ font-size: 28px;
52
+ margin-bottom: 8px;
53
+ font-weight: 600;
54
+ }
55
+
56
+ .reset-header p {
57
+ color: rgba(255, 255, 255, 0.6);
58
+ font-size: 14px;
59
+ margin: 0;
60
+ }
61
+
62
+ .form-group {
63
+ margin-bottom: 20px;
64
+ }
65
+
66
+ .form-group label {
67
+ display: block;
68
+ color: rgba(255, 255, 255, 0.8);
69
+ font-size: 14px;
70
+ margin-bottom: 8px;
71
+ font-weight: 500;
72
+ }
73
+
74
+ .form-group input {
75
+ width: 100%;
76
+ padding: 12px 16px;
77
+ background: rgba(255, 255, 255, 0.05);
78
+ border: 1px solid rgba(255, 255, 255, 0.1);
79
+ border-radius: 8px;
80
+ color: #ffffff;
81
+ font-size: 15px;
82
+ transition: all 0.3s ease;
83
+ box-sizing: border-box;
84
+ }
85
+
86
+ .form-group input:focus {
87
+ outline: none;
88
+ border-color: rgba(255, 107, 107, 0.5);
89
+ background: rgba(255, 255, 255, 0.08);
90
+ }
91
+
92
+ .reset-button {
93
+ width: 100%;
94
+ padding: 14px;
95
+ background: linear-gradient(135deg, #ff6b6b 0%, #ee5a6f 100%);
96
+ border: none;
97
+ border-radius: 8px;
98
+ color: white;
99
+ font-size: 16px;
100
+ font-weight: 600;
101
+ cursor: pointer;
102
+ transition: all 0.3s ease;
103
+ margin-top: 24px;
104
+ }
105
+
106
+ .reset-button:hover {
107
+ transform: translateY(-2px);
108
+ box-shadow: 0 8px 20px rgba(255, 107, 107, 0.3);
109
+ }
110
+
111
+ .reset-button:disabled {
112
+ opacity: 0.5;
113
+ cursor: not-allowed;
114
+ transform: none;
115
+ }
116
+
117
+ .message {
118
+ border-radius: 8px;
119
+ padding: 12px 16px;
120
+ font-size: 14px;
121
+ margin-bottom: 20px;
122
+ display: none;
123
+ }
124
+
125
+ .error-message {
126
+ background: rgba(255, 107, 107, 0.1);
127
+ border: 1px solid rgba(255, 107, 107, 0.3);
128
+ color: #ff6b6b;
129
+ }
130
+
131
+ .success-message {
132
+ background: rgba(76, 209, 55, 0.1);
133
+ border: 1px solid rgba(76, 209, 55, 0.3);
134
+ color: #4cd137;
135
+ }
136
+
137
+ .login-link {
138
+ text-align: center;
139
+ margin-top: 24px;
140
+ padding-top: 24px;
141
+ border-top: 1px solid rgba(255, 255, 255, 0.1);
142
+ }
143
+
144
+ .login-link a {
145
+ color: #ff6b6b;
146
+ text-decoration: none;
147
+ font-weight: 500;
148
+ }
149
+ </style>
150
+ </head>
151
+ <body>
152
+ <div class="reset-container">
153
+ <div class="reset-box">
154
+ <div class="reset-header">
155
+ <h1 id="pageTitle">Reset Password</h1>
156
+ <p id="pageDescription">Enter your email to receive a reset link</p>
157
+ </div>
158
+
159
+ <div id="error" class="message error-message"></div>
160
+ <div id="success" class="message success-message"></div>
161
+
162
+ <!-- Request Form (no token) -->
163
+ <form id="requestForm" style="display: none;">
164
+ <div class="form-group">
165
+ <label for="email">Email Address</label>
166
+ <input
167
+ type="email"
168
+ id="email"
169
+ name="email"
170
+ placeholder="you@example.com"
171
+ required
172
+ autofocus
173
+ >
174
+ </div>
175
+
176
+ <button type="submit" class="reset-button" id="requestBtn">
177
+ Send Reset Link
178
+ </button>
179
+ </form>
180
+
181
+ <!-- Confirm Form (with token) -->
182
+ <form id="confirmForm" style="display: none;">
183
+ <div class="form-group">
184
+ <label for="newPassword">New Password</label>
185
+ <input
186
+ type="password"
187
+ id="newPassword"
188
+ name="newPassword"
189
+ placeholder="Enter new password"
190
+ required
191
+ autofocus
192
+ >
193
+ </div>
194
+
195
+ <div class="form-group">
196
+ <label for="confirmPassword">Confirm Password</label>
197
+ <input
198
+ type="password"
199
+ id="confirmPassword"
200
+ name="confirmPassword"
201
+ placeholder="Re-enter new password"
202
+ required
203
+ >
204
+ </div>
205
+
206
+ <button type="submit" class="reset-button" id="confirmBtn">
207
+ Reset Password
208
+ </button>
209
+ </form>
210
+
211
+ <div class="login-link">
212
+ <p style="color: rgba(255, 255, 255, 0.6); font-size: 14px; margin: 0;">
213
+ <a href="/login">Back to Login</a>
214
+ </p>
215
+ </div>
216
+ </div>
217
+ </div>
218
+
219
+ <script>
220
+ const urlParams = new URLSearchParams(window.location.search);
221
+ const token = urlParams.get('token');
222
+
223
+ const requestForm = document.getElementById('requestForm');
224
+ const confirmForm = document.getElementById('confirmForm');
225
+ const pageTitle = document.getElementById('pageTitle');
226
+ const pageDescription = document.getElementById('pageDescription');
227
+ const errorDiv = document.getElementById('error');
228
+ const successDiv = document.getElementById('success');
229
+
230
+ // Show request or confirm form based on token presence
231
+ if (token) {
232
+ pageTitle.textContent = 'Create New Password';
233
+ pageDescription.textContent = 'Enter your new password below';
234
+ confirmForm.style.display = 'block';
235
+ } else {
236
+ requestForm.style.display = 'block';
237
+ }
238
+
239
+ function showError(message) {
240
+ errorDiv.textContent = message;
241
+ errorDiv.style.display = 'block';
242
+ successDiv.style.display = 'none';
243
+ }
244
+
245
+ function showSuccess(message) {
246
+ successDiv.textContent = message;
247
+ successDiv.style.display = 'block';
248
+ errorDiv.style.display = 'none';
249
+ }
250
+
251
+ function hideMessages() {
252
+ errorDiv.style.display = 'none';
253
+ successDiv.style.display = 'none';
254
+ }
255
+
256
+ // Handle password reset request
257
+ requestForm.addEventListener('submit', async (e) => {
258
+ e.preventDefault();
259
+ hideMessages();
260
+
261
+ const email = document.getElementById('email').value.trim();
262
+ const requestBtn = document.getElementById('requestBtn');
263
+
264
+ if (!email) {
265
+ showError('Please enter your email address');
266
+ return;
267
+ }
268
+
269
+ requestBtn.disabled = true;
270
+ requestBtn.textContent = 'Sending...';
271
+
272
+ try {
273
+ const response = await fetch('/api/reset-password/request', {
274
+ method: 'POST',
275
+ headers: { 'Content-Type': 'application/json' },
276
+ body: JSON.stringify({ email })
277
+ });
278
+
279
+ const data = await response.json();
280
+
281
+ if (response.ok) {
282
+ showSuccess('Password reset link sent! Check your email.');
283
+ requestForm.reset();
284
+ } else {
285
+ showError(data.error || 'Failed to send reset link');
286
+ }
287
+ } catch (error) {
288
+ showError('Network error. Please try again.');
289
+ } finally {
290
+ requestBtn.disabled = false;
291
+ requestBtn.textContent = 'Send Reset Link';
292
+ }
293
+ });
294
+
295
+ // Handle password reset confirmation
296
+ confirmForm.addEventListener('submit', async (e) => {
297
+ e.preventDefault();
298
+ hideMessages();
299
+
300
+ const newPassword = document.getElementById('newPassword').value;
301
+ const confirmPassword = document.getElementById('confirmPassword').value;
302
+ const confirmBtn = document.getElementById('confirmBtn');
303
+
304
+ if (!newPassword || !confirmPassword) {
305
+ showError('Please fill in both password fields');
306
+ return;
307
+ }
308
+
309
+ if (newPassword !== confirmPassword) {
310
+ showError('Passwords do not match');
311
+ return;
312
+ }
313
+
314
+ if (newPassword.length < 8) {
315
+ showError('Password must be at least 8 characters');
316
+ return;
317
+ }
318
+
319
+ confirmBtn.disabled = true;
320
+ confirmBtn.textContent = 'Resetting...';
321
+
322
+ try {
323
+ const response = await fetch('/api/reset-password/confirm', {
324
+ method: 'POST',
325
+ headers: { 'Content-Type': 'application/json' },
326
+ body: JSON.stringify({ token, password: newPassword })
327
+ });
328
+
329
+ const data = await response.json();
330
+
331
+ if (response.ok) {
332
+ showSuccess('Password reset successfully! Redirecting to login...');
333
+ setTimeout(() => {
334
+ window.location.href = '/login';
335
+ }, 2000);
336
+ } else {
337
+ showError(data.error || 'Failed to reset password');
338
+ confirmBtn.disabled = false;
339
+ confirmBtn.textContent = 'Reset Password';
340
+ }
341
+ } catch (error) {
342
+ showError('Network error. Please try again.');
343
+ confirmBtn.disabled = false;
344
+ confirmBtn.textContent = 'Reset Password';
345
+ }
346
+ });
347
+ </script>
348
+ </body>
349
+ </html>
@@ -0,0 +1,177 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
6
+ <meta name="theme-color" content="#1a1a2e">
7
+ <meta name="apple-mobile-web-app-capable" content="yes">
8
+ <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
9
+ <meta name="description" content="Verify your Agent Dev email">
10
+ <meta property="og:title" content="Email Verification - Agent Dev">
11
+ <meta property="og:description" content="Verify your Agent Dev email">
12
+ <meta property="og:type" content="website">
13
+ <meta property="og:image" content="/og-image.svg">
14
+ <meta name="twitter:card" content="summary_large_image">
15
+ <meta name="twitter:title" content="Email Verification - Agent Dev">
16
+ <meta name="twitter:description" content="Verify your Agent Dev email">
17
+ <meta name="twitter:image" content="/og-image.svg">
18
+ <title>Email Verification - Agent Dev</title>
19
+ <link rel="icon" type="image/svg+xml" href="/favicon.svg">
20
+ <link rel="apple-touch-icon" href="/icon-192.png">
21
+ <link rel="manifest" href="/manifest.json">
22
+ <link rel="stylesheet" href="/css/styles.css">
23
+ <style>
24
+ .verify-container {
25
+ min-height: 100vh;
26
+ display: flex;
27
+ align-items: center;
28
+ justify-content: center;
29
+ padding: 20px;
30
+ background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
31
+ }
32
+
33
+ .verify-box {
34
+ background: rgba(255, 255, 255, 0.05);
35
+ backdrop-filter: blur(10px);
36
+ border: 1px solid rgba(255, 255, 255, 0.1);
37
+ border-radius: 16px;
38
+ padding: 40px;
39
+ width: 100%;
40
+ max-width: 500px;
41
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
42
+ text-align: center;
43
+ }
44
+
45
+ .verify-icon {
46
+ font-size: 64px;
47
+ margin-bottom: 24px;
48
+ }
49
+
50
+ .verify-title {
51
+ color: #ffffff;
52
+ font-size: 28px;
53
+ margin-bottom: 16px;
54
+ font-weight: 600;
55
+ }
56
+
57
+ .verify-message {
58
+ color: rgba(255, 255, 255, 0.7);
59
+ font-size: 16px;
60
+ line-height: 1.6;
61
+ margin-bottom: 32px;
62
+ }
63
+
64
+ .spinner {
65
+ border: 4px solid rgba(255, 255, 255, 0.1);
66
+ border-top: 4px solid #ff6b6b;
67
+ border-radius: 50%;
68
+ width: 50px;
69
+ height: 50px;
70
+ animation: spin 1s linear infinite;
71
+ margin: 20px auto;
72
+ }
73
+
74
+ @keyframes spin {
75
+ 0% { transform: rotate(0deg); }
76
+ 100% { transform: rotate(360deg); }
77
+ }
78
+
79
+ .success { color: #4cd137; }
80
+ .error { color: #ff6b6b; }
81
+
82
+ .verify-button {
83
+ display: inline-block;
84
+ padding: 14px 32px;
85
+ background: linear-gradient(135deg, #ff6b6b 0%, #ee5a6f 100%);
86
+ border: none;
87
+ border-radius: 8px;
88
+ color: white;
89
+ font-size: 16px;
90
+ font-weight: 600;
91
+ text-decoration: none;
92
+ cursor: pointer;
93
+ transition: all 0.3s ease;
94
+ margin-top: 24px;
95
+ }
96
+
97
+ .verify-button:hover {
98
+ transform: translateY(-2px);
99
+ box-shadow: 0 8px 20px rgba(255, 107, 107, 0.3);
100
+ }
101
+ </style>
102
+ </head>
103
+ <body>
104
+ <div class="verify-container">
105
+ <div class="verify-box">
106
+ <div id="verifying" style="display: block;">
107
+ <div class="verify-icon">📧</div>
108
+ <h1 class="verify-title">Verifying Email...</h1>
109
+ <div class="spinner"></div>
110
+ <p class="verify-message">Please wait while we verify your email address.</p>
111
+ </div>
112
+
113
+ <div id="success" style="display: none;">
114
+ <div class="verify-icon success">✅</div>
115
+ <h1 class="verify-title">Email Verified!</h1>
116
+ <p class="verify-message">
117
+ Your email has been successfully verified. You can now sign in to your account.
118
+ </p>
119
+ <a href="/login" class="verify-button">Go to Login</a>
120
+ </div>
121
+
122
+ <div id="error" style="display: none;">
123
+ <div class="verify-icon error">❌</div>
124
+ <h1 class="verify-title">Verification Failed</h1>
125
+ <p class="verify-message" id="errorMessage">
126
+ The verification link is invalid or has expired.
127
+ </p>
128
+ <a href="/register" class="verify-button">Register Again</a>
129
+ </div>
130
+ </div>
131
+ </div>
132
+
133
+ <script>
134
+ const urlParams = new URLSearchParams(window.location.search);
135
+ const token = urlParams.get('token');
136
+
137
+ if (!token) {
138
+ showError('No verification token provided');
139
+ } else {
140
+ verifyEmail(token);
141
+ }
142
+
143
+ async function verifyEmail(token) {
144
+ try {
145
+ const response = await fetch('/api/verify-email', {
146
+ method: 'POST',
147
+ headers: { 'Content-Type': 'application/json' },
148
+ body: JSON.stringify({ token })
149
+ });
150
+
151
+ const data = await response.json();
152
+
153
+ if (response.ok && data.success) {
154
+ showSuccess();
155
+ } else {
156
+ showError(data.error || 'Verification failed');
157
+ }
158
+ } catch (error) {
159
+ showError('Network error. Please try again.');
160
+ }
161
+ }
162
+
163
+ function showSuccess() {
164
+ document.getElementById('verifying').style.display = 'none';
165
+ document.getElementById('success').style.display = 'block';
166
+ document.getElementById('error').style.display = 'none';
167
+ }
168
+
169
+ function showError(message) {
170
+ document.getElementById('verifying').style.display = 'none';
171
+ document.getElementById('success').style.display = 'none';
172
+ document.getElementById('error').style.display = 'block';
173
+ document.getElementById('errorMessage').textContent = message;
174
+ }
175
+ </script>
176
+ </body>
177
+ </html>