← back to Designer Wallcoverings
DW-Agents/dw-agents/agent-ui-manager/ui-manager-agent.ts.backup-oauth-20251112
1161 lines
import cookieParser from "cookie-parser";
/**
* DW-Agents: UI Manager Agent
*
* Central UI/UX management system:
* - Unified design system and branding
* - Agent directory with live links
* - Theme customization and distribution
* - Style consistency across all agents
* - Central navigation hub
*
* Port: 7240
*/
import express, { Request, Response } from 'express';
import session from 'express-session';
import * as fs from 'fs';
import * as path from 'path';
import { exec } from 'child_process';
import { promisify } from 'util';
import { requireAuth as ssoAuth, handleLogin, SSO_TOKEN_NAME, SSO_TOKEN_VALUE } from '../shared-auth';
import cookieParser from 'cookie-parser';
const execAsync = promisify(exec);
import Anthropic from '@anthropic-ai/sdk';
import { chatMiddleware } from '../shared-chat-integration';
// Import universal UI components and theme manager
const { getUniversalHeader } = require('../shared-ui-components.js');
const { getTheme, getInlineThemeStyles, getThemeConfig, THEMES } = require('../theme-manager.js');
const app = express();
// Global Authentication
const PORT = 7240;
// Global Authentication - MUST come before any other middleware
app.use(cookieParser());
// Add inter-agent chat widget (after auth)
app.use(chatMiddleware({
agentId: 'agent-ui-manager',
agentName: 'Ui Manager',
port: 7240,
category: 'agent'
}));
// Anthropic Claude
const ANTHROPIC_API_KEY = process.env.ANTHROPIC_API_KEY || '';
const anthropic = new Anthropic({ apiKey: ANTHROPIC_API_KEY });
// Get modern minimalist theme
const MODERN_MINIMALIST_THEME = getTheme('modern-minimalist');
// Design system (now using modern minimalist theme)
const DESIGN_SYSTEM = {
colors: {
primary: MODERN_MINIMALIST_THEME.colors.primary,
secondary: MODERN_MINIMALIST_THEME.colors.secondary,
accent: MODERN_MINIMALIST_THEME.colors.accent,
success: MODERN_MINIMALIST_THEME.colors.success,
warning: MODERN_MINIMALIST_THEME.colors.warning,
error: MODERN_MINIMALIST_THEME.colors.error,
critical: MODERN_MINIMALIST_THEME.colors.error,
dark: MODERN_MINIMALIST_THEME.colors.neutral800,
light: MODERN_MINIMALIST_THEME.colors.neutral100,
},
fonts: {
primary: "-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif",
mono: "'Monaco', 'Courier New', monospace",
},
spacing: {
base: '20px',
small: '10px',
large: '40px',
},
borderRadius: {
small: '5px',
medium: '8px',
large: '10px',
},
shadows: {
small: '0 2px 5px rgba(0,0,0,0.05)',
medium: '0 2px 10px rgba(0,0,0,0.1)',
large: '0 10px 40px rgba(0,0,0,0.3)',
},
// Action link button styles - standard across all agents
actionLinkStyles: {
base: `
display: inline-block;
color: #DC143C;
text-decoration: none;
font-weight: 600;
margin-left: 10px;
padding: 4px 12px;
background: rgba(220, 20, 60, 0.1);
border-radius: 4px;
border: 1px solid #DC143C;
transition: all 0.3s ease;
`,
hover: `
background: #DC143C;
color: white;
transform: translateX(3px);
`,
// Variant for success/completed actions
success: `
display: inline-block;
color: #27ae60;
text-decoration: none;
font-weight: 600;
margin-left: 10px;
padding: 4px 12px;
background: rgba(39, 174, 96, 0.1);
border-radius: 4px;
border: 1px solid #27ae60;
transition: all 0.3s ease;
`,
successHover: `
background: #27ae60;
color: white;
transform: translateX(3px);
`,
},
};
// Agent registry
const AGENTS = [
{ name: 'Task Orchestrator', port: 9900, path: '/', icon: '🎯', category: 'Core', description: 'Master task coordination' },
{ name: 'Shopify Store', port: 7238, path: '/', icon: '🛍️', category: 'E-Commerce', description: 'Store management' },
{ name: 'Log Monitor', port: 7239, path: '/', icon: '📋', category: 'Monitoring', description: 'Log analysis & error detection' },
{ name: 'Purchasing Office', port: 9880, path: '/', icon: '📦', category: 'Operations', description: 'Office supplies ordering' },
{ name: 'Accounting', port: 9882, path: '/', icon: '💰', category: 'Finance', description: 'Financial management' },
{ name: 'Marketing', port: 9881, path: '/', icon: '📢', category: 'Marketing', description: 'Campaign management' },
{ name: 'Zendesk Chat', port: 9884, path: '/', icon: '💬', category: 'Customer', description: 'Customer support' },
{ name: 'Digital Samples', port: 9879, path: '/', icon: '🎨', category: 'Customer', description: 'Sample management' },
{ name: 'Trend Research', port: 9883, path: '/', icon: '📊', category: 'Analytics', description: 'Market analysis' },
{ name: 'Legal Team', port: 9878, path: '/', icon: '⚖️', category: 'Legal', description: 'Legal & compliance' },
{ name: 'Master Hub', port: 9712, path: '/', icon: '🏠', category: 'Core', description: 'Central dashboard' },
{ name: 'UI Manager', port: 7240, path: '/', icon: '🎨', category: 'System', description: 'UI/UX management' },
];
// Middleware
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
declare module 'express-session' {
interface SessionData {
authenticated: boolean;
currentTheme: any;
chatHistory: Array<{ role: 'user' | 'assistant', content: string }>;
}
}
app.use(
session({
secret: 'dw-agents-ui-manager-2025',
resave: false,
saveUninitialized: true,
rolling: true,
cookie: {
secure: false,
httpOnly: true,
maxAge: 7 * 24 * 60 * 60 * 1000,
},
})
);
// Authentication
app.post('/login', handleLogin);
const requireAuth = (req: Request, res: Response, next: any) => {
if (req.cookies[SSO_TOKEN_NAME] === SSO_TOKEN_VALUE || req.session.authenticated) {
next();
} else {
res.redirect('/login');
}
};
app.get('/login', (req: Request, res: Response) => {
res.send(`
<!DOCTYPE html>
<html>
<head>
<title>UI Manager - Login</title>
<style>
body {
font-family: ${DESIGN_SYSTEM.fonts.primary};
background: linear-gradient(135deg, ${DESIGN_SYSTEM.colors.primary} 0%, ${DESIGN_SYSTEM.colors.secondary} 100%);
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
}
.login-box {
background: white;
padding: 40px;
border-radius: ${DESIGN_SYSTEM.borderRadius.large};
box-shadow: ${DESIGN_SYSTEM.shadows.large};
width: 300px;
}
h2 { margin-top: 0; color: ${DESIGN_SYSTEM.colors.dark}; }
input {
width: 100%;
padding: 12px;
margin: 10px 0;
border: 1px solid #ddd;
border-radius: ${DESIGN_SYSTEM.borderRadius.small};
box-sizing: border-box;
}
button {
width: 100%;
padding: 12px;
background: ${DESIGN_SYSTEM.colors.primary};
color: white;
border: none;
border-radius: ${DESIGN_SYSTEM.borderRadius.small};
cursor: pointer;
font-size: 16px;
}
button:hover { background: ${DESIGN_SYSTEM.colors.secondary}; }
</style>
</head>
<body>
<div class="login-box">
<h2>🎨 UI Manager</h2>
<form method="POST" action="/login">
<input type="password" name="password" placeholder="Password" required autofocus />
<button type="submit">Login</button>
</form>
</div>
</body>
</html>
`);
});
// Check agent status
async function checkAgentStatus(port: number): Promise<boolean> {
try {
// Try /health first, then fallback to root / if health doesn't exist
const { stdout } = await execAsync(`curl -s -o /dev/null -w "%{http_code}" --max-time 2 http://localhost:${port}/health 2>/dev/null || curl -s -o /dev/null -w "%{http_code}" --max-time 2 http://localhost:${port}/ 2>/dev/null || echo "000"`);
const statusCode = stdout.trim();
return statusCode === '200' || statusCode.startsWith('2') || statusCode === '404' || statusCode === '302';
} catch (error) {
return false;
}
}
// Main dashboard
app.get('/', requireAuth, async (req: Request, res: Response) => {
// Check status of all agents
const agentStatuses = await Promise.all(
AGENTS.map(async (agent) => ({
...agent,
online: await checkAgentStatus(agent.port),
}))
);
const categories = [...new Set(AGENTS.map(a => a.category))];
res.send(`
<!DOCTYPE html>
<html>
<head>
<title>UI Manager - Agent Directory</title>
${getInlineThemeStyles('modern-minimalist')}
<style>
/* Override theme defaults for this page */
body {
background: #f8fafc !important;
padding: 10px;
}
.header {
background: linear-gradient(135deg, ${DESIGN_SYSTEM.colors.primary} 0%, ${DESIGN_SYSTEM.colors.secondary} 100%);
color: white;
padding: ${DESIGN_SYSTEM.spacing.base};
box-shadow: ${DESIGN_SYSTEM.shadows.medium};
border-radius: 10px;
margin-bottom: 20px;
}
.header h1 { font-size: 28px; margin-bottom: 10px; }
.header p { font-size: 14px; opacity: 0.9; }
.nav {
background: white;
padding: 15px ${DESIGN_SYSTEM.spacing.base};
box-shadow: ${DESIGN_SYSTEM.shadows.small};
display: flex;
gap: 20px;
overflow-x: auto;
}
.nav-item {
padding: 10px 20px;
background: ${DESIGN_SYSTEM.colors.light};
border-radius: ${DESIGN_SYSTEM.borderRadius.small};
cursor: pointer;
white-space: nowrap;
transition: all 0.3s;
}
.nav-item:hover { background: ${DESIGN_SYSTEM.colors.primary}; color: white; }
.nav-item.active { background: ${DESIGN_SYSTEM.colors.primary}; color: white; }
.content {
padding: 15px;
max-width: 1600px;
margin: 0 auto;
}
.stats {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: ${DESIGN_SYSTEM.spacing.base};
margin-bottom: ${DESIGN_SYSTEM.spacing.base};
}
.stat-card {
background: white;
padding: ${DESIGN_SYSTEM.spacing.base};
border-radius: ${DESIGN_SYSTEM.borderRadius.medium};
box-shadow: ${DESIGN_SYSTEM.shadows.medium};
text-align: center;
}
.stat-card h3 { font-size: 14px; color: #666; margin-bottom: 10px; }
.stat-card p { font-size: 32px; font-weight: bold; color: ${DESIGN_SYSTEM.colors.primary}; }
.agent-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 12px;
}
.agent-card {
background: white;
border-radius: ${DESIGN_SYSTEM.borderRadius.medium};
box-shadow: ${DESIGN_SYSTEM.shadows.medium};
overflow: hidden;
transition: transform 0.3s, box-shadow 0.3s;
cursor: pointer;
}
.agent-card:hover {
transform: translateY(-5px);
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
}
.agent-header {
background: linear-gradient(135deg, ${DESIGN_SYSTEM.colors.primary} 0%, ${DESIGN_SYSTEM.colors.secondary} 100%);
color: white;
padding: 12px 15px;
display: flex;
align-items: center;
gap: 10px;
}
.agent-icon {
font-size: 32px;
}
.agent-info h3 { font-size: 18px; margin-bottom: 5px; }
.agent-info p { font-size: 12px; opacity: 0.9; }
.agent-body {
padding: 12px 15px;
}
.agent-body p {
color: #666;
margin-bottom: 15px;
font-size: 14px;
}
.agent-footer {
display: flex;
gap: 8px;
padding: 0 15px 12px 15px;
}
.btn {
flex: 1;
padding: 8px;
border: none;
border-radius: ${DESIGN_SYSTEM.borderRadius.small};
cursor: pointer;
font-size: 12px;
transition: all 0.3s;
text-align: center;
text-decoration: none;
display: inline-block;
font-weight: 600;
}
.btn-primary {
background: ${DESIGN_SYSTEM.colors.primary};
color: white;
}
.btn-primary:hover { background: ${DESIGN_SYSTEM.colors.secondary}; }
.btn-secondary {
background: ${DESIGN_SYSTEM.colors.light};
color: ${DESIGN_SYSTEM.colors.dark};
}
.btn-secondary:hover { background: #ddd; }
.status-badge {
display: inline-block;
padding: 4px 12px;
border-radius: 12px;
font-size: 11px;
font-weight: bold;
margin-left: auto;
}
.status-online {
background: ${DESIGN_SYSTEM.colors.success};
color: white;
}
.status-offline {
background: ${DESIGN_SYSTEM.colors.error};
color: white;
}
.category-section {
margin-bottom: 30px;
}
.category-title {
font-size: 20px;
font-weight: 600;
color: ${DESIGN_SYSTEM.colors.dark};
margin-bottom: 15px;
padding-bottom: 10px;
border-bottom: 2px solid ${DESIGN_SYSTEM.colors.primary};
}
.design-system {
background: white;
padding: ${DESIGN_SYSTEM.spacing.base};
border-radius: ${DESIGN_SYSTEM.borderRadius.medium};
box-shadow: ${DESIGN_SYSTEM.shadows.medium};
margin-bottom: ${DESIGN_SYSTEM.spacing.base};
}
.color-palette {
display: flex;
gap: 10px;
flex-wrap: wrap;
}
.color-swatch {
width: 80px;
height: 80px;
border-radius: ${DESIGN_SYSTEM.borderRadius.small};
display: flex;
align-items: flex-end;
padding: 8px;
color: white;
font-size: 11px;
font-weight: bold;
text-shadow: 0 1px 2px rgba(0,0,0,0.3);
}
.chat-container {
background: white;
padding: ${DESIGN_SYSTEM.spacing.base};
border-radius: ${DESIGN_SYSTEM.borderRadius.medium};
box-shadow: ${DESIGN_SYSTEM.shadows.medium};
height: calc(100vh - 250px);
display: flex;
flex-direction: column;
}
.chat-messages {
flex: 1;
overflow-y: auto;
padding: ${DESIGN_SYSTEM.spacing.base};
background: #fafafa;
border-radius: ${DESIGN_SYSTEM.borderRadius.small};
margin-bottom: ${DESIGN_SYSTEM.spacing.base};
}
.chat-message {
margin-bottom: 15px;
padding: 12px;
border-radius: ${DESIGN_SYSTEM.borderRadius.small};
max-width: 80%;
}
.chat-message.user {
background: ${DESIGN_SYSTEM.colors.primary};
color: white;
margin-left: auto;
text-align: right;
}
.chat-message.assistant {
background: white;
box-shadow: ${DESIGN_SYSTEM.shadows.small};
}
.chat-input-area {
display: flex;
gap: 10px;
}
.chat-input {
flex: 1;
padding: 12px;
border: 2px solid ${DESIGN_SYSTEM.colors.primary};
border-radius: ${DESIGN_SYSTEM.borderRadius.small};
font-size: 14px;
}
.chat-send {
padding: 12px 30px;
background: ${DESIGN_SYSTEM.colors.primary};
color: white;
border: none;
border-radius: ${DESIGN_SYSTEM.borderRadius.small};
cursor: pointer;
font-weight: 600;
}
.chat-send:hover {
background: ${DESIGN_SYSTEM.colors.secondary};
}
.chat-info {
background: ${DESIGN_SYSTEM.colors.light};
padding: 15px;
border-radius: ${DESIGN_SYSTEM.borderRadius.small};
margin-bottom: 15px;
font-size: 14px;
}
</style>
</head>
<body>
${getUniversalHeader('UI Manager', PORT, 5)}
<div class="header" style="margin-top: 20px;">
<h1>🎨 UI Manager - Agent Directory</h1>
<p>Central hub for all DW-Agents with unified design system</p>
</div>
<div class="nav">
<div class="nav-item active" onclick="showView('agents')">Agent Directory</div>
<div class="nav-item" onclick="showView('preview')">🎬 Live Preview</div>
<div class="nav-item" onclick="showView('theme')">🎨 Theme Manager</div>
<div class="nav-item" onclick="showView('design')">Design System</div>
<div class="nav-item" onclick="showView('functions')">Functions</div>
<div class="nav-item" onclick="showView('chat')">Universal Chat</div>
</div>
<div class="content">
<div id="agents-view">
<div class="stats">
<div class="stat-card">
<h3>Total Agents</h3>
<p>${AGENTS.length}</p>
</div>
<div class="stat-card">
<h3>Online</h3>
<p>${agentStatuses.filter(a => a.online).length}</p>
</div>
<div class="stat-card">
<h3>Offline</h3>
<p>${agentStatuses.filter(a => !a.online).length}</p>
</div>
<div class="stat-card">
<h3>Categories</h3>
<p>${categories.length}</p>
</div>
</div>
${categories.map(category => `
<div class="category-section">
<h2 class="category-title">${category}</h2>
<div class="agent-grid">
${agentStatuses.filter(a => a.category === category).map(agent => `
<div class="agent-card">
<div class="agent-header">
<div class="agent-icon">${agent.icon}</div>
<div class="agent-info">
<h3>${agent.name}</h3>
<p>Port ${agent.port}</p>
</div>
<span class="status-badge ${agent.online ? 'status-online' : 'status-offline'}">
${agent.online ? 'Online' : 'Offline'}
</span>
</div>
<div class="agent-body">
<p>${agent.description}</p>
</div>
<div class="agent-footer">
<a href="http://45.61.58.125:${agent.port}${agent.path}" target="_blank" class="btn btn-primary">
Open Dashboard
</a>
<button class="btn btn-secondary" onclick="checkHealth(${agent.port})">
Check Status
</button>
</div>
</div>
`).join('')}
</div>
</div>
`).join('')}
</div>
<div id="theme-view" style="display: none;">
<div class="design-system">
<h2 style="margin-bottom: 20px;">🎨 Theme Manager - Modern Minimalist</h2>
<p style="color: #666; margin-bottom: 30px;">Professional, clean design theme for all DW-Agents. Use the code snippets below to apply the theme to your agents.</p>
<div style="background: #f8fafc; padding: 20px; border-radius: 8px; border: 1px solid #e2e8f0; margin-bottom: 30px;">
<h3 style="margin-top: 0;">Quick Start</h3>
<p style="margin-bottom: 15px; color: #475569;">Add this to your agent's TypeScript/JavaScript file:</p>
<pre style="background: #1e293b; color: #f1f5f9; padding: 15px; border-radius: 6px; overflow-x: auto; font-size: 13px;"><code>// Import theme utilities
const { getThemedPageWrapper } = require('../shared-ui-components.js');
// Use in your route
app.get('/', (req, res) => {
const content = \`
<div class="card">
<h2>Welcome to My Agent</h2>
<p>Content here...</p>
<button class="btn-primary">Action</button>
</div>
\`;
res.send(getThemedPageWrapper('Agent Name', PORT, content));
});</code></pre>
</div>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 20px; margin-bottom: 30px;">
<div style="background: white; padding: 20px; border-radius: 8px; box-shadow: 0 1px 3px rgba(0,0,0,0.1);">
<h3 style="margin-top: 0;">API Endpoints</h3>
<ul style="line-height: 2; font-family: Monaco, monospace; font-size: 13px; color: #475569;">
<li>GET /api/theme</li>
<li>GET /api/theme/css</li>
<li>GET /api/theme/config</li>
</ul>
</div>
<div style="background: white; padding: 20px; border-radius: 8px; box-shadow: 0 1px 3px rgba(0,0,0,0.1);">
<h3 style="margin-top: 0;">Utility Classes</h3>
<ul style="line-height: 2; font-size: 13px; color: #475569;">
<li><code>.btn-primary</code> - Primary button</li>
<li><code>.btn-secondary</code> - Secondary button</li>
<li><code>.card</code> - Content card</li>
<li><code>.badge</code> - Status badge</li>
<li><code>.grid-2/3/4</code> - Grid layouts</li>
</ul>
</div>
</div>
<h3 style="margin: 30px 0 15px 0;">Modern Minimalist Color Palette</h3>
<div class="color-palette">
<div class="color-swatch" style="background: ${MODERN_MINIMALIST_THEME.colors.primary};">Primary<br>#2563eb</div>
<div class="color-swatch" style="background: ${MODERN_MINIMALIST_THEME.colors.secondary};">Secondary<br>#7c3aed</div>
<div class="color-swatch" style="background: ${MODERN_MINIMALIST_THEME.colors.accent};">Accent<br>#06b6d4</div>
<div class="color-swatch" style="background: ${MODERN_MINIMALIST_THEME.colors.success};">Success<br>#10b981</div>
<div class="color-swatch" style="background: ${MODERN_MINIMALIST_THEME.colors.warning};">Warning<br>#f59e0b</div>
<div class="color-swatch" style="background: ${MODERN_MINIMALIST_THEME.colors.error};">Error<br>#ef4444</div>
</div>
<h3 style="margin: 30px 0 15px 0;">Typography Scale</h3>
<div style="background: white; padding: 20px; border-radius: 8px; box-shadow: 0 1px 3px rgba(0,0,0,0.1);">
<p style="font-size: 0.75rem; margin: 10px 0;">XS - 12px / 0.75rem</p>
<p style="font-size: 0.875rem; margin: 10px 0;">SM - 14px / 0.875rem</p>
<p style="font-size: 1rem; margin: 10px 0;">Base - 16px / 1rem</p>
<p style="font-size: 1.125rem; margin: 10px 0;">LG - 18px / 1.125rem</p>
<p style="font-size: 1.25rem; margin: 10px 0;">XL - 20px / 1.25rem</p>
<p style="font-size: 1.5rem; margin: 10px 0;">2XL - 24px / 1.5rem</p>
<p style="font-size: 1.875rem; margin: 10px 0;">3XL - 30px / 1.875rem</p>
</div>
<h3 style="margin: 30px 0 15px 0;">Component Examples</h3>
<div style="background: white; padding: 30px; border-radius: 8px; box-shadow: 0 1px 3px rgba(0,0,0,0.1);">
<div style="display: flex; gap: 10px; margin-bottom: 20px;">
<button class="btn-primary">Primary Button</button>
<button class="btn-secondary">Secondary Button</button>
</div>
<div style="display: flex; gap: 10px; margin-bottom: 20px;">
<span class="badge badge-success">Success</span>
<span class="badge badge-warning">Warning</span>
<span class="badge badge-error">Error</span>
<span class="badge badge-info">Info</span>
</div>
<div class="card" style="max-width: 400px;">
<h4 style="margin-top: 0;">Example Card</h4>
<p style="color: #475569;">This is how cards look in the modern minimalist theme. Clean, professional, with subtle shadows.</p>
<button class="btn-primary" style="margin-top: 10px;">Take Action</button>
</div>
</div>
</div>
</div>
<div id="design-view" style="display: none;">
<div class="design-system">
<h2 style="margin-bottom: 20px;">Design System</h2>
<h3 style="margin: 20px 0 10px 0;">Color Palette</h3>
<div class="color-palette">
<div class="color-swatch" style="background: ${DESIGN_SYSTEM.colors.primary};">Primary</div>
<div class="color-swatch" style="background: ${DESIGN_SYSTEM.colors.secondary};">Secondary</div>
<div class="color-swatch" style="background: ${DESIGN_SYSTEM.colors.accent};">Accent</div>
<div class="color-swatch" style="background: ${DESIGN_SYSTEM.colors.success};">Success</div>
<div class="color-swatch" style="background: ${DESIGN_SYSTEM.colors.warning};">Warning</div>
<div class="color-swatch" style="background: ${DESIGN_SYSTEM.colors.error};">Error</div>
</div>
<h3 style="margin: 30px 0 10px 0;">Typography</h3>
<p><strong>Primary Font:</strong> ${DESIGN_SYSTEM.fonts.primary}</p>
<p><strong>Monospace Font:</strong> ${DESIGN_SYSTEM.fonts.mono}</p>
<h3 style="margin: 30px 0 10px 0;">Spacing</h3>
<p><strong>Base:</strong> ${DESIGN_SYSTEM.spacing.base}</p>
<p><strong>Small:</strong> ${DESIGN_SYSTEM.spacing.small}</p>
<p><strong>Large:</strong> ${DESIGN_SYSTEM.spacing.large}</p>
<h3 style="margin: 30px 0 10px 0;">Border Radius</h3>
<p><strong>Small:</strong> ${DESIGN_SYSTEM.borderRadius.small}</p>
<p><strong>Medium:</strong> ${DESIGN_SYSTEM.borderRadius.medium}</p>
<p><strong>Large:</strong> ${DESIGN_SYSTEM.borderRadius.large}</p>
</div>
</div>
<div id="functions-view" style="display: none;">
<div class="design-system">
<h2 style="margin-bottom: 20px;">UI Manager Functions</h2>
<h3>Available Functions:</h3>
<ul style="line-height: 2; margin: 20px 0;">
<li><strong>Agent Directory</strong> - Central hub linking to all agent dashboards</li>
<li><strong>Status Monitoring</strong> - Real-time health checks for all agents</li>
<li><strong>Design System</strong> - Unified color palette and styling guidelines</li>
<li><strong>Universal Chat</strong> - AI assistant that works across all agents</li>
<li><strong>Style Distribution</strong> - Generate CSS for consistent branding</li>
<li><strong>Theme Customization</strong> - Customize colors and styles</li>
<li><strong>Quick Navigation</strong> - One-click access to any agent</li>
<li><strong>Visual Organization</strong> - Agents grouped by category</li>
<li><strong>Responsive Design</strong> - Mobile-friendly layouts</li>
</ul>
<h3 style="margin-top: 30px;">API Endpoints:</h3>
<ul style="line-height: 2; margin: 20px 0; font-family: ${DESIGN_SYSTEM.fonts.mono}; font-size: 13px;">
<li>GET /api/agents - List all agents</li>
<li>GET /api/status - Check all agent statuses</li>
<li>GET /api/design-system - Get design tokens</li>
<li>POST /api/chat - Universal AI chat</li>
<li>POST /api/theme - Update theme</li>
<li>GET /api/css - Generate unified CSS</li>
</ul>
</div>
</div>
<div id="preview-view" style="display: none;">
<!-- Chat at top -->
<div style="background: white; padding: 20px; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); margin-bottom: 20px;">
<h3 style="margin-bottom: 15px;">💬 UI Feedback Chat - Make Suggestions!</h3>
<div style="background: #fafafa; padding: 15px; border-radius: 5px; height: 150px; overflow-y: auto; margin-bottom: 15px;" id="previewChatMessages">
<div style="padding: 10px; background: white; border-radius: 5px; margin-bottom: 10px; box-shadow: 0 1px 3px rgba(0,0,0,0.1);">
<strong>UI Manager:</strong> Tell me what you think about the agent UIs below! I can help improve colors, layouts, consistency, etc.
</div>
</div>
<div style="display: flex; gap: 10px;">
<input type="text" id="previewChatInput" placeholder="e.g., 'Make all buttons the same color' or 'Add more spacing'" style="flex: 1; padding: 12px; border: 2px solid ${DESIGN_SYSTEM.colors.primary}; border-radius: 5px;" />
<button onclick="sendPreviewMessage()" style="padding: 12px 30px; background: ${DESIGN_SYSTEM.colors.primary}; color: white; border: none; border-radius: 5px; cursor: pointer; font-weight: 600;">Send Feedback</button>
</div>
</div>
<!-- Agent Preview Grid -->
<div class="design-system" style="padding: 20px;">
<h2 style="margin-bottom: 20px;">🎬 Live Agent UI Preview - All Running Agents</h2>
<p style="margin-bottom: 20px; color: #666;">View all agent interfaces side-by-side. Click any preview to open in full screen.</p>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(400px, 1fr)); gap: 20px;">
${agentStatuses.filter(a => a.online).map(agent => `
<div style="background: white; border-radius: 10px; overflow: hidden; box-shadow: 0 2px 10px rgba(0,0,0,0.1);">
<div style="background: linear-gradient(135deg, ${DESIGN_SYSTEM.colors.primary} 0%, ${DESIGN_SYSTEM.colors.secondary} 100%); color: white; padding: 15px; display: flex; justify-content: space-between; align-items: center;">
<div>
<strong style="font-size: 16px;">${agent.icon} ${agent.name}</strong>
<div style="font-size: 12px; opacity: 0.9;">Port ${agent.port}</div>
</div>
<a href="http://45.61.58.125:${agent.port}${agent.path}" target="_blank" style="background: rgba(255,255,255,0.2); color: white; padding: 8px 16px; border-radius: 5px; text-decoration: none; font-size: 12px;">Open Full</a>
</div>
<div style="position: relative; padding-bottom: 75%; background: #f5f5f5;">
<iframe
src="http://45.61.58.125:${agent.port}${agent.path}"
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: none;"
sandbox="allow-same-origin allow-scripts allow-forms"
loading="lazy"
></iframe>
</div>
<div style="padding: 15px; background: #fafafa; border-top: 1px solid #eee;">
<p style="margin: 0; font-size: 13px; color: #666;">${agent.description}</p>
</div>
</div>
`).join('')}
</div>
</div>
</div>
<div id="chat-view" style="display: none;">
<div class="chat-container">
<div class="chat-info">
<strong>🤖 Universal Chat - Your AI Assistant</strong><br>
Ask me anything about your agents, tasks, store data, logs, or system operations.
I can help you manage, monitor, and control all DW-Agents from one place!
</div>
<div class="chat-messages" id="chatMessages">
<div class="chat-message assistant">
👋 Hello! I'm your universal AI assistant for all DW-Agents. I can help you with:
<ul style="margin: 10px 0 0 20px;">
<li>Check agent status and health</li>
<li>Query Shopify store data</li>
<li>Review log issues</li>
<li>Create and assign tasks</li>
<li>Navigate to specific agents</li>
<li>Manage UI and styling</li>
</ul>
What would you like to know?
</div>
</div>
<div class="chat-input-area">
<input type="text" class="chat-input" id="chatInput" placeholder="Ask about agents, tasks, orders, logs, or anything else..." />
<button class="chat-send" onclick="sendMessage()">Send</button>
</div>
</div>
</div>
</div>
<script>
function showView(view) {
document.querySelectorAll('.nav-item').forEach(item => item.classList.remove('active'));
event.target.classList.add('active');
document.getElementById('agents-view').style.display = 'none';
document.getElementById('preview-view').style.display = 'none';
document.getElementById('theme-view').style.display = 'none';
document.getElementById('design-view').style.display = 'none';
document.getElementById('functions-view').style.display = 'none';
document.getElementById('chat-view').style.display = 'none';
document.getElementById(view + '-view').style.display = 'block';
}
async function checkHealth(port) {
try {
const res = await fetch(\`http://45.61.58.125:\${port}/health\`);
const data = await res.json();
alert(\`Agent Status:\\n\\n\` + JSON.stringify(data, null, 2));
} catch (error) {
alert('Agent is offline or unreachable');
}
}
async function sendMessage() {
const input = document.getElementById('chatInput');
const message = input.value.trim();
if (!message) return;
// Add user message
const messagesDiv = document.getElementById('chatMessages');
const userMsg = document.createElement('div');
userMsg.className = 'chat-message user';
userMsg.textContent = message;
messagesDiv.appendChild(userMsg);
input.value = '';
messagesDiv.scrollTop = messagesDiv.scrollHeight;
// Show thinking indicator
const thinkingMsg = document.createElement('div');
thinkingMsg.className = 'chat-message assistant';
thinkingMsg.innerHTML = '💭 Thinking...';
thinkingMsg.id = 'thinking';
messagesDiv.appendChild(thinkingMsg);
messagesDiv.scrollTop = messagesDiv.scrollHeight;
try {
const res = await fetch('/api/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message })
});
const data = await res.json();
// Remove thinking indicator
document.getElementById('thinking').remove();
// Add assistant response
const assistantMsg = document.createElement('div');
assistantMsg.className = 'chat-message assistant';
assistantMsg.textContent = data.response;
messagesDiv.appendChild(assistantMsg);
messagesDiv.scrollTop = messagesDiv.scrollHeight;
} catch (error) {
document.getElementById('thinking').remove();
const errorMsg = document.createElement('div');
errorMsg.className = 'chat-message assistant';
errorMsg.textContent = 'Error: ' + error.message;
messagesDiv.appendChild(errorMsg);
}
}
// Preview chat for UI feedback
async function sendPreviewMessage() {
const input = document.getElementById('previewChatInput');
const message = input.value.trim();
if (!message) return;
const messagesDiv = document.getElementById('previewChatMessages');
// Add user message
const userMsg = document.createElement('div');
userMsg.style.cssText = 'padding: 10px; background: ${DESIGN_SYSTEM.colors.primary}; color: white; border-radius: 5px; margin-bottom: 10px; text-align: right;';
userMsg.innerHTML = '<strong>You:</strong> ' + message;
messagesDiv.appendChild(userMsg);
input.value = '';
messagesDiv.scrollTop = messagesDiv.scrollHeight;
// Show thinking
const thinkingMsg = document.createElement('div');
thinkingMsg.style.cssText = 'padding: 10px; background: white; border-radius: 5px; margin-bottom: 10px; box-shadow: 0 1px 3px rgba(0,0,0,0.1);';
thinkingMsg.innerHTML = '<strong>UI Manager:</strong> 💭 Analyzing UI feedback...';
thinkingMsg.id = 'preview-thinking';
messagesDiv.appendChild(thinkingMsg);
messagesDiv.scrollTop = messagesDiv.scrollHeight;
try {
const res = await fetch('/api/ui-feedback', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message })
});
const data = await res.json();
document.getElementById('preview-thinking').remove();
const assistantMsg = document.createElement('div');
assistantMsg.style.cssText = 'padding: 10px; background: white; border-radius: 5px; margin-bottom: 10px; box-shadow: 0 1px 3px rgba(0,0,0,0.1);';
assistantMsg.innerHTML = '<strong>UI Manager:</strong> ' + data.response;
messagesDiv.appendChild(assistantMsg);
messagesDiv.scrollTop = messagesDiv.scrollHeight;
} catch (error) {
document.getElementById('preview-thinking').remove();
}
}
// Allow Enter key to send
document.addEventListener('DOMContentLoaded', function() {
const input = document.getElementById('chatInput');
if (input) {
input.addEventListener('keypress', function(e) {
if (e.key === 'Enter') sendMessage();
});
}
const previewInput = document.getElementById('previewChatInput');
if (previewInput) {
previewInput.addEventListener('keypress', function(e) {
if (e.key === 'Enter') sendPreviewMessage();
});
}
});
</script>
</body>
</html>
`);
});
// API: Get all agents
app.get('/api/agents', requireAuth, (req: Request, res: Response) => {
res.json({ agents: AGENTS });
});
// API: Check status of all agents
app.get('/api/status', requireAuth, async (req: Request, res: Response) => {
const statuses = await Promise.all(
AGENTS.map(async (agent) => ({
name: agent.name,
port: agent.port,
online: await checkAgentStatus(agent.port),
}))
);
res.json({ statuses });
});
// API: UI Feedback chat
app.post('/api/ui-feedback', requireAuth, async (req: Request, res: Response) => {
try {
const { message } = req.body;
const systemPrompt = `You are the UI Manager AI for DW-Agents. Users are giving you feedback about the UI/UX of various agent dashboards.
Your role:
- Analyze UI feedback and suggestions
- Provide actionable recommendations
- Suggest specific color codes, spacing values, or layout changes
- Be encouraging and helpful
- Reference the design system: Primary ${DESIGN_SYSTEM.colors.primary}, Secondary ${DESIGN_SYSTEM.colors.secondary}
Available agents to improve:
${AGENTS.map(a => `- ${a.name} (Port ${a.port})`).join('\n')}
Respond with specific, actionable suggestions. Use technical terms when appropriate (CSS, color codes, etc.).`;
const response = await anthropic.messages.create({
model: 'claude-3-5-sonnet-20241022',
max_tokens: 512,
system: systemPrompt,
messages: [{ role: 'user', content: message }],
});
const assistantMessage = response.content[0].type === 'text' ? response.content[0].text : '';
res.json({ response: assistantMessage });
} catch (error: any) {
res.status(500).json({ error: error.message });
}
});
// API: Universal chat
app.post('/api/chat', requireAuth, async (req: Request, res: Response) => {
try {
const { message } = req.body;
if (!req.session.chatHistory) {
req.session.chatHistory = [];
}
req.session.chatHistory.push({ role: 'user', content: message });
const systemPrompt = `You are the Universal AI Assistant for DW-Agents system. You have access to all agent information and can help users:
Available Agents:
${AGENTS.map(a => `- ${a.name} (Port ${a.port}): ${a.description}`).join('\n')}
You can help with:
- Checking agent status and health
- Navigating to specific agents
- Explaining agent functions
- Providing UI/UX guidance
- Helping with design system
- Creating tasks (coordinate with task-orchestrator)
- Querying Shopify store data (coordinate with shopify-store agent)
- Reviewing log issues (coordinate with log-monitor agent)
- Managing the overall system
Be helpful, concise, and actionable. Provide direct links to agents when relevant (http://45.61.58.125:[PORT]).`;
const response = await anthropic.messages.create({
model: 'claude-3-5-sonnet-20241022',
max_tokens: 1024,
system: systemPrompt,
messages: req.session.chatHistory.slice(-10),
});
const assistantMessage = response.content[0].type === 'text' ? response.content[0].text : '';
req.session.chatHistory.push({ role: 'assistant', content: assistantMessage });
res.json({ response: assistantMessage });
} catch (error: any) {
res.status(500).json({ error: error.message });
}
});
// API: Get design system
app.get('/api/design-system', requireAuth, (req: Request, res: Response) => {
res.json({ designSystem: DESIGN_SYSTEM });
});
// API: Get theme (modern minimalist)
app.get('/api/theme', requireAuth, (req: Request, res: Response) => {
res.json({
theme: MODERN_MINIMALIST_THEME,
name: 'modern-minimalist',
description: 'Clean, professional design with modern colors and generous spacing'
});
});
// API: Get theme CSS
app.get('/api/theme/css', requireAuth, (req: Request, res: Response) => {
const themeCss = getInlineThemeStyles('modern-minimalist');
res.setHeader('Content-Type', 'text/css');
res.send(themeCss);
});
// API: Get theme config (JSON)
app.get('/api/theme/config', requireAuth, (req: Request, res: Response) => {
res.setHeader('Content-Type', 'application/json');
res.send(getThemeConfig('modern-minimalist'));
});
// API: Generate unified CSS (legacy support)
app.get('/api/css', requireAuth, (req: Request, res: Response) => {
const css = `
/* DW-Agents Unified Design System */
:root {
--color-primary: ${DESIGN_SYSTEM.colors.primary};
--color-secondary: ${DESIGN_SYSTEM.colors.secondary};
--color-accent: ${DESIGN_SYSTEM.colors.accent};
--color-success: ${DESIGN_SYSTEM.colors.success};
--color-warning: ${DESIGN_SYSTEM.colors.warning};
--color-error: ${DESIGN_SYSTEM.colors.error};
--color-dark: ${DESIGN_SYSTEM.colors.dark};
--color-light: ${DESIGN_SYSTEM.colors.light};
--font-primary: ${DESIGN_SYSTEM.fonts.primary};
--font-mono: ${DESIGN_SYSTEM.fonts.mono};
--spacing-base: ${DESIGN_SYSTEM.spacing.base};
--spacing-small: ${DESIGN_SYSTEM.spacing.small};
--spacing-large: ${DESIGN_SYSTEM.spacing.large};
--radius-small: ${DESIGN_SYSTEM.borderRadius.small};
--radius-medium: ${DESIGN_SYSTEM.borderRadius.medium};
--radius-large: ${DESIGN_SYSTEM.borderRadius.large};
--shadow-small: ${DESIGN_SYSTEM.shadows.small};
--shadow-medium: ${DESIGN_SYSTEM.shadows.medium};
--shadow-large: ${DESIGN_SYSTEM.shadows.large};
}
body {
font-family: var(--font-primary);
background: var(--color-light);
}
.btn-primary {
background: var(--color-primary);
color: white;
padding: 10px 20px;
border: none;
border-radius: var(--radius-small);
cursor: pointer;
}
.btn-primary:hover {
background: var(--color-secondary);
}
`;
res.setHeader('Content-Type', 'text/css');
res.send(css);
});
// Health check
app.get('/health', (req: Request, res: Response) => {
res.json({
status: 'healthy',
agent: 'ui-manager',
port: PORT,
managedAgents: AGENTS.length,
timestamp: new Date().toISOString()
});
});
// Start server
// Metrics endpoint for daily reporting
app.get("/api/metrics", (req: Request, res: Response) => {
res.json({
status: "online",
uptime: process.uptime(),
responseTime: 0,
tasksCompleted: 0,
errorsToday: 0,
lastActivity: new Date().toISOString(),
qnaReadiness: 100
});
});
app.listen(PORT, () => {
console.log(`🎨 UI Manager Agent running on port ${PORT}`);
console.log(`📊 Dashboard: http://localhost:${PORT}`);
console.log(`🔗 Managing ${AGENTS.length} agents`);
});
// Graceful shutdown
process.on('SIGTERM', () => {
console.log('SIGTERM received, shutting down gracefully...');
process.exit(0);
});
process.on('SIGINT', () => {
console.log('SIGINT received, shutting down gracefully...');
process.exit(0);
});