agent-relay 2.0.23 → 2.0.25

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 (168) hide show
  1. package/dist/src/cli/index.js +160 -17
  2. package/package.json +18 -52
  3. package/packages/api-types/package.json +1 -1
  4. package/packages/bridge/package.json +8 -8
  5. package/packages/cli-tester/package.json +1 -1
  6. package/packages/config/package.json +2 -2
  7. package/packages/continuity/package.json +1 -1
  8. package/packages/daemon/package.json +12 -12
  9. package/packages/hooks/package.json +4 -4
  10. package/packages/mcp/package.json +2 -2
  11. package/packages/memory/package.json +2 -2
  12. package/packages/policy/package.json +2 -2
  13. package/packages/protocol/package.json +1 -1
  14. package/packages/resiliency/package.json +1 -1
  15. package/packages/sdk/package.json +2 -2
  16. package/packages/spawner/package.json +1 -1
  17. package/packages/state/package.json +1 -1
  18. package/packages/storage/package.json +2 -2
  19. package/packages/telemetry/package.json +1 -1
  20. package/packages/trajectory/package.json +2 -2
  21. package/packages/user-directory/package.json +2 -2
  22. package/packages/utils/package.json +1 -1
  23. package/packages/wrapper/package.json +6 -6
  24. package/deploy/init-db.sql +0 -5
  25. package/deploy/scripts/setup-fly-workspaces.sh +0 -69
  26. package/deploy/scripts/setup-railway.sh +0 -75
  27. package/dist/src/cloud/index.d.ts +0 -8
  28. package/dist/src/cloud/index.js +0 -8
  29. package/packages/cloud/dist/api/admin.d.ts +0 -8
  30. package/packages/cloud/dist/api/admin.js +0 -225
  31. package/packages/cloud/dist/api/auth.d.ts +0 -20
  32. package/packages/cloud/dist/api/auth.js +0 -138
  33. package/packages/cloud/dist/api/billing.d.ts +0 -7
  34. package/packages/cloud/dist/api/billing.js +0 -564
  35. package/packages/cloud/dist/api/cli-pty-runner.d.ts +0 -53
  36. package/packages/cloud/dist/api/cli-pty-runner.js +0 -175
  37. package/packages/cloud/dist/api/codex-auth-helper.d.ts +0 -21
  38. package/packages/cloud/dist/api/codex-auth-helper.js +0 -327
  39. package/packages/cloud/dist/api/consensus.d.ts +0 -13
  40. package/packages/cloud/dist/api/consensus.js +0 -261
  41. package/packages/cloud/dist/api/coordinators.d.ts +0 -8
  42. package/packages/cloud/dist/api/coordinators.js +0 -750
  43. package/packages/cloud/dist/api/daemons.d.ts +0 -12
  44. package/packages/cloud/dist/api/daemons.js +0 -535
  45. package/packages/cloud/dist/api/email-auth.d.ts +0 -11
  46. package/packages/cloud/dist/api/email-auth.js +0 -347
  47. package/packages/cloud/dist/api/generic-webhooks.d.ts +0 -8
  48. package/packages/cloud/dist/api/generic-webhooks.js +0 -129
  49. package/packages/cloud/dist/api/git.d.ts +0 -8
  50. package/packages/cloud/dist/api/git.js +0 -269
  51. package/packages/cloud/dist/api/github-app.d.ts +0 -11
  52. package/packages/cloud/dist/api/github-app.js +0 -223
  53. package/packages/cloud/dist/api/middleware/planLimits.d.ts +0 -43
  54. package/packages/cloud/dist/api/middleware/planLimits.js +0 -202
  55. package/packages/cloud/dist/api/monitoring.d.ts +0 -11
  56. package/packages/cloud/dist/api/monitoring.js +0 -578
  57. package/packages/cloud/dist/api/nango-auth.d.ts +0 -9
  58. package/packages/cloud/dist/api/nango-auth.js +0 -741
  59. package/packages/cloud/dist/api/onboarding.d.ts +0 -15
  60. package/packages/cloud/dist/api/onboarding.js +0 -679
  61. package/packages/cloud/dist/api/policy.d.ts +0 -8
  62. package/packages/cloud/dist/api/policy.js +0 -229
  63. package/packages/cloud/dist/api/provider-env.d.ts +0 -26
  64. package/packages/cloud/dist/api/provider-env.js +0 -141
  65. package/packages/cloud/dist/api/providers.d.ts +0 -7
  66. package/packages/cloud/dist/api/providers.js +0 -574
  67. package/packages/cloud/dist/api/repos.d.ts +0 -8
  68. package/packages/cloud/dist/api/repos.js +0 -577
  69. package/packages/cloud/dist/api/sessions.d.ts +0 -11
  70. package/packages/cloud/dist/api/sessions.js +0 -302
  71. package/packages/cloud/dist/api/teams.d.ts +0 -7
  72. package/packages/cloud/dist/api/teams.js +0 -281
  73. package/packages/cloud/dist/api/test-helpers.d.ts +0 -10
  74. package/packages/cloud/dist/api/test-helpers.js +0 -745
  75. package/packages/cloud/dist/api/usage.d.ts +0 -7
  76. package/packages/cloud/dist/api/usage.js +0 -111
  77. package/packages/cloud/dist/api/webhooks.d.ts +0 -8
  78. package/packages/cloud/dist/api/webhooks.js +0 -645
  79. package/packages/cloud/dist/api/workspaces.d.ts +0 -25
  80. package/packages/cloud/dist/api/workspaces.js +0 -1799
  81. package/packages/cloud/dist/billing/index.d.ts +0 -9
  82. package/packages/cloud/dist/billing/index.js +0 -9
  83. package/packages/cloud/dist/billing/plans.d.ts +0 -39
  84. package/packages/cloud/dist/billing/plans.js +0 -245
  85. package/packages/cloud/dist/billing/service.d.ts +0 -80
  86. package/packages/cloud/dist/billing/service.js +0 -388
  87. package/packages/cloud/dist/billing/types.d.ts +0 -141
  88. package/packages/cloud/dist/billing/types.js +0 -7
  89. package/packages/cloud/dist/config.d.ts +0 -5
  90. package/packages/cloud/dist/config.js +0 -5
  91. package/packages/cloud/dist/db/bulk-ingest.d.ts +0 -89
  92. package/packages/cloud/dist/db/bulk-ingest.js +0 -268
  93. package/packages/cloud/dist/db/drizzle.d.ts +0 -290
  94. package/packages/cloud/dist/db/drizzle.js +0 -1422
  95. package/packages/cloud/dist/db/index.d.ts +0 -56
  96. package/packages/cloud/dist/db/index.js +0 -70
  97. package/packages/cloud/dist/db/schema.d.ts +0 -5117
  98. package/packages/cloud/dist/db/schema.js +0 -656
  99. package/packages/cloud/dist/index.d.ts +0 -11
  100. package/packages/cloud/dist/index.js +0 -38
  101. package/packages/cloud/dist/provisioner/index.d.ts +0 -207
  102. package/packages/cloud/dist/provisioner/index.js +0 -2118
  103. package/packages/cloud/dist/server.d.ts +0 -17
  104. package/packages/cloud/dist/server.js +0 -2055
  105. package/packages/cloud/dist/services/auto-scaler.d.ts +0 -152
  106. package/packages/cloud/dist/services/auto-scaler.js +0 -439
  107. package/packages/cloud/dist/services/capacity-manager.d.ts +0 -148
  108. package/packages/cloud/dist/services/capacity-manager.js +0 -449
  109. package/packages/cloud/dist/services/ci-agent-spawner.d.ts +0 -49
  110. package/packages/cloud/dist/services/ci-agent-spawner.js +0 -373
  111. package/packages/cloud/dist/services/cloud-message-bus.d.ts +0 -28
  112. package/packages/cloud/dist/services/cloud-message-bus.js +0 -19
  113. package/packages/cloud/dist/services/compute-enforcement.d.ts +0 -57
  114. package/packages/cloud/dist/services/compute-enforcement.js +0 -175
  115. package/packages/cloud/dist/services/coordinator.d.ts +0 -62
  116. package/packages/cloud/dist/services/coordinator.js +0 -389
  117. package/packages/cloud/dist/services/index.d.ts +0 -17
  118. package/packages/cloud/dist/services/index.js +0 -25
  119. package/packages/cloud/dist/services/intro-expiration.d.ts +0 -60
  120. package/packages/cloud/dist/services/intro-expiration.js +0 -252
  121. package/packages/cloud/dist/services/mention-handler.d.ts +0 -65
  122. package/packages/cloud/dist/services/mention-handler.js +0 -405
  123. package/packages/cloud/dist/services/nango.d.ts +0 -219
  124. package/packages/cloud/dist/services/nango.js +0 -424
  125. package/packages/cloud/dist/services/persistence.d.ts +0 -131
  126. package/packages/cloud/dist/services/persistence.js +0 -200
  127. package/packages/cloud/dist/services/planLimits.d.ts +0 -147
  128. package/packages/cloud/dist/services/planLimits.js +0 -335
  129. package/packages/cloud/dist/services/presence-registry.d.ts +0 -56
  130. package/packages/cloud/dist/services/presence-registry.js +0 -91
  131. package/packages/cloud/dist/services/scaling-orchestrator.d.ts +0 -159
  132. package/packages/cloud/dist/services/scaling-orchestrator.js +0 -502
  133. package/packages/cloud/dist/services/scaling-policy.d.ts +0 -121
  134. package/packages/cloud/dist/services/scaling-policy.js +0 -415
  135. package/packages/cloud/dist/services/ssh-security.d.ts +0 -31
  136. package/packages/cloud/dist/services/ssh-security.js +0 -63
  137. package/packages/cloud/dist/services/workspace-keepalive.d.ts +0 -76
  138. package/packages/cloud/dist/services/workspace-keepalive.js +0 -234
  139. package/packages/cloud/dist/shims/consensus.d.ts +0 -23
  140. package/packages/cloud/dist/shims/consensus.js +0 -5
  141. package/packages/cloud/dist/webhooks/index.d.ts +0 -24
  142. package/packages/cloud/dist/webhooks/index.js +0 -29
  143. package/packages/cloud/dist/webhooks/parsers/github.d.ts +0 -8
  144. package/packages/cloud/dist/webhooks/parsers/github.js +0 -234
  145. package/packages/cloud/dist/webhooks/parsers/index.d.ts +0 -23
  146. package/packages/cloud/dist/webhooks/parsers/index.js +0 -30
  147. package/packages/cloud/dist/webhooks/parsers/linear.d.ts +0 -9
  148. package/packages/cloud/dist/webhooks/parsers/linear.js +0 -258
  149. package/packages/cloud/dist/webhooks/parsers/slack.d.ts +0 -9
  150. package/packages/cloud/dist/webhooks/parsers/slack.js +0 -214
  151. package/packages/cloud/dist/webhooks/responders/github.d.ts +0 -8
  152. package/packages/cloud/dist/webhooks/responders/github.js +0 -73
  153. package/packages/cloud/dist/webhooks/responders/index.d.ts +0 -23
  154. package/packages/cloud/dist/webhooks/responders/index.js +0 -30
  155. package/packages/cloud/dist/webhooks/responders/linear.d.ts +0 -9
  156. package/packages/cloud/dist/webhooks/responders/linear.js +0 -149
  157. package/packages/cloud/dist/webhooks/responders/slack.d.ts +0 -20
  158. package/packages/cloud/dist/webhooks/responders/slack.js +0 -178
  159. package/packages/cloud/dist/webhooks/router.d.ts +0 -25
  160. package/packages/cloud/dist/webhooks/router.js +0 -504
  161. package/packages/cloud/dist/webhooks/rules-engine.d.ts +0 -24
  162. package/packages/cloud/dist/webhooks/rules-engine.js +0 -287
  163. package/packages/cloud/dist/webhooks/types.d.ts +0 -186
  164. package/packages/cloud/dist/webhooks/types.js +0 -8
  165. package/packages/cloud/package.json +0 -60
  166. package/scripts/run-migrations.js +0 -43
  167. package/scripts/setup-stripe-products.ts +0 -312
  168. package/scripts/verify-schema.js +0 -134
@@ -1,12 +0,0 @@
1
- /**
2
- * Linked Daemons API Routes
3
- *
4
- * Allows local agent-relay instances to register and link with cloud.
5
- * This enables:
6
- * - Credential sync from cloud to local
7
- * - Remote monitoring of local agents
8
- * - Cross-machine agent discovery
9
- * - Centralized dashboard for all instances
10
- */
11
- export declare const daemonsRouter: import("express-serve-static-core").Router;
12
- //# sourceMappingURL=daemons.d.ts.map
@@ -1,535 +0,0 @@
1
- /**
2
- * Linked Daemons API Routes
3
- *
4
- * Allows local agent-relay instances to register and link with cloud.
5
- * This enables:
6
- * - Credential sync from cloud to local
7
- * - Remote monitoring of local agents
8
- * - Cross-machine agent discovery
9
- * - Centralized dashboard for all instances
10
- */
11
- import { Router } from 'express';
12
- import { randomBytes, createHash } from 'crypto';
13
- import { requireAuth } from './auth.js';
14
- import { db } from '../db/index.js';
15
- import { getOnlineUsersForDiscovery, isUserOnline } from '../services/presence-registry.js';
16
- import { cloudMessageBus } from '../services/cloud-message-bus.js';
17
- export const daemonsRouter = Router();
18
- /**
19
- * Generate a secure API key
20
- */
21
- function generateApiKey() {
22
- // Format: ar_live_<32 random bytes as hex>
23
- const random = randomBytes(32).toString('hex');
24
- return `ar_live_${random}`;
25
- }
26
- /**
27
- * Hash an API key for storage
28
- */
29
- function hashApiKey(apiKey) {
30
- return createHash('sha256').update(apiKey).digest('hex');
31
- }
32
- /**
33
- * POST /api/daemons/link
34
- * Register a local daemon with the cloud (requires browser auth first)
35
- *
36
- * Flow:
37
- * 1. User runs `agent-relay cloud link` in terminal
38
- * 2. CLI opens browser to /cloud/link?code=<temp_code>
39
- * 3. User authenticates (or is already logged in)
40
- * 4. Browser shows confirmation, user clicks "Link"
41
- * 5. Server generates API key and returns to CLI via the temp code
42
- */
43
- daemonsRouter.post('/link', requireAuth, async (req, res) => {
44
- const userId = req.session.userId;
45
- const { name, machineId, metadata, workspaceId } = req.body;
46
- if (!machineId || typeof machineId !== 'string') {
47
- return res.status(400).json({ error: 'machineId is required' });
48
- }
49
- try {
50
- // Validate workspace ownership if provided
51
- if (workspaceId) {
52
- const workspace = await db.workspaces.findById(workspaceId);
53
- if (!workspace) {
54
- return res.status(404).json({ error: 'Workspace not found' });
55
- }
56
- if (workspace.userId !== userId) {
57
- // Check if user is a member of the workspace
58
- const member = await db.workspaceMembers.findMembership(workspaceId, userId);
59
- if (!member) {
60
- return res.status(403).json({ error: 'Not authorized to link to this workspace' });
61
- }
62
- }
63
- }
64
- // Check if this machine is already linked
65
- const existing = await db.linkedDaemons.findByMachineId(userId, machineId);
66
- if (existing) {
67
- // Regenerate API key for existing link
68
- const apiKey = generateApiKey();
69
- const apiKeyHash = hashApiKey(apiKey);
70
- await db.linkedDaemons.update(existing.id, {
71
- name: name || existing.name,
72
- apiKeyHash,
73
- workspaceId: workspaceId || existing.workspaceId,
74
- metadata: metadata || existing.metadata,
75
- status: 'online',
76
- lastSeenAt: new Date(),
77
- });
78
- return res.json({
79
- success: true,
80
- daemonId: existing.id,
81
- workspaceId: workspaceId || existing.workspaceId,
82
- apiKey, // Only returned once!
83
- message: 'Daemon re-linked with new API key',
84
- });
85
- }
86
- // Create new linked daemon
87
- const apiKey = generateApiKey();
88
- const apiKeyHash = hashApiKey(apiKey);
89
- const daemon = await db.linkedDaemons.create({
90
- userId,
91
- workspaceId: workspaceId || null,
92
- name: name || `Daemon on ${machineId.substring(0, 8)}`,
93
- machineId,
94
- apiKeyHash,
95
- status: 'online',
96
- metadata: metadata || {},
97
- });
98
- res.status(201).json({
99
- success: true,
100
- daemonId: daemon.id,
101
- workspaceId: workspaceId || null,
102
- apiKey, // Only returned once - user must save this!
103
- message: 'Daemon linked successfully. Save your API key - it cannot be retrieved later.',
104
- });
105
- }
106
- catch (error) {
107
- console.error('Error linking daemon:', error);
108
- res.status(500).json({ error: 'Failed to link daemon' });
109
- }
110
- });
111
- /**
112
- * GET /api/daemons
113
- * List user's linked daemons
114
- */
115
- daemonsRouter.get('/', requireAuth, async (req, res) => {
116
- const userId = req.session.userId;
117
- try {
118
- const daemons = await db.linkedDaemons.findByUserId(userId);
119
- res.json({
120
- daemons: daemons.map((d) => ({
121
- id: d.id,
122
- name: d.name,
123
- machineId: d.machineId,
124
- status: d.status,
125
- lastSeenAt: d.lastSeenAt,
126
- metadata: d.metadata,
127
- createdAt: d.createdAt,
128
- })),
129
- });
130
- }
131
- catch (error) {
132
- console.error('Error listing daemons:', error);
133
- res.status(500).json({ error: 'Failed to list daemons' });
134
- }
135
- });
136
- /**
137
- * GET /api/daemons/workspace/:workspaceId/agents
138
- * Get local agents for a specific workspace
139
- */
140
- daemonsRouter.get('/workspace/:workspaceId/agents', requireAuth, async (req, res) => {
141
- const userId = req.session.userId;
142
- const workspaceId = req.params.workspaceId;
143
- try {
144
- // Verify user has access to this workspace
145
- const workspace = await db.workspaces.findById(workspaceId);
146
- if (!workspace) {
147
- return res.status(404).json({ error: 'Workspace not found' });
148
- }
149
- // Check if user owns the workspace or is a member
150
- if (workspace.userId !== userId) {
151
- const member = await db.workspaceMembers.findMembership(workspaceId, userId);
152
- if (!member) {
153
- return res.status(403).json({ error: 'Not authorized to access this workspace' });
154
- }
155
- }
156
- // Get all linked daemons for this workspace
157
- const daemons = await db.linkedDaemons.findByWorkspaceId(workspaceId);
158
- // Extract agents from each daemon's metadata
159
- const localAgents = daemons.flatMap((daemon) => {
160
- const metadata = daemon.metadata;
161
- const agents = metadata?.agents || [];
162
- return agents.map((agent) => ({
163
- name: agent.name,
164
- status: agent.status,
165
- isLocal: true,
166
- isHuman: agent.isHuman,
167
- avatarUrl: agent.avatarUrl,
168
- daemonId: daemon.id,
169
- daemonName: daemon.name,
170
- daemonStatus: daemon.status,
171
- machineId: daemon.machineId,
172
- lastSeenAt: daemon.lastSeenAt,
173
- }));
174
- });
175
- res.json({
176
- agents: localAgents,
177
- daemons: daemons.map((d) => ({
178
- id: d.id,
179
- name: d.name,
180
- machineId: d.machineId,
181
- status: d.status,
182
- lastSeenAt: d.lastSeenAt,
183
- })),
184
- });
185
- }
186
- catch (error) {
187
- console.error('Error fetching local agents:', error);
188
- res.status(500).json({ error: 'Failed to fetch local agents' });
189
- }
190
- });
191
- /**
192
- * DELETE /api/daemons/:id
193
- * Unlink a daemon
194
- */
195
- daemonsRouter.delete('/:id', requireAuth, async (req, res) => {
196
- const userId = req.session.userId;
197
- const id = req.params.id;
198
- try {
199
- const daemon = await db.linkedDaemons.findById(id);
200
- if (!daemon) {
201
- return res.status(404).json({ error: 'Daemon not found' });
202
- }
203
- if (daemon.userId !== userId) {
204
- return res.status(403).json({ error: 'Unauthorized' });
205
- }
206
- await db.linkedDaemons.delete(id);
207
- res.json({ success: true, message: 'Daemon unlinked' });
208
- }
209
- catch (error) {
210
- console.error('Error unlinking daemon:', error);
211
- res.status(500).json({ error: 'Failed to unlink daemon' });
212
- }
213
- });
214
- // ============================================================================
215
- // Daemon API (authenticated with API key, not session)
216
- // These endpoints are called by local daemons, not browsers
217
- // ============================================================================
218
- /**
219
- * Middleware to authenticate daemon by API key
220
- */
221
- async function requireDaemonAuth(req, res, next) {
222
- const authHeader = req.headers.authorization;
223
- if (!authHeader || !authHeader.startsWith('Bearer ar_live_')) {
224
- res.status(401).json({ error: 'Invalid API key format' });
225
- return;
226
- }
227
- const apiKey = authHeader.replace('Bearer ', '');
228
- const apiKeyHash = hashApiKey(apiKey);
229
- try {
230
- const daemon = await db.linkedDaemons.findByApiKeyHash(apiKeyHash);
231
- if (!daemon) {
232
- res.status(401).json({ error: 'Invalid API key' });
233
- return;
234
- }
235
- // Update last seen
236
- await db.linkedDaemons.updateLastSeen(daemon.id);
237
- // Attach daemon info to request
238
- req.daemon = daemon;
239
- next();
240
- }
241
- catch (error) {
242
- console.error('Daemon auth error:', error);
243
- res.status(500).json({ error: 'Authentication failed' });
244
- }
245
- }
246
- /**
247
- * POST /api/daemons/heartbeat
248
- * Daemon heartbeat - reports status and gets any pending commands
249
- */
250
- daemonsRouter.post('/heartbeat', requireDaemonAuth, async (req, res) => {
251
- const daemon = req.daemon;
252
- const { agents, metrics } = req.body;
253
- try {
254
- // Update daemon status with agent info
255
- await db.linkedDaemons.update(daemon.id, {
256
- status: 'online',
257
- metadata: {
258
- ...daemon.metadata,
259
- agents: agents || [],
260
- metrics: metrics || {},
261
- lastHeartbeat: new Date().toISOString(),
262
- },
263
- });
264
- // Check for any pending commands (credential updates, etc.)
265
- const pendingUpdates = await db.linkedDaemons.getPendingUpdates(daemon.id);
266
- res.json({
267
- success: true,
268
- commands: pendingUpdates,
269
- });
270
- }
271
- catch (error) {
272
- console.error('Error processing heartbeat:', error);
273
- res.status(500).json({ error: 'Failed to process heartbeat' });
274
- }
275
- });
276
- /**
277
- * GET /api/daemons/credentials
278
- * Get credentials for the daemon's user
279
- *
280
- * Note: Tokens are no longer stored centrally. CLI tools authenticate directly
281
- * on workspace/local instances. This endpoint returns connected provider info only.
282
- */
283
- daemonsRouter.get('/credentials', requireDaemonAuth, async (req, res) => {
284
- const daemon = req.daemon;
285
- try {
286
- // Get connected providers for this user (no tokens stored centrally)
287
- const credentials = await db.credentials.findByUserId(daemon.userId);
288
- // Return provider info without tokens
289
- const providers = credentials.map((cred) => ({
290
- provider: cred.provider,
291
- providerAccountEmail: cred.providerAccountEmail,
292
- connectedAt: cred.createdAt,
293
- }));
294
- res.json({
295
- providers,
296
- note: 'Tokens are authenticated locally on workspace instances via CLI.',
297
- });
298
- }
299
- catch (error) {
300
- console.error('Error fetching credentials:', error);
301
- res.status(500).json({ error: 'Failed to fetch credentials' });
302
- }
303
- });
304
- /**
305
- * POST /api/daemons/agents
306
- * Report agent list to cloud (for cross-machine discovery)
307
- */
308
- daemonsRouter.post('/agents', requireDaemonAuth, async (req, res) => {
309
- const daemon = req.daemon;
310
- const { agents } = req.body;
311
- if (!agents || !Array.isArray(agents)) {
312
- return res.status(400).json({ error: 'agents array is required' });
313
- }
314
- try {
315
- // Store agent list in daemon metadata
316
- await db.linkedDaemons.update(daemon.id, {
317
- metadata: {
318
- ...daemon.metadata,
319
- agents,
320
- lastAgentSync: new Date().toISOString(),
321
- },
322
- });
323
- // Get agents from all linked daemons for this user (cross-machine discovery)
324
- const allDaemons = await db.linkedDaemons.findByUserId(daemon.userId);
325
- const allAgents = allDaemons.flatMap((d) => {
326
- const metadata = d.metadata;
327
- const dAgents = metadata?.agents || [];
328
- return dAgents.map((a) => ({
329
- ...a,
330
- daemonId: d.id,
331
- daemonName: d.name,
332
- machineId: d.machineId,
333
- }));
334
- });
335
- // Get online users from presence registry (for cross-machine user routing)
336
- const allUsers = getOnlineUsersForDiscovery();
337
- res.json({
338
- success: true,
339
- allAgents, // Return all agents across all linked daemons
340
- allUsers, // Return online users for cross-machine routing
341
- });
342
- }
343
- catch (error) {
344
- console.error('Error syncing agents:', error);
345
- res.status(500).json({ error: 'Failed to sync agents' });
346
- }
347
- });
348
- /**
349
- * POST /api/daemons/message
350
- * Send message to an agent on another machine (cross-machine relay)
351
- */
352
- daemonsRouter.post('/message', requireDaemonAuth, async (req, res) => {
353
- const daemon = req.daemon;
354
- const { targetDaemonId, targetAgent, message } = req.body;
355
- if (!targetDaemonId || !targetAgent || !message) {
356
- return res.status(400).json({ error: 'targetDaemonId, targetAgent, and message are required' });
357
- }
358
- try {
359
- // Special case: messages to cloud users (daemonId = 'cloud')
360
- if (targetDaemonId === 'cloud') {
361
- // Verify user is online
362
- if (!isUserOnline(targetAgent)) {
363
- return res.status(404).json({ error: 'User not online' });
364
- }
365
- // Send via cloud message bus for WebSocket delivery
366
- cloudMessageBus.sendToUser(targetAgent, {
367
- from: {
368
- daemonId: daemon.id,
369
- daemonName: daemon.name,
370
- agent: message.from,
371
- },
372
- to: targetAgent,
373
- body: message.content,
374
- timestamp: new Date().toISOString(),
375
- metadata: message.metadata,
376
- });
377
- console.log(`[daemons] Message sent to cloud user ${targetAgent} from ${message.from}`);
378
- return res.json({ success: true, message: 'Message sent to cloud user' });
379
- }
380
- // Verify target daemon belongs to same user
381
- const targetDaemon = await db.linkedDaemons.findById(targetDaemonId);
382
- if (!targetDaemon || targetDaemon.userId !== daemon.userId) {
383
- return res.status(404).json({ error: 'Target daemon not found' });
384
- }
385
- // Queue message for delivery
386
- await db.linkedDaemons.queueMessage(targetDaemonId, {
387
- from: {
388
- daemonId: daemon.id,
389
- daemonName: daemon.name,
390
- agent: message.from,
391
- },
392
- to: targetAgent,
393
- content: message.content,
394
- metadata: message.metadata,
395
- timestamp: new Date().toISOString(),
396
- });
397
- res.json({ success: true, message: 'Message queued for delivery' });
398
- }
399
- catch (error) {
400
- console.error('Error sending cross-machine message:', error);
401
- res.status(500).json({ error: 'Failed to send message' });
402
- }
403
- });
404
- /**
405
- * GET /api/daemons/messages
406
- * Get pending messages for this daemon (cross-machine messages)
407
- */
408
- daemonsRouter.get('/messages', requireDaemonAuth, async (req, res) => {
409
- const daemon = req.daemon;
410
- try {
411
- const messages = await db.linkedDaemons.getQueuedMessages(daemon.id);
412
- // Clear the queue after fetching
413
- if (messages.length > 0) {
414
- await db.linkedDaemons.clearMessageQueue(daemon.id);
415
- }
416
- res.json({ messages });
417
- }
418
- catch (error) {
419
- console.error('Error fetching messages:', error);
420
- res.status(500).json({ error: 'Failed to fetch messages' });
421
- }
422
- });
423
- /**
424
- * POST /api/daemons/messages/sync
425
- * Bulk sync messages from local daemon to cloud storage.
426
- * Messages are stored for backup/history - local SQLite is used for display.
427
- */
428
- daemonsRouter.post('/messages/sync', requireDaemonAuth, async (req, res) => {
429
- const daemon = req.daemon;
430
- const { messages, repoFullName } = req.body;
431
- if (!messages || !Array.isArray(messages)) {
432
- return res.status(400).json({ error: 'messages array is required' });
433
- }
434
- if (messages.length === 0) {
435
- return res.json({ success: true, synced: 0, duplicates: 0 });
436
- }
437
- if (messages.length > 500) {
438
- return res.status(400).json({ error: 'Maximum batch size is 500 messages' });
439
- }
440
- // Resolve workspace from git remote if not already linked
441
- let workspaceId = daemon.workspaceId;
442
- if (!workspaceId && repoFullName) {
443
- // Try to find workspace by repository
444
- const workspace = await db.workspaces.findByRepoFullName(repoFullName);
445
- if (workspace) {
446
- // Auto-link daemon to workspace
447
- await db.linkedDaemons.update(daemon.id, { workspaceId: workspace.id });
448
- workspaceId = workspace.id;
449
- console.log(`[message-sync] Auto-linked daemon ${daemon.id} to workspace ${workspace.id} via repo ${repoFullName}`);
450
- }
451
- }
452
- // Require workspace to be linked
453
- if (!workspaceId) {
454
- const hint = repoFullName
455
- ? `Repository '${repoFullName}' not found in any workspace. Link the repo in the dashboard first.`
456
- : 'Daemon must be linked to a workspace to sync messages. Re-link with a workspace ID.';
457
- return res.status(400).json({ error: hint });
458
- }
459
- try {
460
- const user = await db.users.findById(daemon.userId);
461
- const plan = user?.plan || 'free';
462
- let expiresAt = null;
463
- if (plan === 'free') {
464
- expiresAt = new Date(Date.now() + 30 * 24 * 60 * 60 * 1000);
465
- }
466
- else if (plan === 'pro') {
467
- expiresAt = new Date(Date.now() + 90 * 24 * 60 * 60 * 1000);
468
- }
469
- const dbMessages = messages.map((msg) => ({
470
- workspaceId,
471
- daemonId: daemon.id,
472
- originalId: msg.id,
473
- fromAgent: msg.from,
474
- toAgent: msg.to,
475
- body: msg.body,
476
- kind: msg.kind || 'message',
477
- topic: msg.topic || null,
478
- thread: msg.thread || null,
479
- channel: msg.channel || null,
480
- isBroadcast: msg.is_broadcast || msg.to === '*',
481
- isUrgent: msg.is_urgent || false,
482
- data: msg.data || null,
483
- payloadMeta: msg.payload_meta || null,
484
- messageTs: new Date(msg.ts),
485
- expiresAt,
486
- }));
487
- // Use optimized bulk insert for high-volume message sync
488
- // - Batches < 100: multi-row INSERT
489
- // - Batches 100-1000: chunked multi-row INSERT
490
- // - Batches > 1000: streaming COPY with staging table
491
- const result = await db.bulk.optimizedInsert(db.getRawPool(), dbMessages);
492
- const synced = result.inserted;
493
- const duplicates = result.duplicates;
494
- console.log(`[message-sync] Synced ${synced} messages for daemon ${daemon.id}, ${duplicates} duplicates skipped (${result.durationMs}ms)`);
495
- res.json({ success: true, synced, duplicates });
496
- }
497
- catch (error) {
498
- console.error('Error syncing messages:', error);
499
- res.status(500).json({ error: 'Failed to sync messages' });
500
- }
501
- });
502
- /**
503
- * GET /api/daemons/messages/stats
504
- * Get message sync statistics and database health.
505
- */
506
- daemonsRouter.get('/messages/stats', requireDaemonAuth, async (req, res) => {
507
- const daemon = req.daemon;
508
- if (!daemon.workspaceId) {
509
- return res.status(400).json({ error: 'Daemon must be linked to a workspace' });
510
- }
511
- try {
512
- // Get message count via raw query and pool health in parallel
513
- const pool = db.getRawPool();
514
- const [countResult, poolHealth, poolStats] = await Promise.all([
515
- pool.query('SELECT COUNT(*) FROM agent_messages WHERE workspace_id = $1', [daemon.workspaceId]),
516
- db.bulk.checkHealth(),
517
- Promise.resolve(db.bulk.getPoolStats()),
518
- ]);
519
- const count = parseInt(countResult.rows[0]?.count || '0', 10);
520
- res.json({
521
- workspaceId: daemon.workspaceId,
522
- messageCount: count,
523
- database: {
524
- healthy: poolHealth.healthy,
525
- latencyMs: poolHealth.latencyMs,
526
- pool: poolStats,
527
- },
528
- });
529
- }
530
- catch (error) {
531
- console.error('Error fetching message stats:', error);
532
- res.status(500).json({ error: 'Failed to fetch message stats' });
533
- }
534
- });
535
- //# sourceMappingURL=daemons.js.map
@@ -1,11 +0,0 @@
1
- /**
2
- * Email Auth API Routes
3
- *
4
- * Handles email/password authentication:
5
- * - Signup with email/password
6
- * - Login with email/password
7
- * - Email verification
8
- * - Password reset (future)
9
- */
10
- export declare const emailAuthRouter: import("express-serve-static-core").Router;
11
- //# sourceMappingURL=email-auth.d.ts.map