agent-relay 2.0.13 → 2.0.14
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/bin/relay-pty-darwin-arm64 +0 -0
- package/bin/relay-pty-darwin-x64 +0 -0
- package/bin/relay-pty-linux-x64 +0 -0
- package/deploy/workspace/codex.config.toml +5 -0
- package/deploy/workspace/entrypoint.sh +10 -2
- package/dist/dashboard/out/404.html +1 -1
- package/dist/dashboard/out/app/onboarding.html +1 -1
- package/dist/dashboard/out/app/onboarding.txt +1 -1
- package/dist/dashboard/out/app.html +1 -1
- package/dist/dashboard/out/app.txt +1 -1
- package/dist/dashboard/out/cloud/link.html +1 -1
- package/dist/dashboard/out/cloud/link.txt +1 -1
- package/dist/dashboard/out/connect-repos.html +1 -1
- package/dist/dashboard/out/connect-repos.txt +1 -1
- package/dist/dashboard/out/history.html +1 -1
- package/dist/dashboard/out/history.txt +1 -1
- package/dist/dashboard/out/index.html +1 -1
- package/dist/dashboard/out/index.txt +1 -1
- package/dist/dashboard/out/login.html +1 -1
- package/dist/dashboard/out/login.txt +1 -1
- package/dist/dashboard/out/metrics.html +1 -1
- package/dist/dashboard/out/metrics.txt +1 -1
- package/dist/dashboard/out/pricing.html +1 -1
- package/dist/dashboard/out/pricing.txt +1 -1
- package/dist/dashboard/out/providers/setup/claude.html +1 -1
- package/dist/dashboard/out/providers/setup/claude.txt +1 -1
- package/dist/dashboard/out/providers/setup/codex.html +1 -1
- package/dist/dashboard/out/providers/setup/codex.txt +1 -1
- package/dist/dashboard/out/providers/setup/cursor.html +1 -1
- package/dist/dashboard/out/providers/setup/cursor.txt +1 -1
- package/dist/dashboard/out/providers.html +1 -1
- package/dist/dashboard/out/providers.txt +1 -1
- package/dist/dashboard/out/signup.html +1 -1
- package/dist/dashboard/out/signup.txt +1 -1
- package/dist/src/cli/index.js +131 -21
- package/package.json +20 -19
- package/packages/api-types/package.json +1 -1
- package/packages/bridge/dist/index.d.ts +1 -1
- package/packages/bridge/dist/index.js +1 -1
- package/packages/bridge/dist/spawner.d.ts +18 -0
- package/packages/bridge/dist/spawner.js +122 -38
- package/packages/bridge/package.json +8 -7
- package/packages/cloud/package.json +6 -6
- package/packages/config/package.json +2 -2
- package/packages/continuity/package.json +1 -1
- package/packages/daemon/dist/connection.js +5 -1
- package/packages/daemon/dist/relay-ledger.d.ts +3 -1
- package/packages/daemon/dist/relay-ledger.js +8 -2
- package/packages/daemon/dist/router.js +13 -0
- package/packages/daemon/dist/server.d.ts +7 -0
- package/packages/daemon/dist/server.js +338 -4
- package/packages/daemon/package.json +12 -12
- package/packages/dashboard/dist/server.js +29 -5
- package/packages/dashboard/package.json +13 -12
- package/packages/dashboard/ui-dist/404.html +1 -1
- package/packages/dashboard/ui-dist/app/onboarding.html +1 -1
- package/packages/dashboard/ui-dist/app/onboarding.txt +1 -1
- package/packages/dashboard/ui-dist/app.html +1 -1
- package/packages/dashboard/ui-dist/app.txt +1 -1
- package/packages/dashboard/ui-dist/cloud/link.html +1 -1
- package/packages/dashboard/ui-dist/cloud/link.txt +1 -1
- package/packages/dashboard/ui-dist/connect-repos.html +1 -1
- package/packages/dashboard/ui-dist/connect-repos.txt +1 -1
- package/packages/dashboard/ui-dist/history.html +1 -1
- package/packages/dashboard/ui-dist/history.txt +1 -1
- package/packages/dashboard/ui-dist/index.html +1 -1
- package/packages/dashboard/ui-dist/index.txt +1 -1
- package/packages/dashboard/ui-dist/login.html +1 -1
- package/packages/dashboard/ui-dist/login.txt +1 -1
- package/packages/dashboard/ui-dist/metrics.html +1 -1
- package/packages/dashboard/ui-dist/metrics.txt +1 -1
- package/packages/dashboard/ui-dist/pricing.html +1 -1
- package/packages/dashboard/ui-dist/pricing.txt +1 -1
- package/packages/dashboard/ui-dist/providers/setup/claude.html +1 -1
- package/packages/dashboard/ui-dist/providers/setup/claude.txt +1 -1
- package/packages/dashboard/ui-dist/providers/setup/codex.html +1 -1
- package/packages/dashboard/ui-dist/providers/setup/codex.txt +1 -1
- package/packages/dashboard/ui-dist/providers/setup/cursor.html +1 -1
- package/packages/dashboard/ui-dist/providers/setup/cursor.txt +1 -1
- package/packages/dashboard/ui-dist/providers.html +1 -1
- package/packages/dashboard/ui-dist/providers.txt +1 -1
- package/packages/dashboard/ui-dist/signup.html +1 -1
- package/packages/dashboard/ui-dist/signup.txt +1 -1
- package/packages/dashboard-server/dist/server.js +29 -5
- package/packages/dashboard-server/package.json +12 -12
- package/packages/hooks/package.json +4 -4
- package/packages/mcp/README.md +24 -3
- package/packages/mcp/dist/bin.js +13 -5
- package/packages/mcp/dist/client.d.ts +54 -1
- package/packages/mcp/dist/client.js +132 -18
- package/packages/mcp/dist/cloud.d.ts +12 -0
- package/packages/mcp/dist/cloud.js +125 -1
- package/packages/mcp/dist/file-transport.d.ts +97 -0
- package/packages/mcp/dist/file-transport.js +197 -0
- package/packages/mcp/dist/hybrid-client.d.ts +28 -0
- package/packages/mcp/dist/hybrid-client.js +159 -0
- package/packages/mcp/dist/index.d.ts +4 -2
- package/packages/mcp/dist/index.js +6 -2
- package/packages/mcp/dist/install.d.ts +23 -1
- package/packages/mcp/dist/install.js +229 -31
- package/packages/mcp/dist/server.js +7 -1
- package/packages/mcp/dist/simple.d.ts +1 -1
- package/packages/mcp/dist/tools/index.d.ts +1 -0
- package/packages/mcp/dist/tools/index.js +1 -0
- package/packages/mcp/dist/tools/relay-continuity.d.ts +35 -0
- package/packages/mcp/dist/tools/relay-continuity.js +101 -0
- package/packages/mcp/dist/tools/relay-health.d.ts +1 -4
- package/packages/mcp/dist/tools/relay-health.js +7 -15
- package/packages/mcp/dist/tools/relay-logs.js +4 -2
- package/packages/mcp/dist/tools/relay-metrics.d.ts +1 -4
- package/packages/mcp/dist/tools/relay-metrics.js +4 -15
- package/packages/mcp/dist/tools/relay-send.d.ts +2 -2
- package/packages/mcp/package.json +3 -2
- package/packages/memory/package.json +2 -2
- package/packages/policy/package.json +2 -2
- package/packages/protocol/dist/relay-pty-schemas.d.ts +14 -0
- package/packages/protocol/dist/types.d.ts +152 -2
- package/packages/protocol/package.json +1 -1
- package/packages/resiliency/package.json +1 -1
- package/packages/sdk/dist/client.js +7 -0
- package/packages/sdk/package.json +2 -2
- package/packages/spawner/package.json +1 -1
- package/packages/state/package.json +1 -1
- package/packages/storage/package.json +2 -2
- package/packages/telemetry/package.json +1 -1
- package/packages/trajectory/package.json +2 -2
- package/packages/user-directory/package.json +2 -2
- package/packages/utils/dist/logger.js +3 -1
- package/packages/utils/package.json +1 -1
- package/packages/wrapper/dist/relay-pty-orchestrator.d.ts +28 -1
- package/packages/wrapper/dist/relay-pty-orchestrator.js +292 -37
- package/packages/wrapper/package.json +6 -6
- package/scripts/demos/README.md +79 -0
- package/scripts/demos/server-capacity.sh +69 -0
- package/scripts/demos/sprint-planning.sh +73 -0
- /package/dist/dashboard/out/_next/static/{Fa21F5wPUusvLUxj67gGm → RgEj_9Y-mWbLaxggzni-X}/_buildManifest.js +0 -0
- /package/dist/dashboard/out/_next/static/{Fa21F5wPUusvLUxj67gGm → RgEj_9Y-mWbLaxggzni-X}/_ssgManifest.js +0 -0
- /package/packages/dashboard/ui-dist/_next/static/{Fa21F5wPUusvLUxj67gGm → RgEj_9Y-mWbLaxggzni-X}/_buildManifest.js +0 -0
- /package/packages/dashboard/ui-dist/_next/static/{Fa21F5wPUusvLUxj67gGm → RgEj_9Y-mWbLaxggzni-X}/_ssgManifest.js +0 -0
- /package/packages/dashboard/ui-dist/_next/static/{coRjSO5QkUdvCiJRCqxfi → UkLmDJOkaPWU2PaNQnkx5}/_buildManifest.js +0 -0
- /package/packages/dashboard/ui-dist/_next/static/{coRjSO5QkUdvCiJRCqxfi → UkLmDJOkaPWU2PaNQnkx5}/_ssgManifest.js +0 -0
- /package/packages/dashboard/ui-dist/_next/static/{yKnlyfJx-NzAy4kPAGkMr → bv9xidgU2pXi7xxPoCAK-}/_buildManifest.js +0 -0
- /package/packages/dashboard/ui-dist/_next/static/{yKnlyfJx-NzAy4kPAGkMr → bv9xidgU2pXi7xxPoCAK-}/_ssgManifest.js +0 -0
|
@@ -20,6 +20,7 @@ import { getMemoryMonitor } from '@agent-relay/resiliency';
|
|
|
20
20
|
import { generateId } from '@agent-relay/wrapper';
|
|
21
21
|
import { createConsensusIntegration, } from './consensus-integration.js';
|
|
22
22
|
import { initTelemetry, track, shutdown as shutdownTelemetry, } from '@agent-relay/telemetry';
|
|
23
|
+
import { RelayWatchdog } from './relay-watchdog.js';
|
|
23
24
|
export const DEFAULT_SOCKET_PATH = '/tmp/agent-relay.sock';
|
|
24
25
|
export const DEFAULT_DAEMON_CONFIG = {
|
|
25
26
|
...DEFAULT_CONFIG,
|
|
@@ -43,6 +44,8 @@ export class Daemon {
|
|
|
43
44
|
consensus;
|
|
44
45
|
cloudSyncDebounceTimer;
|
|
45
46
|
spawnManager;
|
|
47
|
+
shuttingDown = false;
|
|
48
|
+
relayWatchdog;
|
|
46
49
|
/** Telemetry tracking */
|
|
47
50
|
startTime;
|
|
48
51
|
agentSpawnCount = 0;
|
|
@@ -92,7 +95,12 @@ export class Daemon {
|
|
|
92
95
|
// The registry persists on every update; this is a no-op helper for symmetry.
|
|
93
96
|
const agents = this.registry.getAgents();
|
|
94
97
|
try {
|
|
95
|
-
const
|
|
98
|
+
const targetDir = this.config.teamDir ?? path.dirname(this.config.socketPath);
|
|
99
|
+
const targetPath = path.join(targetDir, 'agents.json');
|
|
100
|
+
// Ensure directory exists (defensive - may have been deleted)
|
|
101
|
+
if (!fs.existsSync(targetDir)) {
|
|
102
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
103
|
+
}
|
|
96
104
|
const data = JSON.stringify({ agents }, null, 2);
|
|
97
105
|
// Write atomically: write to temp file first, then rename
|
|
98
106
|
// This prevents race conditions where readers see partial/empty data
|
|
@@ -109,16 +117,27 @@ export class Daemon {
|
|
|
109
117
|
* This file contains agents currently processing/thinking after receiving a message.
|
|
110
118
|
*/
|
|
111
119
|
writeProcessingStateFile() {
|
|
120
|
+
// Skip writes during shutdown to avoid race conditions with directory cleanup
|
|
121
|
+
if (this.shuttingDown)
|
|
122
|
+
return;
|
|
112
123
|
try {
|
|
113
124
|
const processingAgents = this.router.getProcessingAgents();
|
|
114
|
-
const
|
|
125
|
+
const targetDir = this.config.teamDir ?? path.dirname(this.config.socketPath);
|
|
126
|
+
const targetPath = path.join(targetDir, 'processing-state.json');
|
|
127
|
+
// Ensure directory exists (defensive - may have been deleted)
|
|
128
|
+
if (!fs.existsSync(targetDir)) {
|
|
129
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
130
|
+
}
|
|
115
131
|
const data = JSON.stringify({ processingAgents, updatedAt: Date.now() }, null, 2);
|
|
116
132
|
const tempPath = `${targetPath}.tmp`;
|
|
117
133
|
fs.writeFileSync(tempPath, data, 'utf-8');
|
|
118
134
|
fs.renameSync(tempPath, targetPath);
|
|
119
135
|
}
|
|
120
136
|
catch (err) {
|
|
121
|
-
|
|
137
|
+
// Suppress ENOENT errors during shutdown race conditions
|
|
138
|
+
if (!this.shuttingDown) {
|
|
139
|
+
log.error('Failed to write processing-state.json', { error: String(err) });
|
|
140
|
+
}
|
|
122
141
|
}
|
|
123
142
|
}
|
|
124
143
|
/**
|
|
@@ -129,7 +148,12 @@ export class Daemon {
|
|
|
129
148
|
try {
|
|
130
149
|
const connectedAgents = this.router.getAgents();
|
|
131
150
|
const connectedUsers = this.router.getUsers();
|
|
132
|
-
const
|
|
151
|
+
const targetDir = this.config.teamDir ?? path.dirname(this.config.socketPath);
|
|
152
|
+
const targetPath = path.join(targetDir, 'connected-agents.json');
|
|
153
|
+
// Ensure directory exists (defensive - may have been deleted)
|
|
154
|
+
if (!fs.existsSync(targetDir)) {
|
|
155
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
156
|
+
}
|
|
133
157
|
const data = JSON.stringify({
|
|
134
158
|
agents: connectedAgents,
|
|
135
159
|
users: connectedUsers,
|
|
@@ -333,6 +357,12 @@ export class Daemon {
|
|
|
333
357
|
this.processingStateInterval = setInterval(() => {
|
|
334
358
|
this.writeProcessingStateFile();
|
|
335
359
|
}, Daemon.PROCESSING_STATE_INTERVAL_MS);
|
|
360
|
+
// Start RelayWatchdog for MCP file-based messages (ledger-based for durability)
|
|
361
|
+
// Use parent of teamDir since teamDir is .agent-relay/team/ but outbox is at .agent-relay/outbox/
|
|
362
|
+
const relayRoot = path.dirname(teamDir);
|
|
363
|
+
this.initRelayWatchdog(relayRoot).catch(err => {
|
|
364
|
+
log.error('Failed to start RelayWatchdog', { error: String(err) });
|
|
365
|
+
});
|
|
336
366
|
// Track daemon start
|
|
337
367
|
track('daemon_start', {});
|
|
338
368
|
log.info('Listening', { socketPath: this.config.socketPath });
|
|
@@ -340,6 +370,107 @@ export class Daemon {
|
|
|
340
370
|
});
|
|
341
371
|
});
|
|
342
372
|
}
|
|
373
|
+
/**
|
|
374
|
+
* Initialize RelayWatchdog for MCP and file-based agents.
|
|
375
|
+
* Uses the ledger-based watchdog for durable file processing.
|
|
376
|
+
*/
|
|
377
|
+
async initRelayWatchdog(relayDir) {
|
|
378
|
+
// Create project-local relay paths
|
|
379
|
+
const relayPaths = {
|
|
380
|
+
rootDir: relayDir,
|
|
381
|
+
outboxDir: path.join(relayDir, 'outbox'),
|
|
382
|
+
attachmentsDir: path.join(relayDir, 'attachments'),
|
|
383
|
+
metaDir: path.join(relayDir, 'meta'),
|
|
384
|
+
legacyOutboxDir: path.join(relayDir, 'outbox'), // Same as outboxDir for project-local
|
|
385
|
+
};
|
|
386
|
+
this.relayWatchdog = new RelayWatchdog({
|
|
387
|
+
relayPaths,
|
|
388
|
+
ledgerPath: path.join(relayDir, 'meta', 'file-ledger.sqlite'),
|
|
389
|
+
});
|
|
390
|
+
// Handle delivered files from file-based agents (MCP, etc.)
|
|
391
|
+
this.relayWatchdog.on('file:delivered', (file) => {
|
|
392
|
+
const { agentName, messageType, headers, body } = file;
|
|
393
|
+
log.debug('File delivered', { agentName, messageType, headers });
|
|
394
|
+
// Determine message type from headers or filename
|
|
395
|
+
const kind = headers['KIND']?.toLowerCase() || messageType;
|
|
396
|
+
if (kind === 'spawn') {
|
|
397
|
+
// Handle spawn request
|
|
398
|
+
if (this.spawnManager) {
|
|
399
|
+
const envelope = {
|
|
400
|
+
v: PROTOCOL_VERSION,
|
|
401
|
+
type: 'SPAWN',
|
|
402
|
+
id: generateId(),
|
|
403
|
+
ts: Date.now(),
|
|
404
|
+
payload: {
|
|
405
|
+
name: headers['NAME'] || '',
|
|
406
|
+
cli: headers['CLI'] || 'claude',
|
|
407
|
+
task: body,
|
|
408
|
+
cwd: headers['CWD'],
|
|
409
|
+
model: headers['MODEL'],
|
|
410
|
+
spawnerName: agentName,
|
|
411
|
+
},
|
|
412
|
+
};
|
|
413
|
+
const virtualConnection = { agentName, send: () => true };
|
|
414
|
+
this.spawnManager.handleSpawn(virtualConnection, envelope);
|
|
415
|
+
}
|
|
416
|
+
else {
|
|
417
|
+
log.warn('Spawn request ignored - SpawnManager not enabled');
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
else if (kind === 'release') {
|
|
421
|
+
// Handle release request
|
|
422
|
+
if (this.spawnManager) {
|
|
423
|
+
const envelope = {
|
|
424
|
+
v: PROTOCOL_VERSION,
|
|
425
|
+
type: 'RELEASE',
|
|
426
|
+
id: generateId(),
|
|
427
|
+
ts: Date.now(),
|
|
428
|
+
payload: {
|
|
429
|
+
name: headers['NAME'] || '',
|
|
430
|
+
reason: body || undefined,
|
|
431
|
+
},
|
|
432
|
+
};
|
|
433
|
+
const virtualConnection = { agentName, send: () => true };
|
|
434
|
+
this.spawnManager.handleRelease(virtualConnection, envelope);
|
|
435
|
+
}
|
|
436
|
+
else {
|
|
437
|
+
log.warn('Release request ignored - SpawnManager not enabled');
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
else {
|
|
441
|
+
// Default: treat as message
|
|
442
|
+
const to = headers['TO'];
|
|
443
|
+
if (!to) {
|
|
444
|
+
log.warn('Message missing TO header', { agentName, headers });
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
const envelope = {
|
|
448
|
+
v: PROTOCOL_VERSION,
|
|
449
|
+
type: 'SEND',
|
|
450
|
+
id: generateId(),
|
|
451
|
+
ts: Date.now(),
|
|
452
|
+
from: agentName,
|
|
453
|
+
to,
|
|
454
|
+
payload: {
|
|
455
|
+
kind: 'message',
|
|
456
|
+
body,
|
|
457
|
+
thread: headers['THREAD'],
|
|
458
|
+
},
|
|
459
|
+
};
|
|
460
|
+
const virtualConnection = { agentName };
|
|
461
|
+
this.router.route(virtualConnection, envelope);
|
|
462
|
+
}
|
|
463
|
+
});
|
|
464
|
+
// Log errors
|
|
465
|
+
this.relayWatchdog.on('error', (error) => {
|
|
466
|
+
log.error('RelayWatchdog error', { error: error.message });
|
|
467
|
+
});
|
|
468
|
+
this.relayWatchdog.on('file:failed', (record, error) => {
|
|
469
|
+
log.error('File processing failed', { fileId: record.fileId, error: error.message });
|
|
470
|
+
});
|
|
471
|
+
await this.relayWatchdog.start();
|
|
472
|
+
log.info('RelayWatchdog started', { relayDir });
|
|
473
|
+
}
|
|
343
474
|
/**
|
|
344
475
|
* Initialize cloud sync service for cross-machine agent communication.
|
|
345
476
|
*/
|
|
@@ -615,6 +746,8 @@ export class Daemon {
|
|
|
615
746
|
async stop() {
|
|
616
747
|
if (!this.running)
|
|
617
748
|
return;
|
|
749
|
+
// Mark as shutting down to prevent race conditions with state file writes
|
|
750
|
+
this.shuttingDown = true;
|
|
618
751
|
// Track daemon stop
|
|
619
752
|
const uptimeSeconds = this.startTime
|
|
620
753
|
? Math.floor((Date.now() - this.startTime) / 1000)
|
|
@@ -640,6 +773,11 @@ export class Daemon {
|
|
|
640
773
|
clearInterval(this.processingStateInterval);
|
|
641
774
|
this.processingStateInterval = undefined;
|
|
642
775
|
}
|
|
776
|
+
// Stop RelayWatchdog
|
|
777
|
+
if (this.relayWatchdog) {
|
|
778
|
+
await this.relayWatchdog.stop();
|
|
779
|
+
this.relayWatchdog = undefined;
|
|
780
|
+
}
|
|
643
781
|
// Close all active connections
|
|
644
782
|
for (const connection of this.connections) {
|
|
645
783
|
connection.close();
|
|
@@ -933,6 +1071,202 @@ export class Daemon {
|
|
|
933
1071
|
this.spawnManager.handleRelease(connection, releaseEnvelope);
|
|
934
1072
|
break;
|
|
935
1073
|
}
|
|
1074
|
+
// Query handlers (MCP/client requests)
|
|
1075
|
+
case 'STATUS': {
|
|
1076
|
+
const uptimeMs = this.startTime ? Date.now() - this.startTime : 0;
|
|
1077
|
+
const response = {
|
|
1078
|
+
v: PROTOCOL_VERSION,
|
|
1079
|
+
type: 'STATUS_RESPONSE',
|
|
1080
|
+
id: envelope.id,
|
|
1081
|
+
ts: Date.now(),
|
|
1082
|
+
payload: {
|
|
1083
|
+
version: '2.0.13',
|
|
1084
|
+
uptime: uptimeMs,
|
|
1085
|
+
cloudConnected: this.cloudSync?.isConnected() ?? false,
|
|
1086
|
+
agentCount: this.router.connectionCount,
|
|
1087
|
+
},
|
|
1088
|
+
};
|
|
1089
|
+
connection.send(response);
|
|
1090
|
+
break;
|
|
1091
|
+
}
|
|
1092
|
+
case 'INBOX': {
|
|
1093
|
+
const inboxPayload = envelope.payload;
|
|
1094
|
+
const agentName = inboxPayload.agent || connection.agentName;
|
|
1095
|
+
// Get messages from storage
|
|
1096
|
+
const getInboxMessages = async () => {
|
|
1097
|
+
if (!this.storage?.getMessages) {
|
|
1098
|
+
return [];
|
|
1099
|
+
}
|
|
1100
|
+
try {
|
|
1101
|
+
const messages = await this.storage.getMessages({
|
|
1102
|
+
to: agentName,
|
|
1103
|
+
from: inboxPayload.from,
|
|
1104
|
+
limit: inboxPayload.limit || 50,
|
|
1105
|
+
unreadOnly: inboxPayload.unreadOnly,
|
|
1106
|
+
});
|
|
1107
|
+
return messages.map(m => ({
|
|
1108
|
+
id: m.id,
|
|
1109
|
+
from: m.from,
|
|
1110
|
+
body: m.body,
|
|
1111
|
+
channel: m.data?.channel,
|
|
1112
|
+
thread: m.thread,
|
|
1113
|
+
timestamp: m.ts,
|
|
1114
|
+
}));
|
|
1115
|
+
}
|
|
1116
|
+
catch {
|
|
1117
|
+
return [];
|
|
1118
|
+
}
|
|
1119
|
+
};
|
|
1120
|
+
getInboxMessages().then(messages => {
|
|
1121
|
+
const response = {
|
|
1122
|
+
v: PROTOCOL_VERSION,
|
|
1123
|
+
type: 'INBOX_RESPONSE',
|
|
1124
|
+
id: envelope.id,
|
|
1125
|
+
ts: Date.now(),
|
|
1126
|
+
payload: { messages },
|
|
1127
|
+
};
|
|
1128
|
+
connection.send(response);
|
|
1129
|
+
}).catch(err => {
|
|
1130
|
+
this.sendErrorEnvelope(connection, `Failed to get inbox: ${err.message}`);
|
|
1131
|
+
});
|
|
1132
|
+
break;
|
|
1133
|
+
}
|
|
1134
|
+
case 'LIST_AGENTS': {
|
|
1135
|
+
const listPayload = envelope.payload;
|
|
1136
|
+
// Get connected agents from router
|
|
1137
|
+
const connectedAgents = this.router.getAgents();
|
|
1138
|
+
// Get all agents from registry for metadata lookup
|
|
1139
|
+
const registryAgents = this.registry?.getAgents() ?? [];
|
|
1140
|
+
const registryMap = new Map(registryAgents.map(a => [a.name, a]));
|
|
1141
|
+
// Build agent list from connected agents
|
|
1142
|
+
const agents = connectedAgents
|
|
1143
|
+
.filter(name => !this.isInternalAgent(name))
|
|
1144
|
+
.map(name => {
|
|
1145
|
+
const registryAgent = registryMap.get(name);
|
|
1146
|
+
return {
|
|
1147
|
+
name,
|
|
1148
|
+
cli: registryAgent?.cli,
|
|
1149
|
+
idle: false, // Connected agents are not idle
|
|
1150
|
+
parent: registryAgent?.task?.includes('spawned by') ? 'parent' : undefined,
|
|
1151
|
+
};
|
|
1152
|
+
});
|
|
1153
|
+
// Optionally include idle agents from registry
|
|
1154
|
+
if (listPayload.includeIdle && this.registry) {
|
|
1155
|
+
for (const agent of registryAgents) {
|
|
1156
|
+
if (!connectedAgents.includes(agent.name) && !this.isInternalAgent(agent.name)) {
|
|
1157
|
+
agents.push({
|
|
1158
|
+
name: agent.name,
|
|
1159
|
+
cli: agent.cli,
|
|
1160
|
+
idle: true,
|
|
1161
|
+
parent: undefined,
|
|
1162
|
+
});
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
const response = {
|
|
1167
|
+
v: PROTOCOL_VERSION,
|
|
1168
|
+
type: 'LIST_AGENTS_RESPONSE',
|
|
1169
|
+
id: envelope.id,
|
|
1170
|
+
ts: Date.now(),
|
|
1171
|
+
payload: { agents },
|
|
1172
|
+
};
|
|
1173
|
+
connection.send(response);
|
|
1174
|
+
break;
|
|
1175
|
+
}
|
|
1176
|
+
case 'HEALTH': {
|
|
1177
|
+
const healthPayload = envelope.payload;
|
|
1178
|
+
// Compute health based on available data
|
|
1179
|
+
const connectedAgents = this.router.getAgents();
|
|
1180
|
+
const registryAgents = this.registry?.getAgents() ?? [];
|
|
1181
|
+
const agentCount = connectedAgents.filter(n => !this.isInternalAgent(n)).length;
|
|
1182
|
+
// Basic health computation
|
|
1183
|
+
const issues = [];
|
|
1184
|
+
const recommendations = [];
|
|
1185
|
+
let healthScore = 100;
|
|
1186
|
+
// Check for memory issues via memory monitor
|
|
1187
|
+
const memoryMonitor = getMemoryMonitor();
|
|
1188
|
+
const memoryMetrics = memoryMonitor.getAll();
|
|
1189
|
+
const criticalAgents = memoryMetrics.filter(m => m.alertLevel === 'critical');
|
|
1190
|
+
const warningAgents = memoryMetrics.filter(m => m.alertLevel === 'warning');
|
|
1191
|
+
if (criticalAgents.length > 0) {
|
|
1192
|
+
healthScore -= 30;
|
|
1193
|
+
for (const agent of criticalAgents) {
|
|
1194
|
+
issues.push({ severity: 'critical', message: `${agent.name} has critical memory usage` });
|
|
1195
|
+
}
|
|
1196
|
+
recommendations.push('Consider releasing some agents to free memory');
|
|
1197
|
+
}
|
|
1198
|
+
if (warningAgents.length > 0) {
|
|
1199
|
+
healthScore -= 10;
|
|
1200
|
+
for (const agent of warningAgents) {
|
|
1201
|
+
issues.push({ severity: 'warning', message: `${agent.name} has high memory usage` });
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
// Check cloud sync status
|
|
1205
|
+
if (!this.cloudSync?.isConnected()) {
|
|
1206
|
+
issues.push({ severity: 'info', message: 'Cloud sync not connected' });
|
|
1207
|
+
}
|
|
1208
|
+
const summary = healthScore >= 80 ? 'System is healthy' :
|
|
1209
|
+
healthScore >= 50 ? 'System has some issues' :
|
|
1210
|
+
'System needs attention';
|
|
1211
|
+
const healthResponse = {
|
|
1212
|
+
v: PROTOCOL_VERSION,
|
|
1213
|
+
type: 'HEALTH_RESPONSE',
|
|
1214
|
+
id: envelope.id,
|
|
1215
|
+
ts: Date.now(),
|
|
1216
|
+
payload: {
|
|
1217
|
+
healthScore: Math.max(0, healthScore),
|
|
1218
|
+
summary,
|
|
1219
|
+
issues,
|
|
1220
|
+
recommendations,
|
|
1221
|
+
crashes: [], // Would need crash tracking implementation
|
|
1222
|
+
alerts: [], // Would need alert tracking implementation
|
|
1223
|
+
stats: {
|
|
1224
|
+
totalCrashes24h: 0,
|
|
1225
|
+
totalAlerts24h: 0,
|
|
1226
|
+
agentCount,
|
|
1227
|
+
},
|
|
1228
|
+
},
|
|
1229
|
+
};
|
|
1230
|
+
connection.send(healthResponse);
|
|
1231
|
+
break;
|
|
1232
|
+
}
|
|
1233
|
+
case 'METRICS': {
|
|
1234
|
+
const metricsPayload = envelope.payload;
|
|
1235
|
+
// Get metrics from memory monitor
|
|
1236
|
+
const memoryMonitor = getMemoryMonitor();
|
|
1237
|
+
let metrics = memoryMonitor.getAll();
|
|
1238
|
+
// Filter to specific agent if requested
|
|
1239
|
+
if (metricsPayload.agent) {
|
|
1240
|
+
metrics = metrics.filter(m => m.name === metricsPayload.agent);
|
|
1241
|
+
}
|
|
1242
|
+
// Convert to response format
|
|
1243
|
+
const agents = metrics.map(m => ({
|
|
1244
|
+
name: m.name,
|
|
1245
|
+
pid: m.pid,
|
|
1246
|
+
status: m.alertLevel === 'normal' ? 'running' : m.alertLevel,
|
|
1247
|
+
rssBytes: m.current.rssBytes,
|
|
1248
|
+
cpuPercent: m.current.cpuPercent,
|
|
1249
|
+
trend: m.trend,
|
|
1250
|
+
alertLevel: m.alertLevel,
|
|
1251
|
+
highWatermark: m.highWatermark,
|
|
1252
|
+
uptimeMs: m.uptimeMs,
|
|
1253
|
+
}));
|
|
1254
|
+
// System metrics
|
|
1255
|
+
const system = {
|
|
1256
|
+
totalMemory: os.totalmem(),
|
|
1257
|
+
freeMemory: os.freemem(),
|
|
1258
|
+
heapUsed: process.memoryUsage().heapUsed,
|
|
1259
|
+
};
|
|
1260
|
+
const metricsResponse = {
|
|
1261
|
+
v: PROTOCOL_VERSION,
|
|
1262
|
+
type: 'METRICS_RESPONSE',
|
|
1263
|
+
id: envelope.id,
|
|
1264
|
+
ts: Date.now(),
|
|
1265
|
+
payload: { agents, system },
|
|
1266
|
+
};
|
|
1267
|
+
connection.send(metricsResponse);
|
|
1268
|
+
break;
|
|
1269
|
+
}
|
|
936
1270
|
}
|
|
937
1271
|
}
|
|
938
1272
|
handleAck(connection, envelope) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-relay/daemon",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.14",
|
|
4
4
|
"description": "Relay daemon server - agent coordination and message routing",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -22,17 +22,17 @@
|
|
|
22
22
|
"test:watch": "vitest"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@agent-relay/protocol": "2.0.
|
|
26
|
-
"@agent-relay/config": "2.0.
|
|
27
|
-
"@agent-relay/storage": "2.0.
|
|
28
|
-
"@agent-relay/bridge": "2.0.
|
|
29
|
-
"@agent-relay/utils": "2.0.
|
|
30
|
-
"@agent-relay/policy": "2.0.
|
|
31
|
-
"@agent-relay/memory": "2.0.
|
|
32
|
-
"@agent-relay/resiliency": "2.0.
|
|
33
|
-
"@agent-relay/user-directory": "2.0.
|
|
34
|
-
"@agent-relay/wrapper": "2.0.
|
|
35
|
-
"@agent-relay/telemetry": "2.0.
|
|
25
|
+
"@agent-relay/protocol": "2.0.14",
|
|
26
|
+
"@agent-relay/config": "2.0.14",
|
|
27
|
+
"@agent-relay/storage": "2.0.14",
|
|
28
|
+
"@agent-relay/bridge": "2.0.14",
|
|
29
|
+
"@agent-relay/utils": "2.0.14",
|
|
30
|
+
"@agent-relay/policy": "2.0.14",
|
|
31
|
+
"@agent-relay/memory": "2.0.14",
|
|
32
|
+
"@agent-relay/resiliency": "2.0.14",
|
|
33
|
+
"@agent-relay/user-directory": "2.0.14",
|
|
34
|
+
"@agent-relay/wrapper": "2.0.14",
|
|
35
|
+
"@agent-relay/telemetry": "2.0.14",
|
|
36
36
|
"ws": "^8.18.3",
|
|
37
37
|
"better-sqlite3": "^12.6.2",
|
|
38
38
|
"pg": "^8.16.3",
|
|
@@ -1910,9 +1910,9 @@ export async function startDashboard(portOrOptions, dataDirArg, teamDirArg, dbPa
|
|
|
1910
1910
|
return false;
|
|
1911
1911
|
}
|
|
1912
1912
|
};
|
|
1913
|
-
// Helper to subscribe to an agent
|
|
1914
|
-
const subscribeToAgent = (agentName) => {
|
|
1915
|
-
|
|
1913
|
+
// Helper to subscribe to an agent (async to handle spawn timing)
|
|
1914
|
+
const subscribeToAgent = async (agentName) => {
|
|
1915
|
+
let isSpawned = spawner?.hasWorker(agentName) ?? false;
|
|
1916
1916
|
const isDaemon = isDaemonConnected(agentName);
|
|
1917
1917
|
// Check if agent exists (either spawned or daemon-connected)
|
|
1918
1918
|
if (!isSpawned && !isDaemon) {
|
|
@@ -1925,6 +1925,26 @@ export async function startDashboard(portOrOptions, dataDirArg, teamDirArg, dbPa
|
|
|
1925
1925
|
ws.close(4404, 'Agent not found');
|
|
1926
1926
|
return false;
|
|
1927
1927
|
}
|
|
1928
|
+
// If agent is daemon-connected but not yet in spawner's activeWorkers,
|
|
1929
|
+
// poll briefly to handle race condition between spawn API returning and
|
|
1930
|
+
// WebSocket connection. This is common for setup agents (__setup__*).
|
|
1931
|
+
if (!isSpawned && isDaemon && spawner) {
|
|
1932
|
+
const maxWaitMs = 3000; // Wait up to 3 seconds
|
|
1933
|
+
const pollIntervalMs = 100;
|
|
1934
|
+
const startTime = Date.now();
|
|
1935
|
+
while (Date.now() - startTime < maxWaitMs) {
|
|
1936
|
+
await new Promise(resolve => setTimeout(resolve, pollIntervalMs));
|
|
1937
|
+
isSpawned = spawner.hasWorker(agentName);
|
|
1938
|
+
if (isSpawned) {
|
|
1939
|
+
console.log(`[dashboard] Agent ${agentName} appeared in spawner after ${Date.now() - startTime}ms`);
|
|
1940
|
+
break;
|
|
1941
|
+
}
|
|
1942
|
+
// Check if WebSocket was closed during wait
|
|
1943
|
+
if (ws.readyState !== WebSocket.OPEN) {
|
|
1944
|
+
return false;
|
|
1945
|
+
}
|
|
1946
|
+
}
|
|
1947
|
+
}
|
|
1928
1948
|
// Add to subscriptions
|
|
1929
1949
|
clientSubscriptions.add(agentName);
|
|
1930
1950
|
if (!logSubscriptions.has(agentName)) {
|
|
@@ -1960,14 +1980,18 @@ export async function startDashboard(portOrOptions, dataDirArg, teamDirArg, dbPa
|
|
|
1960
1980
|
const pathMatch = pathname.match(/^\/ws\/logs\/(.+)$/);
|
|
1961
1981
|
if (pathMatch) {
|
|
1962
1982
|
const agentName = decodeURIComponent(pathMatch[1]);
|
|
1963
|
-
subscribeToAgent(agentName)
|
|
1983
|
+
subscribeToAgent(agentName).catch((err) => {
|
|
1984
|
+
console.error(`[dashboard] Error subscribing to ${agentName}:`, err);
|
|
1985
|
+
});
|
|
1964
1986
|
}
|
|
1965
1987
|
ws.on('message', (data) => {
|
|
1966
1988
|
try {
|
|
1967
1989
|
const msg = JSON.parse(data.toString());
|
|
1968
1990
|
// Subscribe to agent logs
|
|
1969
1991
|
if (msg.subscribe && typeof msg.subscribe === 'string') {
|
|
1970
|
-
subscribeToAgent(msg.subscribe)
|
|
1992
|
+
subscribeToAgent(msg.subscribe).catch((err) => {
|
|
1993
|
+
console.error(`[dashboard] Error subscribing to ${msg.subscribe}:`, err);
|
|
1994
|
+
});
|
|
1971
1995
|
}
|
|
1972
1996
|
// Unsubscribe from agent logs
|
|
1973
1997
|
if (msg.unsubscribe && typeof msg.unsubscribe === 'string') {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-relay/dashboard",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.14",
|
|
4
4
|
"description": "Web dashboard for Agent Relay - optional package for visual agent coordination",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -25,17 +25,18 @@
|
|
|
25
25
|
"test:watch": "vitest"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@agent-relay/protocol": "2.0.
|
|
29
|
-
"@agent-relay/config": "2.0.
|
|
30
|
-
"@agent-relay/storage": "2.0.
|
|
31
|
-
"@agent-relay/bridge": "2.0.
|
|
32
|
-
"@agent-relay/utils": "2.0.
|
|
33
|
-
"@agent-relay/resiliency": "2.0.
|
|
34
|
-
"@agent-relay/trajectory": "2.0.
|
|
35
|
-
"@agent-relay/cloud": "2.0.
|
|
36
|
-
"@agent-relay/daemon": "2.0.
|
|
37
|
-
"@agent-relay/user-directory": "2.0.
|
|
38
|
-
"@agent-relay/wrapper": "2.0.
|
|
28
|
+
"@agent-relay/protocol": "2.0.14",
|
|
29
|
+
"@agent-relay/config": "2.0.14",
|
|
30
|
+
"@agent-relay/storage": "2.0.14",
|
|
31
|
+
"@agent-relay/bridge": "2.0.14",
|
|
32
|
+
"@agent-relay/utils": "2.0.14",
|
|
33
|
+
"@agent-relay/resiliency": "2.0.14",
|
|
34
|
+
"@agent-relay/trajectory": "2.0.14",
|
|
35
|
+
"@agent-relay/cloud": "2.0.14",
|
|
36
|
+
"@agent-relay/daemon": "2.0.14",
|
|
37
|
+
"@agent-relay/user-directory": "2.0.14",
|
|
38
|
+
"@agent-relay/wrapper": "2.0.14",
|
|
39
|
+
"@agent-relay/sdk": "2.0.14",
|
|
39
40
|
"express": "^5.2.1",
|
|
40
41
|
"ws": "^8.18.3"
|
|
41
42
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="/_next/static/css/605dd4e30c91986f.css" data-precedence="next"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/webpack-1cdd8ed57114d5e1.js"/><script src="/_next/static/chunks/fd9d1056-609918ca7b6280bb.js" async=""></script><script src="/_next/static/chunks/117-c8afed19e821a35d.js" async=""></script><script src="/_next/static/chunks/main-app-fdbeb09028f57c9f.js" async=""></script><meta name="robots" content="noindex"/><title>404: This page could not be found.</title><title>Agent Relay Dashboard</title><meta name="description" content="Fleet control dashboard for Agent Relay"/><script src="/_next/static/chunks/polyfills-42372ed130431b0a.js" noModule=""></script></head><body><div style="font-family:system-ui,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji";height:100vh;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center"><div><style>body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}</style><h1 class="next-error-h1" style="display:inline-block;margin:0 20px 0 0;padding:0 23px 0 0;font-size:24px;font-weight:500;vertical-align:top;line-height:49px">404</h1><div style="display:inline-block"><h2 style="font-size:14px;font-weight:400;line-height:49px;margin:0">This page could not be found.</h2></div></div></div><script src="/_next/static/chunks/webpack-1cdd8ed57114d5e1.js" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0]);self.__next_f.push([2,null])</script><script>self.__next_f.push([1,"1:HL[\"/_next/static/css/605dd4e30c91986f.css\",\"style\"]\n"])</script><script>self.__next_f.push([1,"2:I[2846,[],\"\"]\n4:I[4707,[],\"\"]\n5:I[6423,[],\"\"]\nb:I[1060,[],\"\"]\n6:{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"}\n7:{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"padding\":\"0 23px 0 0\",\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\",\"lineHeight\":\"49px\"}\n8:{\"display\":\"inline-block\"}\n9:{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"49px\",\"margin\":0}\nc:[]\n"])</script><script>self.__next_f.push([1,"0:[\"$\",\"$L2\",null,{\"buildId\":\"
|
|
1
|
+
<!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="/_next/static/css/605dd4e30c91986f.css" data-precedence="next"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/webpack-1cdd8ed57114d5e1.js"/><script src="/_next/static/chunks/fd9d1056-609918ca7b6280bb.js" async=""></script><script src="/_next/static/chunks/117-c8afed19e821a35d.js" async=""></script><script src="/_next/static/chunks/main-app-fdbeb09028f57c9f.js" async=""></script><meta name="robots" content="noindex"/><title>404: This page could not be found.</title><title>Agent Relay Dashboard</title><meta name="description" content="Fleet control dashboard for Agent Relay"/><script src="/_next/static/chunks/polyfills-42372ed130431b0a.js" noModule=""></script></head><body><div style="font-family:system-ui,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji";height:100vh;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center"><div><style>body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}</style><h1 class="next-error-h1" style="display:inline-block;margin:0 20px 0 0;padding:0 23px 0 0;font-size:24px;font-weight:500;vertical-align:top;line-height:49px">404</h1><div style="display:inline-block"><h2 style="font-size:14px;font-weight:400;line-height:49px;margin:0">This page could not be found.</h2></div></div></div><script src="/_next/static/chunks/webpack-1cdd8ed57114d5e1.js" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0]);self.__next_f.push([2,null])</script><script>self.__next_f.push([1,"1:HL[\"/_next/static/css/605dd4e30c91986f.css\",\"style\"]\n"])</script><script>self.__next_f.push([1,"2:I[2846,[],\"\"]\n4:I[4707,[],\"\"]\n5:I[6423,[],\"\"]\nb:I[1060,[],\"\"]\n6:{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"}\n7:{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"padding\":\"0 23px 0 0\",\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\",\"lineHeight\":\"49px\"}\n8:{\"display\":\"inline-block\"}\n9:{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"49px\",\"margin\":0}\nc:[]\n"])</script><script>self.__next_f.push([1,"0:[\"$\",\"$L2\",null,{\"buildId\":\"RgEj_9Y-mWbLaxggzni-X\",\"assetPrefix\":\"\",\"urlParts\":[\"\",\"_not-found\"],\"initialTree\":[\"\",{\"children\":[\"/_not-found\",{\"children\":[\"__PAGE__\",{}]}]},\"$undefined\",\"$undefined\",true],\"initialSeedData\":[\"\",{\"children\":[\"/_not-found\",{\"children\":[\"__PAGE__\",{},[[\"$L3\",[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"padding\":\"0 23px 0 0\",\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\",\"lineHeight\":\"49px\"},\"children\":\"404\"}],[\"$\",\"div\",null,{\"style\":{\"display\":\"inline-block\"},\"children\":[\"$\",\"h2\",null,{\"style\":{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"49px\",\"margin\":0},\"children\":\"This page could not be found.\"}]}]]}]}]],null],null],null]},[null,[\"$\",\"$L4\",null,{\"parallelRouterKey\":\"children\",\"segmentPath\":[\"children\",\"/_not-found\",\"children\"],\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L5\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"notFoundStyles\":\"$undefined\"}]],null]},[[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/605dd4e30c91986f.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\"}]],[\"$\",\"html\",null,{\"lang\":\"en\",\"children\":[\"$\",\"body\",null,{\"children\":[\"$\",\"$L4\",null,{\"parallelRouterKey\":\"children\",\"segmentPath\":[\"children\"],\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L5\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":\"$6\",\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":\"$7\",\"children\":\"404\"}],[\"$\",\"div\",null,{\"style\":\"$8\",\"children\":[\"$\",\"h2\",null,{\"style\":\"$9\",\"children\":\"This page could not be found.\"}]}]]}]}]],\"notFoundStyles\":[]}]}]}]],null],null],\"couldBeIntercepted\":false,\"initialHead\":[[\"$\",\"meta\",null,{\"name\":\"robots\",\"content\":\"noindex\"}],\"$La\"],\"globalErrorComponent\":\"$b\",\"missingSlots\":\"$Wc\"}]\n"])</script><script>self.__next_f.push([1,"a:[[\"$\",\"meta\",\"0\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}],[\"$\",\"meta\",\"1\",{\"charSet\":\"utf-8\"}],[\"$\",\"title\",\"2\",{\"children\":\"Agent Relay Dashboard\"}],[\"$\",\"meta\",\"3\",{\"name\":\"description\",\"content\":\"Fleet control dashboard for Agent Relay\"}]]\n3:null\n"])</script></body></html>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="/_next/static/css/605dd4e30c91986f.css" data-precedence="next"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/webpack-1cdd8ed57114d5e1.js"/><script src="/_next/static/chunks/fd9d1056-609918ca7b6280bb.js" async=""></script><script src="/_next/static/chunks/117-c8afed19e821a35d.js" async=""></script><script src="/_next/static/chunks/main-app-fdbeb09028f57c9f.js" async=""></script><script src="/_next/static/chunks/app/app/onboarding/page-9914652442f7e4fb.js" async=""></script><title>Agent Relay Dashboard</title><meta name="description" content="Fleet control dashboard for Agent Relay"/><link rel="apple-touch-icon" href="/apple-icon.png?9e7a840704165ca6" type="image/png" sizes="256x256"/><script src="/_next/static/chunks/polyfills-42372ed130431b0a.js" noModule=""></script></head><body><!--$!--><template data-dgst="BAILOUT_TO_CLIENT_SIDE_RENDERING"></template><div class="min-h-screen bg-gradient-to-br from-[#0a0a0f] via-[#0d1117] to-[#0a0a0f] flex items-center justify-center"><div class="text-center"><svg class="w-8 h-8 text-accent-cyan animate-spin mx-auto" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"></path></svg><p class="mt-4 text-text-muted">Loading...</p></div></div><!--/$--><script src="/_next/static/chunks/webpack-1cdd8ed57114d5e1.js" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0]);self.__next_f.push([2,null])</script><script>self.__next_f.push([1,"1:HL[\"/_next/static/css/605dd4e30c91986f.css\",\"style\"]\n"])</script><script>self.__next_f.push([1,"2:I[2846,[],\"\"]\n4:I[9107,[],\"ClientPageRoot\"]\n5:I[4011,[\"615\",\"static/chunks/app/app/onboarding/page-9914652442f7e4fb.js\"],\"default\",1]\n6:I[4707,[],\"\"]\n7:I[6423,[],\"\"]\n9:I[1060,[],\"\"]\na:[]\n"])</script><script>self.__next_f.push([1,"0:[\"$\",\"$L2\",null,{\"buildId\":\"
|
|
1
|
+
<!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="/_next/static/css/605dd4e30c91986f.css" data-precedence="next"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/webpack-1cdd8ed57114d5e1.js"/><script src="/_next/static/chunks/fd9d1056-609918ca7b6280bb.js" async=""></script><script src="/_next/static/chunks/117-c8afed19e821a35d.js" async=""></script><script src="/_next/static/chunks/main-app-fdbeb09028f57c9f.js" async=""></script><script src="/_next/static/chunks/app/app/onboarding/page-9914652442f7e4fb.js" async=""></script><title>Agent Relay Dashboard</title><meta name="description" content="Fleet control dashboard for Agent Relay"/><link rel="apple-touch-icon" href="/apple-icon.png?9e7a840704165ca6" type="image/png" sizes="256x256"/><script src="/_next/static/chunks/polyfills-42372ed130431b0a.js" noModule=""></script></head><body><!--$!--><template data-dgst="BAILOUT_TO_CLIENT_SIDE_RENDERING"></template><div class="min-h-screen bg-gradient-to-br from-[#0a0a0f] via-[#0d1117] to-[#0a0a0f] flex items-center justify-center"><div class="text-center"><svg class="w-8 h-8 text-accent-cyan animate-spin mx-auto" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"></path></svg><p class="mt-4 text-text-muted">Loading...</p></div></div><!--/$--><script src="/_next/static/chunks/webpack-1cdd8ed57114d5e1.js" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0]);self.__next_f.push([2,null])</script><script>self.__next_f.push([1,"1:HL[\"/_next/static/css/605dd4e30c91986f.css\",\"style\"]\n"])</script><script>self.__next_f.push([1,"2:I[2846,[],\"\"]\n4:I[9107,[],\"ClientPageRoot\"]\n5:I[4011,[\"615\",\"static/chunks/app/app/onboarding/page-9914652442f7e4fb.js\"],\"default\",1]\n6:I[4707,[],\"\"]\n7:I[6423,[],\"\"]\n9:I[1060,[],\"\"]\na:[]\n"])</script><script>self.__next_f.push([1,"0:[\"$\",\"$L2\",null,{\"buildId\":\"RgEj_9Y-mWbLaxggzni-X\",\"assetPrefix\":\"\",\"urlParts\":[\"\",\"app\",\"onboarding\"],\"initialTree\":[\"\",{\"children\":[\"app\",{\"children\":[\"onboarding\",{\"children\":[\"__PAGE__\",{}]}]}]},\"$undefined\",\"$undefined\",true],\"initialSeedData\":[\"\",{\"children\":[\"app\",{\"children\":[\"onboarding\",{\"children\":[\"__PAGE__\",{},[[\"$L3\",[\"$\",\"$L4\",null,{\"props\":{\"params\":{},\"searchParams\":{}},\"Component\":\"$5\"}],null],null],null]},[null,[\"$\",\"$L6\",null,{\"parallelRouterKey\":\"children\",\"segmentPath\":[\"children\",\"app\",\"children\",\"onboarding\",\"children\"],\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L7\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"notFoundStyles\":\"$undefined\"}]],null]},[null,[\"$\",\"$L6\",null,{\"parallelRouterKey\":\"children\",\"segmentPath\":[\"children\",\"app\",\"children\"],\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L7\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"notFoundStyles\":\"$undefined\"}]],null]},[[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/605dd4e30c91986f.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\"}]],[\"$\",\"html\",null,{\"lang\":\"en\",\"children\":[\"$\",\"body\",null,{\"children\":[\"$\",\"$L6\",null,{\"parallelRouterKey\":\"children\",\"segmentPath\":[\"children\"],\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L7\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"padding\":\"0 23px 0 0\",\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\",\"lineHeight\":\"49px\"},\"children\":\"404\"}],[\"$\",\"div\",null,{\"style\":{\"display\":\"inline-block\"},\"children\":[\"$\",\"h2\",null,{\"style\":{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"49px\",\"margin\":0},\"children\":\"This page could not be found.\"}]}]]}]}]],\"notFoundStyles\":[]}]}]}]],null],null],\"couldBeIntercepted\":false,\"initialHead\":[null,\"$L8\"],\"globalErrorComponent\":\"$9\",\"missingSlots\":\"$Wa\"}]\n"])</script><script>self.__next_f.push([1,"8:[[\"$\",\"meta\",\"0\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}],[\"$\",\"meta\",\"1\",{\"charSet\":\"utf-8\"}],[\"$\",\"title\",\"2\",{\"children\":\"Agent Relay Dashboard\"}],[\"$\",\"meta\",\"3\",{\"name\":\"description\",\"content\":\"Fleet control dashboard for Agent Relay\"}],[\"$\",\"link\",\"4\",{\"rel\":\"apple-touch-icon\",\"href\":\"/apple-icon.png?9e7a840704165ca6\",\"type\":\"image/png\",\"sizes\":\"256x256\"}]]\n3:null\n"])</script></body></html>
|