← back to Designer Wallcoverings
DW-Agents/dw-agents/agent-accounting/accounting-agent.ts
1125 lines
import cookieParser from "cookie-parser";
/**
* DW-Agents: Accounting Agent
*
* Financial management and reporting system
* - P&L generation
* - Expense tracking and categorization
* - Invoice management
* - Budget forecasting
* - Chat interface for financial queries
*/
import Anthropic from '@anthropic-ai/sdk';
import express, { Request, Response } from 'express';
import { requireGlobalAuth } from '../shared-global-auth';
import session from 'express-session';
import { exec } from 'child_process';
import { fileURLToPath } from 'url';
import path from 'path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
import { chatMiddleware } from '../shared-chat-integration';
const app = express();
// Global Authentication
const PORT = process.env.PORT || 9882;
// Global Authentication - MUST come before any other middleware
app.use(cookieParser());
// Add inter-agent chat widget (after auth)
app.use(chatMiddleware({
agentId: 'agent-accounting',
agentName: 'Accounting',
port: 9882,
category: 'agent'
}));
// Anthropic AI client
const anthropic = new Anthropic({
apiKey: process.env.ANTHROPIC_API_KEY || '',
});
// Middleware
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// Session configuration with extended TypeScript types
declare module 'express-session' {
interface SessionData {
authenticated: boolean;
chatHistory: Array<{ role: 'user' | 'assistant', content: string }>;
}
}
app.use(
session({
secret: 'dw-agents-accounting-2025',
resave: false,
saveUninitialized: true,
rolling: true,
cookie: {
secure: false,
httpOnly: true,
maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days
},
})
);
// Authentication middleware
const requireAuth = (req: Request, res: Response, next: any) => {
if (req.session.authenticated) {
return next();
}
res.redirect('/login');
};
// Statistics tracking
const stats = {
totalQueries: 0,
expensesTracked: 0,
invoicesManaged: 0,
reportsGenerated: 0,
budgetForecasts: 0,
lastActivity: new Date(),
};
// Sample financial data (in production, integrate with QuickBooks/accounting system)
interface Expense {
id: string;
date: Date;
category: string;
vendor: string;
amount: number;
description: string;
status: 'pending' | 'approved' | 'paid';
}
interface Invoice {
id: string;
invoiceNumber: string;
customerName: string;
issueDate: Date;
dueDate: Date;
amount: number;
status: 'draft' | 'sent' | 'paid' | 'overdue';
}
const expenses: Expense[] = [
{
id: 'exp-001',
date: new Date('2025-11-01'),
category: 'Office Supplies',
vendor: 'Amazon Business',
amount: 247.50,
description: 'Printer paper, pens, staplers',
status: 'paid'
},
{
id: 'exp-002',
date: new Date('2025-11-03'),
category: 'Marketing',
vendor: 'Constant Contact',
amount: 95.00,
description: 'Monthly email marketing subscription',
status: 'paid'
},
{
id: 'exp-003',
date: new Date('2025-11-04'),
category: 'Software',
vendor: 'Shopify',
amount: 299.00,
description: 'Monthly e-commerce platform fee',
status: 'approved'
},
{
id: 'exp-004',
date: new Date('2025-11-05'),
category: 'Shipping',
vendor: 'UPS',
amount: 156.75,
description: 'Customer order shipping fees',
status: 'pending'
},
];
const invoices: Invoice[] = [
{
id: 'inv-001',
invoiceNumber: 'INV-2025-1001',
customerName: 'ABC Design Studio',
issueDate: new Date('2025-10-15'),
dueDate: new Date('2025-11-15'),
amount: 4250.00,
status: 'paid'
},
{
id: 'inv-002',
invoiceNumber: 'INV-2025-1002',
customerName: 'Interior Elegance LLC',
issueDate: new Date('2025-10-28'),
dueDate: new Date('2025-11-28'),
amount: 3890.50,
status: 'sent'
},
{
id: 'inv-003',
invoiceNumber: 'INV-2025-1003',
customerName: 'Luxury Homes Inc',
issueDate: new Date('2025-11-01'),
dueDate: new Date('2025-12-01'),
amount: 6750.00,
status: 'sent'
},
{
id: 'inv-004',
invoiceNumber: 'INV-2025-1004',
customerName: 'Modern Living Design',
issueDate: new Date('2025-09-20'),
dueDate: new Date('2025-10-20'),
amount: 2340.00,
status: 'overdue'
},
];
// Activity log
const activityLog: Array<{
timestamp: Date;
action: string;
details: string;
}> = [];
function logActivity(action: string, details: string) {
activityLog.unshift({
timestamp: new Date(),
action,
details,
});
if (activityLog.length > 50) {
activityLog.pop();
}
stats.lastActivity = new Date();
}
// Routes
// Login page
app.get('/login', (req: Request, res: Response) => {
res.send(`
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Accounting Agent - Login</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
.login-container {
background: white;
padding: 40px;
border-radius: 16px;
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
width: 100%;
max-width: 400px;
}
h1 {
color: #333;
font-size: 28px;
margin-bottom: 10px;
text-align: center;
}
.subtitle {
color: #666;
text-align: center;
margin-bottom: 30px;
font-size: 14px;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
color: #555;
margin-bottom: 8px;
font-weight: 500;
}
input {
width: 100%;
padding: 12px;
border: 2px solid #e0e0e0;
border-radius: 8px;
font-size: 16px;
transition: border-color 0.3s;
}
input:focus {
outline: none;
border-color: #667eea;
}
button {
width: 100%;
padding: 14px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
border-radius: 8px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: transform 0.2s;
}
button:hover {
transform: translateY(-2px);
}
.agent-badge {
background: #667eea;
color: white;
padding: 8px 16px;
border-radius: 20px;
font-size: 12px;
text-align: center;
margin-bottom: 20px;
font-weight: 600;
}
</style>
</head>
<body>
<div class="login-container">
<div class="agent-badge">💰 ACCOUNTING AGENT</div>
<h1>Financial Management</h1>
<p class="subtitle">DW-Agents System</p>
<form method="POST" action="/login">
<div class="form-group">
<label for="username">Username</label>
<input type="text" id="username" name="username" required autocomplete="username">
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" id="password" name="password" required autocomplete="current-password">
</div>
<button type="submit">Access Dashboard</button>
</form>
</div>
</body>
</html>
`);
});
// Login handler
app.post('/login', (req: Request, res: Response) => {
const { username, password } = req.body;
if (username === 'admin2025' && password === 'Otis') {
req.session.authenticated = true;
req.session.chatHistory = [];
logActivity('Login', 'User authenticated successfully');
res.redirect('/');
} else {
res.redirect('/login');
}
});
// Main dashboard
app.get('/', requireGlobalAuth, (req: Request, res: Response) => {
res.send(`
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Accounting Agent - Dashboard</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1400px;
margin: 0 auto;
}
.header {
background: white;
padding: 30px;
border-radius: 16px;
margin-bottom: 20px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}
.header h1 {
color: #333;
font-size: 32px;
margin-bottom: 10px;
}
.agent-status {
display: inline-block;
background: #10b981;
color: white;
padding: 6px 12px;
border-radius: 20px;
font-size: 14px;
font-weight: 600;
}
.tabs {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
.tab-button {
background: white;
color: #667eea;
padding: 12px 24px;
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 16px;
font-weight: 600;
transition: all 0.3s;
}
.tab-button:hover {
background: #f3f4f6;
}
.tab-button.active {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.tab-content {
display: none;
}
.tab-content.active {
display: block;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.stat-card {
background: white;
padding: 24px;
border-radius: 12px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}
.stat-value {
font-size: 32px;
font-weight: 700;
color: #667eea;
margin-bottom: 5px;
}
.stat-label {
color: #666;
font-size: 14px;
}
.section {
background: white;
padding: 30px;
border-radius: 16px;
margin-bottom: 20px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}
.section h2 {
color: #333;
font-size: 24px;
margin-bottom: 20px;
display: flex;
align-items: center;
gap: 10px;
}
.chat-container {
display: flex;
flex-direction: column;
height: 600px;
}
.chat-messages {
flex: 1;
overflow-y: auto;
padding: 20px;
background: #f9fafb;
border-radius: 12px;
margin-bottom: 20px;
}
.message {
margin-bottom: 16px;
padding: 12px 16px;
border-radius: 12px;
max-width: 80%;
}
.message.user {
background: #667eea;
color: white;
margin-left: auto;
}
.message.assistant {
background: white;
color: #333;
border: 1px solid #e0e0e0;
}
.chat-input-container {
display: flex;
gap: 10px;
}
.chat-input {
flex: 1;
padding: 14px;
border: 2px solid #e0e0e0;
border-radius: 12px;
font-size: 16px;
font-family: inherit;
}
.send-button {
padding: 14px 28px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
border-radius: 12px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: transform 0.2s;
}
.send-button:hover {
transform: translateY(-2px);
}
.suggestion-chips {
display: flex;
gap: 10px;
margin-bottom: 15px;
flex-wrap: wrap;
}
.chip {
background: #f3f4f6;
color: #667eea;
padding: 8px 16px;
border-radius: 20px;
font-size: 14px;
cursor: pointer;
border: 2px solid transparent;
transition: all 0.3s;
}
.chip:hover {
background: #667eea;
color: white;
border-color: #667eea;
}
.table-container {
overflow-x: auto;
}
table {
width: 100%;
border-collapse: collapse;
}
th, td {
padding: 12px;
text-align: left;
border-bottom: 1px solid #e0e0e0;
}
th {
background: #f9fafb;
color: #667eea;
font-weight: 600;
}
tr:hover {
background: #f9fafb;
}
.status-badge {
display: inline-block;
padding: 4px 12px;
border-radius: 12px;
font-size: 12px;
font-weight: 600;
}
.status-paid { background: #d1fae5; color: #065f46; }
.status-approved { background: #dbeafe; color: #1e40af; }
.status-pending { background: #fef3c7; color: #92400e; }
.status-sent { background: #dbeafe; color: #1e40af; }
.status-overdue { background: #fee2e2; color: #991b1b; }
.status-draft { background: #f3f4f6; color: #374151; }
.action-button {
padding: 6px 12px;
background: #667eea;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
}
.action-button:hover {
background: #5568d3;
}
.chart-placeholder {
height: 300px;
background: #f9fafb;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
color: #666;
font-size: 14px;
}
/* Hooks Panel */
.hooks-panel {
background: white;
padding: 20px;
border-radius: 15px;
box-shadow: 0 5px 20px rgba(0,0,0,0.1);
margin: 20px 0;
}
.hooks-panel h3 {
color: #555;
font-size: 0.95em;
text-transform: uppercase;
margin-bottom: 15px;
font-weight: 700;
}
.hook-btn {
width: 100%;
padding: 12px 16px;
margin-bottom: 10px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
border-radius: 10px;
cursor: pointer;
font-weight: 600;
font-size: 0.9em;
transition: all 0.3s;
text-align: left;
display: flex;
align-items: center;
gap: 8px;
}
.hook-btn:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.3);
}
.hook-btn:active {
transform: translateY(0);
}
.hook-btn:disabled {
opacity: 0.6;
cursor: not-allowed;
transform: none;
}
.hook-btn.fix {
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
}
.hook-btn.monitor {
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
}
</style>
</head>
<body style="padding: 20px;">
<div style="text-align: center; padding: 20px; background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); color: white; margin-bottom: 20px;">
<h1>💰 Accounting Agent</h1>
<p>Port: 9882</p>
</div>
<div class="container" style="margin-top: 20px;">
<div class="header">
<h1>💰 Accounting Agent</h1>
<p style="color: #666; margin-top: 5px;">Financial Management & Reporting System</p>
<div style="margin-top: 15px;">
<span class="agent-status">● ONLINE</span>
<span style="color: #888; margin-left: 15px; font-size: 14px;">Port 9882</span>
</div>
</div>
<!-- System Hooks Panel -->
<div class="hooks-panel">
<h3>System Tools</h3>
<button class="hook-btn" onclick="runHook('service-health-check', 'check')">
<span>🏥</span> Health Check
</button>
<button class="hook-btn fix" onclick="runHook('service-health-check', 'fix')">
<span>🔧</span> Auto-Fix
</button>
<button class="hook-btn monitor" onclick="runHook('agent-health-monitor')">
<span>👥</span> Monitor Agents
</button>
</div>
<div class="tabs">
<button class="tab-button active" onclick="switchTab('overview')">📊 Overview</button>
<button class="tab-button" onclick="switchTab('chat')">💬 Chat Assistant</button>
<button class="tab-button" onclick="switchTab('expenses')">📝 Expenses</button>
<button class="tab-button" onclick="switchTab('invoices')">📄 Invoices</button>
<button class="tab-button" onclick="switchTab('reports')">📈 Reports</button>
</div>
<!-- Overview Tab -->
<div id="tab-overview" class="tab-content active">
<div class="stats-grid">
<div class="stat-card">
<div class="stat-value">${stats.totalQueries}</div>
<div class="stat-label">Total Queries</div>
</div>
<div class="stat-card">
<div class="stat-value">${stats.expensesTracked}</div>
<div class="stat-label">Expenses Tracked</div>
</div>
<div class="stat-card">
<div class="stat-value">${stats.invoicesManaged}</div>
<div class="stat-label">Invoices Managed</div>
</div>
<div class="stat-card">
<div class="stat-value">${stats.reportsGenerated}</div>
<div class="stat-label">Reports Generated</div>
</div>
</div>
<div class="section">
<h2>📊 Financial Summary</h2>
<div class="chart-placeholder">
Chart: Monthly Revenue vs Expenses (Integration with QuickBooks recommended)
</div>
</div>
<div class="section">
<h2>🎯 Quick Actions</h2>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px;">
<button class="action-button" style="padding: 20px; font-size: 16px;" onclick="switchTab('expenses')">
Track New Expense
</button>
<button class="action-button" style="padding: 20px; font-size: 16px;" onclick="switchTab('invoices')">
Create Invoice
</button>
<button class="action-button" style="padding: 20px; font-size: 16px;" onclick="switchTab('reports')">
Generate P&L Report
</button>
<button class="action-button" style="padding: 20px; font-size: 16px;" onclick="switchTab('chat')">
Ask Financial Question
</button>
</div>
</div>
</div>
<!-- Chat Tab -->
<div id="tab-chat" class="tab-content">
<div class="section">
<h2>💬 Financial Chat Assistant</h2>
<p style="color: #666; margin-bottom: 20px;">Ask questions about finances, expenses, invoices, or request reports</p>
<div class="suggestion-chips">
<span class="chip" onclick="sendSuggestion('What are my pending expenses?')">What are my pending expenses?</span>
<span class="chip" onclick="sendSuggestion('Show me overdue invoices')">Show overdue invoices</span>
<span class="chip" onclick="sendSuggestion('Generate monthly P&L report')">Generate monthly P&L</span>
<span class="chip" onclick="sendSuggestion('What is my cash flow status?')">Cash flow status</span>
</div>
<div class="chat-container">
<div class="chat-messages" id="chatMessages">
<div class="message assistant">
Hello! I'm your Accounting Agent powered by Claude 4. I can help you with:
<br><br>
• Track and categorize expenses<br>
• Manage invoices and payments<br>
• Generate P&L reports<br>
• Budget forecasting<br>
• Financial analysis and insights<br>
<br>
How can I assist you today?
</div>
</div>
<div class="chat-input-container">
<input
type="text"
class="chat-input"
id="chatInput"
placeholder="Ask about finances, expenses, invoices..."
onkeypress="if(event.key === 'Enter') sendMessage()"
>
<button class="send-button" onclick="sendMessage()">Send</button>
</div>
</div>
</div>
</div>
<!-- Expenses Tab -->
<div id="tab-expenses" class="tab-content">
<div class="section">
<h2>📝 Expense Tracking</h2>
<button class="action-button" style="margin-bottom: 20px;">+ Add New Expense</button>
<div class="table-container" id="expensesTable">
<p style="color: #888;">Loading expenses...</p>
</div>
</div>
</div>
<!-- Invoices Tab -->
<div id="tab-invoices" class="tab-content">
<div class="section">
<h2>📄 Invoice Management</h2>
<button class="action-button" style="margin-bottom: 20px;">+ Create New Invoice</button>
<div class="table-container" id="invoicesTable">
<p style="color: #888;">Loading invoices...</p>
</div>
</div>
</div>
<!-- Reports Tab -->
<div id="tab-reports" class="tab-content">
<div class="section">
<h2>📈 Financial Reports</h2>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 20px; margin-bottom: 30px;">
<div style="border: 2px solid #e0e0e0; padding: 20px; border-radius: 12px;">
<h3 style="color: #667eea; margin-bottom: 10px;">P&L Statement</h3>
<p style="color: #666; font-size: 14px; margin-bottom: 15px;">Profit and Loss report for selected period</p>
<button class="action-button">Generate Report</button>
</div>
<div style="border: 2px solid #e0e0e0; padding: 20px; border-radius: 12px;">
<h3 style="color: #667eea; margin-bottom: 10px;">Budget Analysis</h3>
<p style="color: #666; font-size: 14px; margin-bottom: 15px;">Compare actual vs budgeted amounts</p>
<button class="action-button">Generate Report</button>
</div>
<div style="border: 2px solid #e0e0e0; padding: 20px; border-radius: 12px;">
<h3 style="color: #667eea; margin-bottom: 10px;">Cash Flow</h3>
<p style="color: #666; font-size: 14px; margin-bottom: 15px;">Track cash inflows and outflows</p>
<button class="action-button">Generate Report</button>
</div>
<div style="border: 2px solid #e0e0e0; padding: 20px; border-radius: 12px;">
<h3 style="color: #667eea; margin-bottom: 10px;">Expense Breakdown</h3>
<p style="color: #666; font-size: 14px; margin-bottom: 15px;">Category-wise expense analysis</p>
<button class="action-button">Generate Report</button>
</div>
</div>
<div class="chart-placeholder">
Chart: Year-over-Year Financial Comparison
</div>
</div>
</div>
</div>
<script>
function switchTab(tabName) {
// Hide all tabs
document.querySelectorAll('.tab-content').forEach(tab => {
tab.classList.remove('active');
});
document.querySelectorAll('.tab-button').forEach(btn => {
btn.classList.remove('active');
});
// Show selected tab
document.getElementById('tab-' + tabName).classList.add('active');
event.target.classList.add('active');
// Load data if needed
if (tabName === 'expenses') {
loadExpenses();
} else if (tabName === 'invoices') {
loadInvoices();
}
}
async function sendMessage() {
const input = document.getElementById('chatInput');
const message = input.value.trim();
if (!message) return;
// Add user message
const chatMessages = document.getElementById('chatMessages');
chatMessages.innerHTML += \`
<div class="message user">\${message}</div>
\`;
input.value = '';
chatMessages.scrollTop = chatMessages.scrollHeight;
// Send to API
try {
const response = await fetch('/api/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message })
});
const data = await response.json();
// Add assistant response
chatMessages.innerHTML += \`
<div class="message assistant">\${data.response.replace(/\\n/g, '<br>')}</div>
\`;
chatMessages.scrollTop = chatMessages.scrollHeight;
} catch (error) {
console.error('Chat error:', error);
chatMessages.innerHTML += \`
<div class="message assistant">Sorry, I encountered an error. Please try again.</div>
\`;
}
}
function sendSuggestion(text) {
document.getElementById('chatInput').value = text;
sendMessage();
}
async function loadExpenses() {
const container = document.getElementById('expensesTable');
try {
const response = await fetch('/api/expenses');
const data = await response.json();
let html = '<table><thead><tr><th>Date</th><th>Category</th><th>Vendor</th><th>Description</th><th>Amount</th><th>Status</th><th>Actions</th></tr></thead><tbody>';
data.expenses.forEach(exp => {
const statusClass = 'status-' + exp.status;
html += \`
<tr>
<td>\${new Date(exp.date).toLocaleDateString()}</td>
<td>\${exp.category}</td>
<td>\${exp.vendor}</td>
<td>\${exp.description}</td>
<td>$\${exp.amount.toFixed(2)}</td>
<td><span class="status-badge \${statusClass}">\${exp.status.toUpperCase()}</span></td>
<td><button class="action-button">View</button></td>
</tr>
\`;
});
html += '</tbody></table>';
container.innerHTML = html;
} catch (error) {
console.error('Load expenses error:', error);
container.innerHTML = '<p style="color: #ef4444;">Error loading expenses</p>';
}
}
async function loadInvoices() {
const container = document.getElementById('invoicesTable');
try {
const response = await fetch('/api/invoices');
const data = await response.json();
let html = '<table><thead><tr><th>Invoice #</th><th>Customer</th><th>Issue Date</th><th>Due Date</th><th>Amount</th><th>Status</th><th>Actions</th></tr></thead><tbody>';
data.invoices.forEach(inv => {
const statusClass = 'status-' + inv.status;
html += \`
<tr>
<td>\${inv.invoiceNumber}</td>
<td>\${inv.customerName}</td>
<td>\${new Date(inv.issueDate).toLocaleDateString()}</td>
<td>\${new Date(inv.dueDate).toLocaleDateString()}</td>
<td>$\${inv.amount.toFixed(2)}</td>
<td><span class="status-badge \${statusClass}">\${inv.status.toUpperCase()}</span></td>
<td><button class="action-button">View</button></td>
</tr>
\`;
});
html += '</tbody></table>';
container.innerHTML = html;
} catch (error) {
console.error('Load invoices error:', error);
container.innerHTML = '<p style="color: #ef4444;">Error loading invoices</p>';
}
}
// Hook execution function
async function runHook(hookName, action = '') {
const btn = event.target.closest('.hook-btn');
if (btn.disabled) return;
btn.disabled = true;
const originalText = btn.innerHTML;
btn.innerHTML = '<span style="display:inline-block;width:12px;height:12px;border:2px solid rgba(255,255,255,0.3);border-radius:50%;border-top-color:white;animation:spin 0.6s linear infinite;"></span> Running...';
try {
const response = await fetch('/api/hooks/' + hookName, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ action })
});
const result = await response.json();
if (result.success) {
alert('Hook executed successfully!' + (result.output ? '\\n\\n' + result.output : ''));
} else {
alert('Hook error: ' + (result.error || 'Unknown error'));
}
} catch (err) {
console.error('Hook execution error:', err);
alert('Error: ' + err.message);
} finally {
btn.disabled = false;
btn.innerHTML = originalText;
}
}
</script>
</body>
</html>
`);
});
// API Endpoints
// Chat endpoint
app.post('/api/chat', requireGlobalAuth, async (req: Request, res: Response) => {
try {
const { message } = req.body;
stats.totalQueries++;
if (!req.session.chatHistory) {
req.session.chatHistory = [];
}
// Add user message to history
req.session.chatHistory.push({
role: 'user',
content: message
});
// Keep only last 20 messages for context
if (req.session.chatHistory.length > 20) {
req.session.chatHistory = req.session.chatHistory.slice(-20);
}
const systemPrompt = `You are the Accounting Agent for Designer Wallcoverings, powered by Claude 4 Sonnet.
Your role is to assist with financial management tasks:
CAPABILITIES:
- Expense tracking and categorization
- Invoice management and tracking
- P&L report generation
- Budget forecasting and analysis
- Financial insights and recommendations
- Cash flow analysis
- Vendor payment tracking
CURRENT SYSTEM DATA:
- Total expenses tracked: ${expenses.length}
- Pending expenses: ${expenses.filter(e => e.status === 'pending').length}
- Total invoices: ${invoices.length}
- Overdue invoices: ${invoices.filter(i => i.status === 'overdue').length}
SAMPLE EXPENSE CATEGORIES:
- Office Supplies
- Marketing
- Software/Subscriptions
- Shipping
- Inventory
- Utilities
- Professional Services
When users ask about specific financial data, provide helpful summaries and actionable insights. If they need to integrate with QuickBooks or other accounting software, recommend that enhancement.
Be professional, accurate, and provide clear financial guidance.`;
const response = await anthropic.messages.create({
model: 'claude-opus-4-8',
max_tokens: 1024,
system: systemPrompt,
messages: req.session.chatHistory
});
const assistantMessage = response.content[0].type === 'text'
? response.content[0].text
: 'I apologize, but I encountered an error processing your request.';
// Add assistant response to history
req.session.chatHistory.push({
role: 'assistant',
content: assistantMessage
});
logActivity('Chat Query', `User asked: "${message.substring(0, 50)}..."`);
res.json({ response: assistantMessage });
} catch (error) {
console.error('Chat error:', error);
res.status(500).json({ error: 'Failed to process chat message' });
}
});
// Get expenses
app.get('/api/expenses', requireGlobalAuth, (req: Request, res: Response) => {
res.json({ expenses });
});
// Get invoices
app.get('/api/invoices', requireGlobalAuth, (req: Request, res: Response) => {
res.json({ invoices });
});
// Get statistics
app.get('/api/stats', requireGlobalAuth, (req: Request, res: Response) => {
res.json(stats);
});
// Get activity log
app.get('/api/activity', requireGlobalAuth, (req: Request, res: Response) => {
res.json({ activities: activityLog });
});
// 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
});
});
// Hook execution endpoint
app.post('/api/hooks/:hookName', requireGlobalAuth, async (req, res) => {
const { hookName } = req.params;
const { action } = req.body;
try {
const cmd = `node /root/Projects/Designer-Wallcoverings/DW-MCP/DW-Hooks/${hookName}-hook.js ${action || ''}`.trim();
exec(cmd, { timeout: 30000 }, (error, stdout, stderr) => {
if (error) {
console.error('Hook execution error:', error);
return res.status(500).json({
success: false,
error: stderr || error.message
});
}
res.json({
success: true,
output: stdout
});
});
} catch (err) {
console.error('Hook error:', err);
res.status(500).json({
success: false,
error: err.message
});
}
});
app.listen(PORT, () => {
console.log(`💰 Accounting Agent running on port ${PORT}`);
console.log(`Dashboard: http://45.61.58.125:${PORT}`);
logActivity('System Start', 'Accounting Agent initialized');
});