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,226 @@
1
+ const Redis = require('ioredis');
2
+ const config = require('./config');
3
+
4
+ // Two Redis clients: one for pub/sub, one for regular commands
5
+ let pubClient = null;
6
+ let subClient = null;
7
+ let subscribers = new Map(); // channel -> Set of callback functions
8
+
9
+ function connect() {
10
+ if (pubClient && subClient) {
11
+ return { pubClient, subClient };
12
+ }
13
+
14
+ const redisConfig = {
15
+ host: config.REDIS_HOST || 'localhost',
16
+ port: config.REDIS_PORT || 6379,
17
+ db: config.REDIS_DB || 3,
18
+ retryStrategy: (times) => {
19
+ const delay = Math.min(times * 50, 2000);
20
+ return delay;
21
+ }
22
+ };
23
+
24
+ pubClient = new Redis(redisConfig);
25
+ subClient = new Redis(redisConfig);
26
+
27
+ pubClient.on('error', (err) => console.error('Redis Pub Client Error:', err));
28
+ subClient.on('error', (err) => console.error('Redis Sub Client Error:', err));
29
+
30
+ // Handle incoming messages
31
+ subClient.on('message', (channel, message) => {
32
+ const callbacks = subscribers.get(channel);
33
+ if (callbacks) {
34
+ try {
35
+ const data = JSON.parse(message);
36
+ callbacks.forEach(cb => cb(data));
37
+ } catch (e) {
38
+ console.error('Error parsing Redis message:', e);
39
+ }
40
+ }
41
+ });
42
+
43
+ // Handle pattern messages (for wildcard subscriptions)
44
+ subClient.on('pmessage', (pattern, channel, message) => {
45
+ const callbacks = subscribers.get(pattern);
46
+ if (callbacks) {
47
+ try {
48
+ const data = JSON.parse(message);
49
+ // Extract agentId from channel (agentdev:logs:{agentId})
50
+ const agentId = data.agent || channel.replace('agentdev:logs:', '');
51
+ callbacks.forEach(cb => cb(agentId, data));
52
+ } catch (e) {
53
+ console.error('Error parsing Redis pmessage:', e);
54
+ }
55
+ }
56
+ });
57
+
58
+ return { pubClient, subClient };
59
+ }
60
+
61
+ /**
62
+ * Publish a log message to Redis channel
63
+ * @param {string} agentId - Agent identifier
64
+ * @param {object} logData - Log data { content, level, timestamp }
65
+ */
66
+ async function publishLog(agentId, logData) {
67
+ const { pubClient } = connect();
68
+ const channel = `agentdev:logs:${agentId}`;
69
+ const message = JSON.stringify({
70
+ agent: agentId,
71
+ timestamp: logData.timestamp || new Date().toISOString(),
72
+ level: logData.level || 'INFO',
73
+ content: logData.content
74
+ });
75
+
76
+ await pubClient.publish(channel, message);
77
+ }
78
+
79
+ /**
80
+ * Subscribe to agent logs
81
+ * @param {string} agentId - Agent identifier (or '*' for all agents)
82
+ * @param {function} callback - Callback function(data, channel)
83
+ */
84
+ async function subscribeToLogs(agentId, callback) {
85
+ const { subClient } = connect();
86
+
87
+ if (agentId === '*') {
88
+ // Subscribe to all agent logs with pattern
89
+ const pattern = 'agentdev:logs:*';
90
+ if (!subscribers.has(pattern)) {
91
+ subscribers.set(pattern, new Set());
92
+ await subClient.psubscribe(pattern);
93
+ }
94
+ subscribers.get(pattern).add(callback);
95
+ return pattern;
96
+ } else {
97
+ // Subscribe to specific agent
98
+ const channel = `agentdev:logs:${agentId}`;
99
+ if (!subscribers.has(channel)) {
100
+ subscribers.set(channel, new Set());
101
+ await subClient.subscribe(channel);
102
+ }
103
+ subscribers.get(channel).add(callback);
104
+ return channel;
105
+ }
106
+ }
107
+
108
+ /**
109
+ * Unsubscribe from agent logs
110
+ * @param {string} channelOrPattern - Channel or pattern returned from subscribeToLogs
111
+ * @param {function} callback - Callback function to remove
112
+ */
113
+ async function unsubscribeFromLogs(channelOrPattern, callback) {
114
+ const { subClient } = connect();
115
+ const callbacks = subscribers.get(channelOrPattern);
116
+
117
+ if (callbacks) {
118
+ callbacks.delete(callback);
119
+
120
+ // If no more callbacks, unsubscribe from channel
121
+ if (callbacks.size === 0) {
122
+ subscribers.delete(channelOrPattern);
123
+ if (channelOrPattern.includes('*')) {
124
+ await subClient.punsubscribe(channelOrPattern);
125
+ } else {
126
+ await subClient.unsubscribe(channelOrPattern);
127
+ }
128
+ }
129
+ }
130
+ }
131
+
132
+ /**
133
+ * Publish agent status update
134
+ * @param {object} statusData - Status data { agents: [...], history: [...] }
135
+ */
136
+ async function publishAgentStatus(statusData) {
137
+ const { pubClient } = connect();
138
+ const channel = 'agentdev:agents:status';
139
+ const message = JSON.stringify(statusData);
140
+ await pubClient.publish(channel, message);
141
+ }
142
+
143
+ /**
144
+ * Subscribe to agent status updates
145
+ * @param {function} callback - Callback function(data)
146
+ */
147
+ async function subscribeToAgentStatus(callback) {
148
+ const { subClient } = connect();
149
+ const channel = 'agentdev:agents:status';
150
+
151
+ if (!subscribers.has(channel)) {
152
+ subscribers.set(channel, new Set());
153
+ await subClient.subscribe(channel);
154
+ }
155
+ subscribers.get(channel).add(callback);
156
+ return channel;
157
+ }
158
+
159
+ /**
160
+ * Store value in Redis with optional TTL
161
+ * @param {string} key - Redis key
162
+ * @param {string|object} value - Value to store (objects are JSON stringified)
163
+ * @param {number} ttl - Time to live in seconds (optional)
164
+ */
165
+ async function set(key, value, ttl) {
166
+ const { pubClient } = connect();
167
+ const stringValue = typeof value === 'object' ? JSON.stringify(value) : value;
168
+
169
+ if (ttl) {
170
+ await pubClient.setex(key, ttl, stringValue);
171
+ } else {
172
+ await pubClient.set(key, stringValue);
173
+ }
174
+ }
175
+
176
+ /**
177
+ * Get value from Redis
178
+ * @param {string} key - Redis key
179
+ * @returns {string|object|null} - Value (auto-parsed if JSON)
180
+ */
181
+ async function get(key) {
182
+ const { pubClient } = connect();
183
+ const value = await pubClient.get(key);
184
+
185
+ if (!value) return null;
186
+
187
+ // Try to parse as JSON
188
+ try {
189
+ return JSON.parse(value);
190
+ } catch (e) {
191
+ return value;
192
+ }
193
+ }
194
+
195
+ /**
196
+ * Delete key from Redis
197
+ * @param {string} key - Redis key
198
+ */
199
+ async function del(key) {
200
+ const { pubClient } = connect();
201
+ await pubClient.del(key);
202
+ }
203
+
204
+ /**
205
+ * Close Redis connections
206
+ */
207
+ function disconnect() {
208
+ if (pubClient) pubClient.quit();
209
+ if (subClient) subClient.quit();
210
+ pubClient = null;
211
+ subClient = null;
212
+ subscribers.clear();
213
+ }
214
+
215
+ module.exports = {
216
+ connect,
217
+ disconnect,
218
+ publishLog,
219
+ subscribeToLogs,
220
+ unsubscribeFromLogs,
221
+ publishAgentStatus,
222
+ subscribeToAgentStatus,
223
+ set,
224
+ get,
225
+ del
226
+ };