Clean up .gitignore: remove binaries (images, video, audio, PDFs) from tracking

This commit is contained in:
Jake Shore 2026-01-31 01:13:53 -05:00
parent 0bd02ddfba
commit dd17cf35ab
201 changed files with 90 additions and 22707 deletions

View File

@ -1,5 +0,0 @@
# Browser Use MCP Environment Variables
# DO NOT COMMIT TO PUBLIC REPOS
BROWSER_USE_API_KEY=not_set

110
.gitignore vendored
View File

@ -1,29 +1,87 @@
.DS_Store
.env
.idea/
.reonomy-credentials.*
.vscode/
*.env
*.env.local
*.log
*.png
*.swo
*.swp
/tmp/reonomy-*.html
/tmp/reonomy-*.json
/tmp/reonomy-*.png
/tmp/reonomy-*.txt
# Credentials
# IDE files
# Logs
# Node modules
# OS files # OS files
# Screenshots .DS_Store
# Test files
node_modules/
npm-debug.log*
package-lock.json
reonomy-scraper.log
Thumbs.db Thumbs.db
# IDE files
.idea/
.vscode/
*.swp
*.swo
# Environment / Secrets
.env
*.env
*.env.*
.reonomy-credentials.*
# Logs
*.log
npm-debug.log*
reonomy-scraper.log
# Node
node_modules/
package-lock.json
# ========================
# Binary / Media Files
# (These don't belong in git)
# ========================
# Images
*.png
*.jpg
*.jpeg
*.gif
*.webp
*.ico
*.svg
*.bmp
*.tiff
# Video
*.mp4
*.webm
*.mov
*.avi
*.mkv
*.wmv
# Audio
*.wav
*.mp3
*.ogg
*.flac
*.aac
*.m4a
*.caf
# Documents / PDFs
*.pdf
# Archives
*.zip
*.tar
*.gz
*.rar
*.7z
# Compiled / Binary
*.pyc
__pycache__/
dist/
# ========================
# Project-Specific
# ========================
# Reonomy
/tmp/reonomy-*
reonomy-auth.json
reonomy-daily-stats.json
# Frameworks
pageindex-framework/ pageindex-framework/
# Temp files
/tmp/

Binary file not shown.

Before

Width:  |  Height:  |  Size: 399 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 328 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 391 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 379 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 225 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 351 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 337 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 312 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 628 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 797 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 671 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

View File

@ -0,0 +1 @@
Take a step to do it. Yes. Yes! You did it! I didn't know all the feelings I could miss. Until I looked inside my mind I've been lost since. I don't know how to fit in my own skin. Don't feel at home in this world I'm living in.

View File

@ -0,0 +1 @@
There's no me, there's just us When I said it, I swear I meant it You were always enough I didn't mean to take you for granted You saved me from my broken soul We gave each other full control I left you crying on the road We both know that the night gets cold We gave each other full control We both know that the night gets cold You call me crazy, I'm just faded I'm escaping from your clutch I'm just chasing sweet sedation Using your heart as my crutch Self-inflated expectations Never met or overcome Now I'm jaded, complicated No surprise you chose to run You saved me from my broken soul We gave each other full control I left you crying on the road We both know that the night gets cold We gave each other full control We both know that the night gets cold You saved me from my broken soul We gave each other full control I left you crying on the road We both know that the night gets cold

View File

@ -0,0 +1 @@
I lost my heart, now I feel nothing It's been a while since I've felt something The blame cuts deep and thoughts start crushing I ease the pain with self-destruction I've been so out of touch Mistaken kindness for affection, it's not love Have I been cursed with bad luck? Cause my stars don't align with my sun I lost my heart, now I feel nothing It's been a while since I've felt something The blame cuts deep and thoughts start crushing I ease the pain with self-destruction

View File

@ -0,0 +1 @@
The teardrop in your eye Contains a single lethal dose You're lying by my side But I can't seem to find a pulse The days keep passing by This love's a waste of time, you know Another sleepless night And I might need to overdose Please don't go Won't you stay? Please don't go Won't you stay? You've got me on my knees I'm begging for sweet relief Each time you go You break a piece of my soul You cut me then stomp the blade And convince me you're all I need I'm seeing ghosts They've got their hands around my throat You keep on turning little nothings into somethings You throw in pillows slamming undeserving doors Your words cut deeper than a knife Can't even look me in the eye So please just go Please just go Don't you stay Please just go Don't you stay You've got me on my knees I'm begging for sweet relief Each time you go You break a piece of my soul You cut me then stomp the blade And convince me you're all I need I'm seeing ghosts They've got their hands around my throat You cut me then stomp the blade And convince me you're all I need

View File

@ -0,0 +1 @@
To your right is the Baguio Botanical Garden To your left is the Baguio Botanical Garden To your right is the Baguio Botanical Garden To your right is the Baguio Botanical Garden To your left is the Baguio Botanical Garden To your right is the Baguio Botanical Garden To your left is the Baguio Botanical Garden To your right is the Baguio Botanical Garden To your left is the Baguio Botanical Garden To your right is the Baguio Botanical Garden To your left is the Baguio Botanical Garden To your right is the Baguio Botanical Garden

View File

@ -0,0 +1 @@
We are turning right to Session Road We are turning right to Session Road Are you still there? If so, we'd like to thank you for joining us in today's drive. Thank you for joining us on this drive. We are turning right to Session Road Are you still there? If so, we'd like to thank you for joining us in today's drive.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 133 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 227 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 197 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 155 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 204 KiB

View File

@ -1,11 +0,0 @@
# GoHighLevel API Configuration
GHL_API_KEY=your_private_integration_api_key_here
GHL_BASE_URL=https://services.leadconnectorhq.com
GHL_LOCATION_ID=your_location_id_here
# Server Configuration
MCP_SERVER_PORT=8000
NODE_ENV=development
# Optional: For AI features
OPENAI_API_KEY=your_openai_key_here_optional

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,654 +0,0 @@
/**
* MCP Apps Manager
* Manages rich UI components for GoHighLevel MCP Server
*/
import * as fs from 'fs';
import * as path from 'path';
import { fileURLToPath } from 'url';
/**
* MCP Apps Manager class
* Registers app tools and handles structuredContent responses
*/
// ESM equivalent of __dirname
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// Resolve UI build path - works regardless of working directory
function getUIBuildPath() {
// When compiled, this file is at dist/apps/index.js
// UI files are at dist/app-ui/
const fromDist = path.resolve(__dirname, '..', 'app-ui');
if (fs.existsSync(fromDist)) {
return fromDist;
}
// Fallback: try process.cwd() based paths
const appUiPath = path.join(process.cwd(), 'dist', 'app-ui');
if (fs.existsSync(appUiPath)) {
return appUiPath;
}
// Default fallback
return fromDist;
}
export class MCPAppsManager {
ghlClient;
resourceHandlers = new Map();
uiBuildPath;
constructor(ghlClient) {
this.ghlClient = ghlClient;
this.uiBuildPath = getUIBuildPath();
process.stderr.write(`[MCP Apps] UI build path: ${this.uiBuildPath}\n`);
this.registerResourceHandlers();
}
/**
* Register all UI resource handlers
*/
registerResourceHandlers() {
const resources = [
// All 11 MCP Apps
{ uri: 'ui://ghl/mcp-app', file: 'mcp-app.html' },
{ uri: 'ui://ghl/pipeline-board', file: 'pipeline-board.html' },
{ uri: 'ui://ghl/quick-book', file: 'quick-book.html' },
{ uri: 'ui://ghl/opportunity-card', file: 'opportunity-card.html' },
{ uri: 'ui://ghl/contact-grid', file: 'contact-grid.html' },
{ uri: 'ui://ghl/calendar-view', file: 'calendar-view.html' },
{ uri: 'ui://ghl/invoice-preview', file: 'invoice-preview.html' },
{ uri: 'ui://ghl/campaign-stats', file: 'campaign-stats.html' },
{ uri: 'ui://ghl/agent-stats', file: 'agent-stats.html' },
{ uri: 'ui://ghl/contact-timeline', file: 'contact-timeline.html' },
{ uri: 'ui://ghl/workflow-status', file: 'workflow-status.html' },
];
for (const resource of resources) {
this.resourceHandlers.set(resource.uri, {
uri: resource.uri,
mimeType: 'text/html;profile=mcp-app',
getContent: () => this.loadUIResource(resource.file),
});
}
}
/**
* Load UI resource from build directory
*/
loadUIResource(filename) {
const filePath = path.join(this.uiBuildPath, filename);
try {
return fs.readFileSync(filePath, 'utf-8');
}
catch (error) {
process.stderr.write(`[MCP Apps] UI resource not found: ${filePath}\n`);
return this.getFallbackHTML(filename);
}
}
/**
* Generate fallback HTML when UI resource is not built
*/
getFallbackHTML(filename) {
const componentName = filename.replace('.html', '');
return `
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>GHL ${componentName}</title>
<style>
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; padding: 20px; }
.fallback { text-align: center; color: #666; }
</style>
</head>
<body>
<div class="fallback">
<p>UI component "${componentName}" is loading...</p>
<p>Run <code>npm run build:ui</code> to build UI components.</p>
</div>
<script>
window.addEventListener('message', (e) => {
if (e.data?.type === 'mcp-app-init') {
console.log('MCP App data:', e.data.data);
}
});
</script>
</body>
</html>
`.trim();
}
/**
* Get tool definitions for all app tools
*/
getToolDefinitions() {
return [
// 1. Contact Grid - search and display contacts
{
name: 'view_contact_grid',
description: 'Display contact search results in a data grid with sorting and pagination. Returns a visual UI component.',
inputSchema: {
type: 'object',
properties: {
query: { type: 'string', description: 'Search query string' },
limit: { type: 'number', description: 'Maximum results (default: 25)' }
}
},
_meta: {
ui: { resourceUri: 'ui://ghl/contact-grid' }
}
},
// 2. Pipeline Board - Kanban view of opportunities
{
name: 'view_pipeline_board',
description: 'Display a pipeline as an interactive Kanban board with opportunities. Returns a visual UI component.',
inputSchema: {
type: 'object',
properties: {
pipelineId: { type: 'string', description: 'Pipeline ID to display' }
},
required: ['pipelineId']
},
_meta: {
ui: { resourceUri: 'ui://ghl/pipeline-board' }
}
},
// 3. Quick Book - appointment booking
{
name: 'view_quick_book',
description: 'Display a quick booking interface for scheduling appointments. Returns a visual UI component.',
inputSchema: {
type: 'object',
properties: {
calendarId: { type: 'string', description: 'Calendar ID for booking' },
contactId: { type: 'string', description: 'Optional contact ID to pre-fill' }
},
required: ['calendarId']
},
_meta: {
ui: { resourceUri: 'ui://ghl/quick-book' }
}
},
// 4. Opportunity Card - single opportunity details
{
name: 'view_opportunity_card',
description: 'Display a single opportunity with details, value, and stage info. Returns a visual UI component.',
inputSchema: {
type: 'object',
properties: {
opportunityId: { type: 'string', description: 'Opportunity ID to display' }
},
required: ['opportunityId']
},
_meta: {
ui: { resourceUri: 'ui://ghl/opportunity-card' }
}
},
// 5. Calendar View - calendar with events
{
name: 'view_calendar',
description: 'Display a calendar with events and appointments. Returns a visual UI component.',
inputSchema: {
type: 'object',
properties: {
calendarId: { type: 'string', description: 'Calendar ID to display' },
startDate: { type: 'string', description: 'Start date (ISO format)' },
endDate: { type: 'string', description: 'End date (ISO format)' }
},
required: ['calendarId']
},
_meta: {
ui: { resourceUri: 'ui://ghl/calendar-view' }
}
},
// 6. Invoice Preview - invoice details
{
name: 'view_invoice',
description: 'Display an invoice preview with line items and payment status. Returns a visual UI component.',
inputSchema: {
type: 'object',
properties: {
invoiceId: { type: 'string', description: 'Invoice ID to display' }
},
required: ['invoiceId']
},
_meta: {
ui: { resourceUri: 'ui://ghl/invoice-preview' }
}
},
// 7. Campaign Stats - campaign performance metrics
{
name: 'view_campaign_stats',
description: 'Display campaign statistics and performance metrics. Returns a visual UI component.',
inputSchema: {
type: 'object',
properties: {
campaignId: { type: 'string', description: 'Campaign ID to display stats for' }
},
required: ['campaignId']
},
_meta: {
ui: { resourceUri: 'ui://ghl/campaign-stats' }
}
},
// 8. Agent Stats - agent/user performance
{
name: 'view_agent_stats',
description: 'Display agent/user performance statistics and metrics. Returns a visual UI component.',
inputSchema: {
type: 'object',
properties: {
userId: { type: 'string', description: 'User/Agent ID to display stats for' },
dateRange: { type: 'string', description: 'Date range (e.g., "last7days", "last30days")' }
}
},
_meta: {
ui: { resourceUri: 'ui://ghl/agent-stats' }
}
},
// 9. Contact Timeline - activity history for a contact
{
name: 'view_contact_timeline',
description: 'Display a contact\'s activity timeline with all interactions. Returns a visual UI component.',
inputSchema: {
type: 'object',
properties: {
contactId: { type: 'string', description: 'Contact ID to display timeline for' }
},
required: ['contactId']
},
_meta: {
ui: { resourceUri: 'ui://ghl/contact-timeline' }
}
},
// 10. Workflow Status - workflow execution status
{
name: 'view_workflow_status',
description: 'Display workflow execution status and history. Returns a visual UI component.',
inputSchema: {
type: 'object',
properties: {
workflowId: { type: 'string', description: 'Workflow ID to display status for' }
},
required: ['workflowId']
},
_meta: {
ui: { resourceUri: 'ui://ghl/workflow-status' }
}
},
// 11. MCP App - generic/main dashboard
{
name: 'view_dashboard',
description: 'Display the main GHL dashboard overview. Returns a visual UI component.',
inputSchema: {
type: 'object',
properties: {}
},
_meta: {
ui: { resourceUri: 'ui://ghl/mcp-app' }
}
},
// 12. Update Opportunity - action tool for UI to update opportunities
{
name: 'update_opportunity',
description: 'Update an opportunity (move to stage, change value, status, etc.)',
inputSchema: {
type: 'object',
properties: {
opportunityId: { type: 'string', description: 'Opportunity ID to update' },
pipelineStageId: { type: 'string', description: 'New stage ID (for moving)' },
name: { type: 'string', description: 'Opportunity name' },
monetaryValue: { type: 'number', description: 'Monetary value' },
status: { type: 'string', enum: ['open', 'won', 'lost', 'abandoned'], description: 'Opportunity status' }
},
required: ['opportunityId']
}
}
];
}
/**
* Get app tool names for routing
*/
getAppToolNames() {
return [
'view_contact_grid',
'view_pipeline_board',
'view_quick_book',
'view_opportunity_card',
'view_calendar',
'view_invoice',
'view_campaign_stats',
'view_agent_stats',
'view_contact_timeline',
'view_workflow_status',
'view_dashboard',
'update_opportunity'
];
}
/**
* Check if a tool is an app tool
*/
isAppTool(toolName) {
return this.getAppToolNames().includes(toolName);
}
/**
* Execute an app tool
*/
async executeTool(toolName, args) {
process.stderr.write(`[MCP Apps] Executing app tool: ${toolName}\n`);
switch (toolName) {
case 'view_contact_grid':
return await this.viewContactGrid(args.query, args.limit);
case 'view_pipeline_board':
return await this.viewPipelineBoard(args.pipelineId);
case 'view_quick_book':
return await this.viewQuickBook(args.calendarId, args.contactId);
case 'view_opportunity_card':
return await this.viewOpportunityCard(args.opportunityId);
case 'view_calendar':
return await this.viewCalendar(args.calendarId, args.startDate, args.endDate);
case 'view_invoice':
return await this.viewInvoice(args.invoiceId);
case 'view_campaign_stats':
return await this.viewCampaignStats(args.campaignId);
case 'view_agent_stats':
return await this.viewAgentStats(args.userId, args.dateRange);
case 'view_contact_timeline':
return await this.viewContactTimeline(args.contactId);
case 'view_workflow_status':
return await this.viewWorkflowStatus(args.workflowId);
case 'view_dashboard':
return await this.viewDashboard();
case 'update_opportunity':
return await this.updateOpportunity(args);
default:
throw new Error(`Unknown app tool: ${toolName}`);
}
}
/**
* View contact grid (search results)
*/
async viewContactGrid(query, limit) {
const response = await this.ghlClient.searchContacts({
locationId: this.ghlClient.getConfig().locationId,
query: query,
limit: limit || 25
});
if (!response.success) {
throw new Error(response.error?.message || 'Failed to search contacts');
}
const data = response.data;
const resourceHandler = this.resourceHandlers.get('ui://ghl/contact-grid');
return this.createAppResult(`Found ${data?.contacts?.length || 0} contacts`, resourceHandler.uri, resourceHandler.mimeType, resourceHandler.getContent(), data);
}
/**
* View pipeline board (Kanban)
*/
async viewPipelineBoard(pipelineId) {
const [pipelinesResponse, opportunitiesResponse] = await Promise.all([
this.ghlClient.getPipelines(),
this.ghlClient.searchOpportunities({
location_id: this.ghlClient.getConfig().locationId,
pipeline_id: pipelineId
})
]);
if (!pipelinesResponse.success) {
throw new Error(pipelinesResponse.error?.message || 'Failed to get pipeline');
}
const pipeline = pipelinesResponse.data?.pipelines?.find((p) => p.id === pipelineId);
const opportunities = opportunitiesResponse.data?.opportunities || [];
// Simplify opportunity data to only include fields the UI needs (reduces payload size)
const simplifiedOpportunities = opportunities.map((opp) => ({
id: opp.id,
name: opp.name || 'Untitled',
pipelineStageId: opp.pipelineStageId,
status: opp.status || 'open',
monetaryValue: opp.monetaryValue || 0,
contact: opp.contact ? {
name: opp.contact.name || 'Unknown',
email: opp.contact.email,
phone: opp.contact.phone
} : { name: 'Unknown' },
updatedAt: opp.updatedAt || opp.createdAt,
createdAt: opp.createdAt,
source: opp.source
}));
const data = {
pipeline,
opportunities: simplifiedOpportunities,
stages: pipeline?.stages || []
};
const resourceHandler = this.resourceHandlers.get('ui://ghl/pipeline-board');
return this.createAppResult(`Pipeline: ${pipeline?.name || 'Unknown'} (${opportunities.length} opportunities)`, resourceHandler.uri, resourceHandler.mimeType, resourceHandler.getContent(), data);
}
/**
* View quick book interface
*/
async viewQuickBook(calendarId, contactId) {
const [calendarResponse, contactResponse] = await Promise.all([
this.ghlClient.getCalendar(calendarId),
contactId ? this.ghlClient.getContact(contactId) : Promise.resolve({ success: true, data: null })
]);
if (!calendarResponse.success) {
throw new Error(calendarResponse.error?.message || 'Failed to get calendar');
}
const data = {
calendar: calendarResponse.data,
contact: contactResponse.data,
locationId: this.ghlClient.getConfig().locationId
};
const resourceHandler = this.resourceHandlers.get('ui://ghl/quick-book');
return this.createAppResult(`Quick booking for calendar: ${calendarResponse.data?.name || calendarId}`, resourceHandler.uri, resourceHandler.mimeType, resourceHandler.getContent(), data);
}
/**
* View opportunity card
*/
async viewOpportunityCard(opportunityId) {
const response = await this.ghlClient.getOpportunity(opportunityId);
if (!response.success) {
throw new Error(response.error?.message || 'Failed to get opportunity');
}
const opportunity = response.data;
const resourceHandler = this.resourceHandlers.get('ui://ghl/opportunity-card');
return this.createAppResult(`Opportunity: ${opportunity?.name || opportunityId}`, resourceHandler.uri, resourceHandler.mimeType, resourceHandler.getContent(), opportunity);
}
/**
* View calendar
*/
async viewCalendar(calendarId, startDate, endDate) {
const now = new Date();
const start = startDate || new Date(now.getFullYear(), now.getMonth(), 1).toISOString();
const end = endDate || new Date(now.getFullYear(), now.getMonth() + 1, 0).toISOString();
const [calendarResponse, eventsResponse] = await Promise.all([
this.ghlClient.getCalendar(calendarId),
this.ghlClient.getCalendarEvents({
calendarId: calendarId,
startTime: start,
endTime: end,
locationId: this.ghlClient.getConfig().locationId
})
]);
if (!calendarResponse.success) {
throw new Error(calendarResponse.error?.message || 'Failed to get calendar');
}
const calendar = calendarResponse.data;
const data = {
calendar: calendarResponse.data,
events: eventsResponse.data?.events || [],
startDate: start,
endDate: end
};
const resourceHandler = this.resourceHandlers.get('ui://ghl/calendar-view');
return this.createAppResult(`Calendar: ${calendar?.name || 'Unknown'} (${data.events.length} events)`, resourceHandler.uri, resourceHandler.mimeType, resourceHandler.getContent(), data);
}
/**
* View campaign stats
*/
async viewCampaignStats(campaignId) {
// Get email campaigns
const response = await this.ghlClient.getEmailCampaigns({});
const campaigns = response.data?.schedules || [];
const campaign = campaigns.find((c) => c.id === campaignId) || { id: campaignId };
const data = {
campaign,
campaigns,
campaignId,
locationId: this.ghlClient.getConfig().locationId
};
const resourceHandler = this.resourceHandlers.get('ui://ghl/campaign-stats');
return this.createAppResult(`Campaign stats: ${campaign?.name || campaignId}`, resourceHandler.uri, resourceHandler.mimeType, resourceHandler.getContent(), data);
}
/**
* View agent stats
*/
async viewAgentStats(userId, dateRange) {
// Get location info which may include user data
const locationResponse = await this.ghlClient.getLocationById(this.ghlClient.getConfig().locationId);
const data = {
userId,
dateRange: dateRange || 'last30days',
location: locationResponse.data,
locationId: this.ghlClient.getConfig().locationId
};
const resourceHandler = this.resourceHandlers.get('ui://ghl/agent-stats');
return this.createAppResult(userId ? `Agent stats: ${userId}` : 'Agent overview', resourceHandler.uri, resourceHandler.mimeType, resourceHandler.getContent(), data);
}
/**
* View contact timeline
*/
async viewContactTimeline(contactId) {
const [contactResponse, notesResponse, tasksResponse] = await Promise.all([
this.ghlClient.getContact(contactId),
this.ghlClient.getContactNotes(contactId),
this.ghlClient.getContactTasks(contactId)
]);
if (!contactResponse.success) {
throw new Error(contactResponse.error?.message || 'Failed to get contact');
}
const contact = contactResponse.data;
const data = {
contact: contactResponse.data,
notes: notesResponse.data || [],
tasks: tasksResponse.data || []
};
const resourceHandler = this.resourceHandlers.get('ui://ghl/contact-timeline');
return this.createAppResult(`Timeline for ${contact?.firstName || ''} ${contact?.lastName || ''}`, resourceHandler.uri, resourceHandler.mimeType, resourceHandler.getContent(), data);
}
/**
* View workflow status
*/
async viewWorkflowStatus(workflowId) {
const response = await this.ghlClient.getWorkflows({
locationId: this.ghlClient.getConfig().locationId
});
const workflows = response.data?.workflows || [];
const workflow = workflows.find((w) => w.id === workflowId) || { id: workflowId };
const data = {
workflow,
workflows,
workflowId,
locationId: this.ghlClient.getConfig().locationId
};
const resourceHandler = this.resourceHandlers.get('ui://ghl/workflow-status');
return this.createAppResult(`Workflow: ${workflow?.name || workflowId}`, resourceHandler.uri, resourceHandler.mimeType, resourceHandler.getContent(), data);
}
/**
* View main dashboard
*/
async viewDashboard() {
const [contactsResponse, pipelinesResponse, calendarsResponse] = await Promise.all([
this.ghlClient.searchContacts({ locationId: this.ghlClient.getConfig().locationId, limit: 10 }),
this.ghlClient.getPipelines(),
this.ghlClient.getCalendars()
]);
const data = {
recentContacts: contactsResponse.data?.contacts || [],
pipelines: pipelinesResponse.data?.pipelines || [],
calendars: calendarsResponse.data?.calendars || [],
locationId: this.ghlClient.getConfig().locationId
};
const resourceHandler = this.resourceHandlers.get('ui://ghl/mcp-app');
return this.createAppResult('GHL Dashboard Overview', resourceHandler.uri, resourceHandler.mimeType, resourceHandler.getContent(), data);
}
/**
* View invoice
*/
async viewInvoice(invoiceId) {
const response = await this.ghlClient.getInvoice(invoiceId, {
altId: this.ghlClient.getConfig().locationId,
altType: 'location'
});
if (!response.success) {
throw new Error(response.error?.message || 'Failed to get invoice');
}
const invoice = response.data;
const resourceHandler = this.resourceHandlers.get('ui://ghl/invoice-preview');
return this.createAppResult(`Invoice #${invoice?.invoiceNumber || invoiceId} - ${invoice?.status || 'Unknown status'}`, resourceHandler.uri, resourceHandler.mimeType, resourceHandler.getContent(), invoice);
}
/**
* Update opportunity (action tool for UI)
*/
async updateOpportunity(args) {
const { opportunityId, ...updates } = args;
// Build the update payload
const updatePayload = {};
if (updates.pipelineStageId)
updatePayload.pipelineStageId = updates.pipelineStageId;
if (updates.name)
updatePayload.name = updates.name;
if (updates.monetaryValue !== undefined)
updatePayload.monetaryValue = updates.monetaryValue;
if (updates.status)
updatePayload.status = updates.status;
process.stderr.write(`[MCP Apps] Updating opportunity ${opportunityId}: ${JSON.stringify(updatePayload)}\n`);
const response = await this.ghlClient.updateOpportunity(opportunityId, updatePayload);
if (!response.success) {
throw new Error(response.error?.message || 'Failed to update opportunity');
}
const opportunity = response.data;
return {
content: [{ type: 'text', text: `Updated opportunity: ${opportunity?.name || opportunityId}` }],
structuredContent: {
success: true,
opportunity: {
id: opportunity?.id,
name: opportunity?.name,
pipelineStageId: opportunity?.pipelineStageId,
monetaryValue: opportunity?.monetaryValue,
status: opportunity?.status
}
}
};
}
/**
* Create app tool result with structuredContent
*/
createAppResult(textSummary, resourceUri, mimeType, htmlContent, data) {
// structuredContent is the data object that gets passed to ontoolresult
// The UI accesses it via result.structuredContent
return {
content: [{ type: 'text', text: textSummary }],
structuredContent: data
};
}
/**
* Inject data into HTML as a script tag
*/
injectDataIntoHTML(html, data) {
const dataScript = `<script>window.__MCP_APP_DATA__ = ${JSON.stringify(data)};</script>`;
// Insert before </head> or at the beginning of <body>
if (html.includes('</head>')) {
return html.replace('</head>', `${dataScript}</head>`);
}
else if (html.includes('<body>')) {
return html.replace('<body>', `<body>${dataScript}`);
}
else {
return dataScript + html;
}
}
/**
* Get resource handler by URI
*/
getResourceHandler(uri) {
return this.resourceHandlers.get(uri);
}
/**
* Get all registered resource URIs
*/
getResourceURIs() {
return Array.from(this.resourceHandlers.keys());
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,139 +0,0 @@
/**
* GoHighLevel MCP Apps Server (Slimmed Down)
* Only includes MCP Apps - no other tools
*/
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, McpError } from '@modelcontextprotocol/sdk/types.js';
import * as dotenv from 'dotenv';
import { GHLApiClient } from './clients/ghl-api-client.js';
import { MCPAppsManager } from './apps/index.js';
// Load environment variables
dotenv.config();
/**
* MCP Apps Only Server
*/
class GHLMCPAppsServer {
server;
ghlClient;
mcpAppsManager;
constructor() {
// Initialize MCP server with capabilities
this.server = new Server({
name: 'ghl-mcp-apps-only',
version: '1.0.0',
}, {
capabilities: {
tools: {},
resources: {},
},
});
// Initialize GHL API client
this.ghlClient = this.initializeGHLClient();
// Initialize MCP Apps Manager
this.mcpAppsManager = new MCPAppsManager(this.ghlClient);
this.setupHandlers();
this.setupErrorHandling();
}
/**
* Initialize the GHL API client with config from environment
*/
initializeGHLClient() {
const config = {
accessToken: process.env.GHL_API_KEY || '',
locationId: process.env.GHL_LOCATION_ID || '',
baseUrl: process.env.GHL_BASE_URL || 'https://services.leadconnectorhq.com',
version: '2021-07-28'
};
if (!config.accessToken) {
process.stderr.write('Warning: GHL_API_KEY not set in environment\n');
}
if (!config.locationId) {
process.stderr.write('Warning: GHL_LOCATION_ID not set in environment\n');
}
return new GHLApiClient(config);
}
/**
* Setup request handlers
*/
setupHandlers() {
// List tools - only MCP App tools
this.server.setRequestHandler(ListToolsRequestSchema, async () => {
const appTools = this.mcpAppsManager.getToolDefinitions();
process.stderr.write(`[MCP Apps Only] Listing ${appTools.length} app tools\n`);
return { tools: appTools };
});
// List resources - MCP App UI resources
this.server.setRequestHandler(ListResourcesRequestSchema, async () => {
const resourceUris = this.mcpAppsManager.getResourceURIs();
const resources = resourceUris.map(uri => {
const handler = this.mcpAppsManager.getResourceHandler(uri);
return {
uri: uri,
name: uri,
mimeType: handler?.mimeType || 'text/html;profile=mcp-app'
};
});
process.stderr.write(`[MCP Apps Only] Listing ${resources.length} UI resources\n`);
return { resources };
});
// Read resource - serve UI HTML
this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
const uri = request.params.uri;
process.stderr.write(`[MCP Apps Only] Reading resource: ${uri}\n`);
const handler = this.mcpAppsManager.getResourceHandler(uri);
if (!handler) {
throw new McpError(ErrorCode.InvalidRequest, `Resource not found: ${uri}`);
}
return {
contents: [{
uri: uri,
mimeType: handler.mimeType,
text: handler.getContent()
}]
};
});
// Call tool - execute MCP App tools
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
process.stderr.write(`[MCP Apps Only] Calling tool: ${name}\n`);
if (!this.mcpAppsManager.isAppTool(name)) {
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
}
try {
const result = await this.mcpAppsManager.executeTool(name, args || {});
return result;
}
catch (error) {
process.stderr.write(`[MCP Apps Only] Tool error: ${error.message}\n`);
throw new McpError(ErrorCode.InternalError, error.message);
}
});
}
/**
* Setup error handling
*/
setupErrorHandling() {
this.server.onerror = (error) => {
process.stderr.write(`[MCP Apps Only] Server error: ${error}\n`);
};
process.on('SIGINT', async () => {
await this.server.close();
process.exit(0);
});
}
/**
* Start the server
*/
async run() {
const transport = new StdioServerTransport();
await this.server.connect(transport);
process.stderr.write('[MCP Apps Only] Server started - Apps only mode\n');
}
}
// Start server
const server = new GHLMCPAppsServer();
server.run().catch((error) => {
process.stderr.write(`Failed to start server: ${error}\n`);
process.exit(1);
});

View File

@ -1,5 +0,0 @@
/**
* TypeScript interfaces for GoHighLevel API integration
* Based on official OpenAPI specifications v2021-07-28 (Contacts) and v2021-04-15 (Conversations)
*/
export {};

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
import{j as e,A as h,g as n,f as d,R as m,a as g}from"./styles-CphAgR3l.js";function o({contact:s}){const a=s.name||`${s.firstName||""} ${s.lastName||""}`.trim()||"Unknown Contact",i=n(s.firstName,s.lastName),r=()=>{const l=[s.address1,s.city,s.state,s.postalCode,s.country].filter(Boolean);return l.length>0?l.join(", "):null};return e.jsx("div",{className:"ghl-app",children:e.jsxs("div",{className:"ghl-card",children:[e.jsx("div",{className:"ghl-card-header",children:e.jsxs("div",{className:"ghl-flex ghl-items-center ghl-gap-4",children:[e.jsx("div",{className:"ghl-avatar ghl-avatar-lg",style:{background:x(a)},children:i}),e.jsxs("div",{children:[e.jsx("h2",{style:{fontSize:20,fontWeight:600,marginBottom:4},children:a}),s.companyName&&e.jsx("p",{className:"ghl-text-secondary",children:s.companyName})]})]})}),e.jsxs("div",{className:"ghl-card-body",children:[e.jsxs("div",{className:"ghl-grid ghl-grid-2",style:{gap:16},children:[s.email&&e.jsxs("div",{children:[e.jsx("label",{className:"ghl-text-sm ghl-text-muted",style:{display:"block",marginBottom:4},children:"Email"}),e.jsx("a",{href:`mailto:${s.email}`,style:{color:"var(--ghl-primary)",textDecoration:"none"},children:s.email})]}),s.phone&&e.jsxs("div",{children:[e.jsx("label",{className:"ghl-text-sm ghl-text-muted",style:{display:"block",marginBottom:4},children:"Phone"}),e.jsx("a",{href:`tel:${s.phone}`,style:{color:"var(--ghl-primary)",textDecoration:"none"},children:s.phone})]}),s.website&&e.jsxs("div",{children:[e.jsx("label",{className:"ghl-text-sm ghl-text-muted",style:{display:"block",marginBottom:4},children:"Website"}),e.jsx("a",{href:s.website,target:"_blank",rel:"noopener noreferrer",style:{color:"var(--ghl-primary)",textDecoration:"none"},children:s.website})]}),s.source&&e.jsxs("div",{children:[e.jsx("label",{className:"ghl-text-sm ghl-text-muted",style:{display:"block",marginBottom:4},children:"Source"}),e.jsx("span",{children:s.source})]}),r()&&e.jsxs("div",{style:{gridColumn:"span 2"},children:[e.jsx("label",{className:"ghl-text-sm ghl-text-muted",style:{display:"block",marginBottom:4},children:"Address"}),e.jsx("span",{children:r()})]})]}),s.tags&&s.tags.length>0&&e.jsxs("div",{style:{marginTop:16},children:[e.jsx("label",{className:"ghl-text-sm ghl-text-muted",style:{display:"block",marginBottom:8},children:"Tags"}),e.jsx("div",{className:"ghl-flex ghl-gap-2",style:{flexWrap:"wrap"},children:s.tags.map((l,t)=>e.jsx("span",{className:"ghl-badge ghl-badge-primary",children:l},t))})]}),s.customFields&&s.customFields.length>0&&e.jsxs("div",{style:{marginTop:16},children:[e.jsx("label",{className:"ghl-text-sm ghl-text-muted",style:{display:"block",marginBottom:8},children:"Custom Fields"}),e.jsx("div",{className:"ghl-grid ghl-grid-2",style:{gap:12},children:s.customFields.slice(0,6).map(l=>e.jsxs("div",{children:[e.jsxs("span",{className:"ghl-text-sm ghl-text-muted",children:[l.key,": "]}),e.jsx("span",{children:String(l.value)})]},l.id))})]})]}),e.jsx("div",{className:"ghl-card-footer",children:e.jsxs("div",{className:"ghl-flex ghl-justify-between ghl-text-sm ghl-text-muted",children:[e.jsxs("span",{children:["Added: ",d(s.dateAdded)]}),e.jsxs("span",{children:["Updated: ",d(s.dateUpdated)]})]})})]})})}function x(s){let a=0;for(let r=0;r<s.length;r++)a=s.charCodeAt(r)+((a<<5)-a);const i=["#4f46e5","#7c3aed","#2563eb","#0891b2","#059669","#d97706","#dc2626","#db2777","#9333ea","#0d9488"];return i[Math.abs(a)%i.length]}function c(){return e.jsx(h,{children:s=>e.jsx(o,{contact:s})})}m.createRoot(document.getElementById("root")).render(g.createElement(c));

View File

@ -1 +0,0 @@
import{j as e,A as k,r as g,g as w,f as A,R as $,a as z}from"./styles-CphAgR3l.js";function D({data:a}){const[l,o]=g.useState("name"),[n,y]=g.useState("asc"),[c,b]=g.useState(1),[d,x]=g.useState(new Set),j=10,p=a.contacts||[],v=[...p].sort((s,t)=>{let i,r;switch(l){case"name":i=s.name||`${s.firstName||""} ${s.lastName||""}`.trim(),r=t.name||`${t.firstName||""} ${t.lastName||""}`.trim();break;case"email":i=s.email||"",r=t.email||"";break;case"dateAdded":i=s.dateAdded||"",r=t.dateAdded||"";break;default:return 0}const m=i.localeCompare(r);return n==="asc"?m:-m}),u=Math.ceil(v.length/j),h=v.slice((c-1)*j,c*j),N=s=>{l===s?y(n==="asc"?"desc":"asc"):(o(s),y("asc"))},C=s=>{const t=new Set(d);t.has(s)?t.delete(s):t.add(s),x(t)},S=()=>{d.size===h.length?x(new Set):x(new Set(h.map(s=>s.id)))},f=({field:s})=>e.jsx("span",{style:{marginLeft:4,opacity:l===s?1:.3},children:l===s&&n==="desc"?"↓":"↑"});return p.length===0?e.jsx("div",{className:"ghl-app",children:e.jsxs("div",{className:"ghl-empty",children:[e.jsx("div",{className:"ghl-empty-icon",children:"👥"}),e.jsx("p",{children:"No contacts found"})]})}):e.jsx("div",{className:"ghl-app",children:e.jsxs("div",{className:"ghl-card",children:[e.jsx("div",{className:"ghl-card-header",children:e.jsxs("div",{className:"ghl-flex ghl-justify-between ghl-items-center",children:[e.jsxs("div",{children:[e.jsx("h2",{style:{fontSize:18,fontWeight:600},children:"Contacts"}),e.jsxs("p",{className:"ghl-text-sm ghl-text-muted",children:[a.total||p.length," total contacts"]})]}),d.size>0&&e.jsx("div",{className:"ghl-flex ghl-gap-2",children:e.jsxs("span",{className:"ghl-text-sm ghl-text-secondary",children:[d.size," selected"]})})]})}),e.jsx("div",{style:{overflowX:"auto"},children:e.jsxs("table",{className:"ghl-table",children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{style:{width:40},children:e.jsx("input",{type:"checkbox",checked:d.size===h.length&&h.length>0,onChange:S})}),e.jsxs("th",{onClick:()=>N("name"),style:{cursor:"pointer"},children:["Contact ",e.jsx(f,{field:"name"})]}),e.jsxs("th",{onClick:()=>N("email"),style:{cursor:"pointer"},children:["Email ",e.jsx(f,{field:"email"})]}),e.jsx("th",{children:"Phone"}),e.jsx("th",{children:"Tags"}),e.jsxs("th",{onClick:()=>N("dateAdded"),style:{cursor:"pointer"},children:["Added ",e.jsx(f,{field:"dateAdded"})]})]})}),e.jsx("tbody",{children:h.map(s=>{const t=s.name||`${s.firstName||""} ${s.lastName||""}`.trim()||"Unknown",i=w(s.firstName,s.lastName);return e.jsxs("tr",{children:[e.jsx("td",{children:e.jsx("input",{type:"checkbox",checked:d.has(s.id),onChange:()=>C(s.id)})}),e.jsx("td",{children:e.jsxs("div",{className:"ghl-flex ghl-items-center ghl-gap-2",children:[e.jsx("div",{className:"ghl-avatar ghl-avatar-sm",style:{background:P(t)},children:i}),e.jsxs("div",{children:[e.jsx("div",{className:"ghl-font-medium",children:t}),s.companyName&&e.jsx("div",{className:"ghl-text-sm ghl-text-muted",children:s.companyName})]})]})}),e.jsx("td",{children:s.email?e.jsx("a",{href:`mailto:${s.email}`,style:{color:"var(--ghl-primary)",textDecoration:"none"},children:s.email}):e.jsx("span",{className:"ghl-text-muted",children:"-"})}),e.jsx("td",{children:s.phone?e.jsx("a",{href:`tel:${s.phone}`,style:{color:"var(--ghl-primary)",textDecoration:"none"},children:s.phone}):e.jsx("span",{className:"ghl-text-muted",children:"-"})}),e.jsx("td",{children:e.jsxs("div",{className:"ghl-flex ghl-gap-2",style:{flexWrap:"wrap",maxWidth:200},children:[(s.tags||[]).slice(0,3).map((r,m)=>e.jsx("span",{className:"ghl-badge ghl-badge-primary",children:r},m)),(s.tags||[]).length>3&&e.jsxs("span",{className:"ghl-badge",children:["+",s.tags.length-3]})]})}),e.jsx("td",{className:"ghl-text-muted",children:A(s.dateAdded)})]},s.id)})})]})}),u>1&&e.jsx("div",{className:"ghl-card-footer",children:e.jsxs("div",{className:"ghl-flex ghl-justify-between ghl-items-center",children:[e.jsxs("span",{className:"ghl-text-sm ghl-text-muted",children:["Page ",c," of ",u]}),e.jsxs("div",{className:"ghl-flex ghl-gap-2",children:[e.jsx("button",{className:"ghl-btn ghl-btn-secondary ghl-btn-sm",disabled:c===1,onClick:()=>b(s=>s-1),children:"Previous"}),e.jsx("button",{className:"ghl-btn ghl-btn-secondary ghl-btn-sm",disabled:c===u,onClick:()=>b(s=>s+1),children:"Next"})]})]})})]})})}function P(a){let l=0;for(let n=0;n<a.length;n++)l=a.charCodeAt(n)+((l<<5)-l);const o=["#4f46e5","#7c3aed","#2563eb","#0891b2","#059669","#d97706","#dc2626","#db2777","#9333ea","#0d9488"];return o[Math.abs(l)%o.length]}function E(){return e.jsx(k,{children:a=>e.jsx(D,{data:a})})}$.createRoot(document.getElementById("root")).render(z.createElement(E));

View File

@ -1 +0,0 @@
import{j as e,A as x,r as h,R as g,a as u}from"./styles-CphAgR3l.js";function m({data:t}){const{conversation:n,messages:c}=t,l=h.useRef(null),d=n.contactName||n.contact?.name||`${n.contact?.firstName||""} ${n.contact?.lastName||""}`.trim()||"Unknown Contact";h.useEffect(()=>{l.current?.scrollIntoView({behavior:"smooth"})},[]);const r=[...c||[]].sort((a,s)=>{const o=a.dateAdded?new Date(a.dateAdded).getTime():0,p=s.dateAdded?new Date(s.dateAdded).getTime():0;return o-p}),i=new Map;for(const a of r){const s=a.dateAdded?new Date(a.dateAdded).toLocaleDateString("en-US",{month:"short",day:"numeric",year:"numeric"}):"Unknown Date";i.has(s)||i.set(s,[]),i.get(s).push(a)}return e.jsx("div",{className:"ghl-app",style:{height:"100%",display:"flex",flexDirection:"column"},children:e.jsxs("div",{className:"ghl-card",style:{flex:1,display:"flex",flexDirection:"column",maxHeight:600},children:[e.jsx("div",{className:"ghl-card-header",children:e.jsxs("div",{className:"ghl-flex ghl-items-center ghl-gap-4",children:[e.jsx("div",{style:{width:44,height:44,borderRadius:"50%",background:"var(--ghl-primary)",display:"flex",alignItems:"center",justifyContent:"center",color:"white",fontWeight:600},children:d.charAt(0).toUpperCase()}),e.jsxs("div",{children:[e.jsx("h2",{style:{fontSize:16,fontWeight:600,marginBottom:2},children:d}),e.jsxs("div",{className:"ghl-flex ghl-items-center ghl-gap-2",children:[n.contact?.phone&&e.jsx("span",{className:"ghl-text-sm ghl-text-muted",children:n.contact.phone}),n.type&&e.jsx("span",{className:"ghl-badge",children:n.type})]})]})]})}),e.jsx("div",{style:{flex:1,overflowY:"auto",padding:16,background:"var(--ghl-bg-secondary)"},children:r.length===0?e.jsxs("div",{className:"ghl-empty",children:[e.jsx("div",{className:"ghl-empty-icon",children:"💬"}),e.jsx("p",{children:"No messages in this conversation"})]}):e.jsxs(e.Fragment,{children:[Array.from(i.entries()).map(([a,s])=>e.jsxs("div",{children:[e.jsxs("div",{style:{textAlign:"center",margin:"16px 0",position:"relative"},children:[e.jsx("span",{style:{background:"var(--ghl-bg-secondary)",padding:"4px 12px",fontSize:12,color:"var(--ghl-text-muted)",position:"relative",zIndex:1},children:a}),e.jsx("div",{style:{position:"absolute",top:"50%",left:0,right:0,height:1,background:"var(--ghl-border)",zIndex:0}})]}),s.map(o=>e.jsx(f,{message:o},o.id))]},a)),e.jsx("div",{ref:l})]})}),e.jsx("div",{className:"ghl-card-footer",children:e.jsxs("div",{className:"ghl-flex ghl-items-center ghl-gap-2",children:[e.jsxs("div",{style:{flex:1,padding:"10px 14px",background:"var(--ghl-bg)",border:"1px solid var(--ghl-border)",borderRadius:20,fontSize:14,color:"var(--ghl-text-muted)"},children:["Reply to ",d,"..."]}),e.jsx("button",{className:"ghl-btn ghl-btn-primary",style:{borderRadius:20,padding:"10px 20px"},children:"Send"})]})})]})})}function f({message:t}){const n=t.direction==="outbound",c=r=>r?new Date(r).toLocaleTimeString("en-US",{hour:"numeric",minute:"2-digit"}):"",l=r=>{switch(r){case"delivered":return"✓✓";case"sent":return"✓";case"read":return"✓✓";case"failed":return"✕";default:return""}},d=r=>{switch(r){case"SMS":return"📱";case"Email":return"📧";case"WhatsApp":return"💬";case"FB":return"👤";case"GMB":return"🏢";case"Call":return"📞";default:return""}};return e.jsx("div",{style:{display:"flex",justifyContent:n?"flex-end":"flex-start",marginBottom:8},children:e.jsxs("div",{style:{maxWidth:"75%",background:n?"var(--ghl-primary)":"var(--ghl-bg)",color:n?"white":"var(--ghl-text)",padding:"10px 14px",borderRadius:n?"18px 18px 4px 18px":"18px 18px 18px 4px",boxShadow:"var(--ghl-shadow)"},children:[t.type&&e.jsxs("div",{style:{fontSize:10,marginBottom:4,opacity:.7},children:[d(t.type)," ",t.type]}),e.jsx("div",{style:{wordBreak:"break-word",whiteSpace:"pre-wrap"},children:t.body||e.jsx("span",{style:{opacity:.6,fontStyle:"italic"},children:"[No content]"})}),t.attachments&&t.attachments.length>0&&e.jsx("div",{style:{marginTop:8},children:t.attachments.map((r,i)=>e.jsx("div",{style:{padding:"6px 10px",background:n?"rgba(255,255,255,0.1)":"var(--ghl-bg-secondary)",borderRadius:8,fontSize:12,marginTop:4},children:"📎 Attachment"},i))}),e.jsxs("div",{style:{display:"flex",alignItems:"center",justifyContent:"flex-end",gap:6,marginTop:6,fontSize:11,opacity:.7},children:[e.jsx("span",{children:c(t.dateAdded)}),n&&t.status&&e.jsx("span",{style:{color:t.status==="failed"?"#ef4444":"inherit"},children:l(t.status)})]})]})})}function y(){return e.jsx(x,{children:t=>e.jsx(m,{data:t})})}g.createRoot(document.getElementById("root")).render(u.createElement(y));

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
import{j as e,A as u,b as g,f as p,R as v,a as j}from"./styles-CphAgR3l.js";function f({data:s}){const{pipeline:n,opportunities:l,stages:h}=s,c=[...h||[]].sort((t,a)=>(t.position||0)-(a.position||0)),r=new Map;for(const t of c)r.set(t.id,[]);for(const t of l||[]){const a=r.get(t.pipelineStageId||"");a&&a.push(t)}const m=t=>{const a=r.get(t)||[],i=a.length,d=a.reduce((o,x)=>o+(x.monetaryValue||0),0);return{count:i,value:d}};return n?e.jsxs("div",{className:"ghl-app",children:[e.jsxs("div",{style:{marginBottom:16},children:[e.jsx("h2",{style:{fontSize:20,fontWeight:600,marginBottom:4},children:n.name}),e.jsxs("p",{className:"ghl-text-sm ghl-text-muted",children:[l?.length||0," opportunities | ",c.length," stages"]})]}),e.jsx("div",{style:{display:"flex",gap:16,overflowX:"auto",paddingBottom:16},children:c.map(t=>{const{count:a,value:i}=m(t.id),d=r.get(t.id)||[];return e.jsxs("div",{style:{flex:"0 0 280px",display:"flex",flexDirection:"column",maxHeight:500},children:[e.jsxs("div",{style:{padding:"12px 16px",background:"var(--ghl-bg-secondary)",borderRadius:"var(--ghl-radius) var(--ghl-radius) 0 0",border:"1px solid var(--ghl-border)",borderBottom:"none"},children:[e.jsxs("div",{className:"ghl-flex ghl-justify-between ghl-items-center",children:[e.jsx("span",{className:"ghl-font-semibold",children:t.name}),e.jsx("span",{className:"ghl-badge",children:a})]}),i>0&&e.jsx("div",{className:"ghl-text-sm ghl-text-muted",style:{marginTop:4},children:g(i)})]}),e.jsx("div",{style:{flex:1,padding:8,background:"var(--ghl-bg-tertiary)",borderRadius:"0 0 var(--ghl-radius) var(--ghl-radius)",border:"1px solid var(--ghl-border)",borderTop:"none",overflowY:"auto",minHeight:200},children:d.length===0?e.jsx("div",{style:{padding:20,textAlign:"center",color:"var(--ghl-text-muted)",fontSize:13},children:"No opportunities"}):e.jsx("div",{style:{display:"flex",flexDirection:"column",gap:8},children:d.map(o=>e.jsx(b,{opportunity:o},o.id))})})]},t.id)})}),e.jsx("div",{className:"ghl-card",style:{marginTop:16},children:e.jsx("div",{className:"ghl-card-body",children:e.jsxs("div",{className:"ghl-flex ghl-justify-between",children:[e.jsxs("div",{children:[e.jsx("span",{className:"ghl-text-muted",children:"Total Opportunities"}),e.jsx("div",{className:"ghl-font-semibold",style:{fontSize:20},children:l?.length||0})]}),e.jsxs("div",{style:{textAlign:"right"},children:[e.jsx("span",{className:"ghl-text-muted",children:"Total Pipeline Value"}),e.jsx("div",{className:"ghl-font-semibold",style:{fontSize:20,color:"var(--ghl-success)"},children:g(l?.reduce((t,a)=>t+(a.monetaryValue||0),0)||0)})]})]})})})]}):e.jsx("div",{className:"ghl-app",children:e.jsxs("div",{className:"ghl-empty",children:[e.jsx("div",{className:"ghl-empty-icon",children:"📊"}),e.jsx("p",{children:"Pipeline not found"})]})})}function b({opportunity:s}){const n=s.contact?.name||`${s.contact?.firstName||""} ${s.contact?.lastName||""}`.trim()||"Unknown Contact",l={open:"var(--ghl-primary)",won:"var(--ghl-success)",lost:"var(--ghl-danger)",abandoned:"var(--ghl-warning)"};return e.jsxs("div",{style:{background:"var(--ghl-bg)",borderRadius:"var(--ghl-radius)",padding:12,boxShadow:"var(--ghl-shadow)",border:"1px solid var(--ghl-border)"},children:[e.jsx("div",{className:"ghl-font-medium",style:{marginBottom:8},children:s.name}),e.jsxs("div",{className:"ghl-flex ghl-items-center ghl-gap-2",style:{marginBottom:8},children:[e.jsx("span",{style:{width:24,height:24,borderRadius:"50%",background:"var(--ghl-bg-tertiary)",display:"flex",alignItems:"center",justifyContent:"center",fontSize:10},children:"👤"}),e.jsx("span",{className:"ghl-text-sm",children:n})]}),e.jsxs("div",{className:"ghl-flex ghl-justify-between ghl-items-center",children:[s.monetaryValue?e.jsx("span",{className:"ghl-font-semibold",style:{color:"var(--ghl-success)"},children:g(s.monetaryValue)}):e.jsx("span",{className:"ghl-text-muted ghl-text-sm",children:"No value"}),s.status&&e.jsx("span",{className:"ghl-badge",style:{background:`${l[s.status]||"var(--ghl-bg-tertiary)"}20`,color:l[s.status]||"var(--ghl-text-muted)"},children:s.status})]}),s.dateAdded&&e.jsxs("div",{className:"ghl-text-sm ghl-text-muted",style:{marginTop:8},children:["Added ",p(s.dateAdded)]})]})}function y(){return e.jsx(u,{children:s=>e.jsx(f,{data:s})})}v.createRoot(document.getElementById("root")).render(j.createElement(y));

View File

@ -1 +0,0 @@
:root{--ghl-primary: #4f46e5;--ghl-primary-hover: #4338ca;--ghl-success: #22c55e;--ghl-warning: #f59e0b;--ghl-danger: #ef4444;--ghl-info: #3b82f6;--ghl-bg: #ffffff;--ghl-bg-secondary: #f9fafb;--ghl-bg-tertiary: #f3f4f6;--ghl-text: #111827;--ghl-text-secondary: #6b7280;--ghl-text-muted: #9ca3af;--ghl-border: #e5e7eb;--ghl-border-dark: #d1d5db;--ghl-shadow: 0 1px 3px rgba(0, 0, 0, .1);--ghl-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, .1);--ghl-radius: 8px;--ghl-radius-lg: 12px}*{box-sizing:border-box;margin:0;padding:0}body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;font-size:14px;line-height:1.5;color:var(--ghl-text);background:var(--ghl-bg)}.ghl-app{padding:16px;max-width:100%;overflow-x:hidden}.ghl-card{background:var(--ghl-bg);border:1px solid var(--ghl-border);border-radius:var(--ghl-radius-lg);box-shadow:var(--ghl-shadow);overflow:hidden}.ghl-card-header{padding:16px;border-bottom:1px solid var(--ghl-border);background:var(--ghl-bg-secondary)}.ghl-card-body{padding:16px}.ghl-card-footer{padding:12px 16px;border-top:1px solid var(--ghl-border);background:var(--ghl-bg-secondary)}.ghl-btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;padding:8px 16px;font-size:14px;font-weight:500;border-radius:var(--ghl-radius);border:1px solid transparent;cursor:pointer;transition:all .15s ease}.ghl-btn-primary{background:var(--ghl-primary);color:#fff}.ghl-btn-primary:hover{background:var(--ghl-primary-hover)}.ghl-btn-secondary{background:var(--ghl-bg);color:var(--ghl-text);border-color:var(--ghl-border)}.ghl-btn-secondary:hover{background:var(--ghl-bg-secondary)}.ghl-btn-sm{padding:4px 10px;font-size:12px}.ghl-badge{display:inline-flex;align-items:center;padding:2px 8px;font-size:12px;font-weight:500;border-radius:9999px;background:var(--ghl-bg-tertiary);color:var(--ghl-text-secondary)}.ghl-badge-primary{background:#4f46e51a;color:var(--ghl-primary)}.ghl-badge-success{background:#22c55e1a;color:var(--ghl-success)}.ghl-badge-warning{background:#f59e0b1a;color:var(--ghl-warning)}.ghl-badge-danger{background:#ef44441a;color:var(--ghl-danger)}.ghl-avatar{display:inline-flex;align-items:center;justify-content:center;width:40px;height:40px;border-radius:50%;background:var(--ghl-primary);color:#fff;font-weight:600;font-size:16px}.ghl-avatar-lg{width:64px;height:64px;font-size:24px}.ghl-avatar-sm{width:32px;height:32px;font-size:12px}.ghl-table{width:100%;border-collapse:collapse}.ghl-table th,.ghl-table td{padding:12px;text-align:left;border-bottom:1px solid var(--ghl-border)}.ghl-table th{font-weight:600;background:var(--ghl-bg-secondary);color:var(--ghl-text-secondary);font-size:12px;text-transform:uppercase;letter-spacing:.05em}.ghl-table tr:hover{background:var(--ghl-bg-secondary)}.ghl-grid{display:grid;gap:16px}.ghl-grid-2{grid-template-columns:repeat(2,1fr)}.ghl-grid-3{grid-template-columns:repeat(3,1fr)}.ghl-grid-4{grid-template-columns:repeat(4,1fr)}.ghl-flex{display:flex}.ghl-flex-col{flex-direction:column}.ghl-items-center{align-items:center}.ghl-justify-between{justify-content:space-between}.ghl-gap-2{gap:8px}.ghl-gap-4{gap:16px}.ghl-text-sm{font-size:12px}.ghl-text-lg{font-size:18px}.ghl-text-muted{color:var(--ghl-text-muted)}.ghl-text-secondary{color:var(--ghl-text-secondary)}.ghl-font-medium{font-weight:500}.ghl-font-semibold{font-weight:600}.ghl-status-paid{color:var(--ghl-success)}.ghl-status-pending{color:var(--ghl-warning)}.ghl-status-overdue{color:var(--ghl-danger)}.ghl-status-draft{color:var(--ghl-text-muted)}.ghl-loading{display:flex;align-items:center;justify-content:center;padding:40px;color:var(--ghl-text-muted)}.ghl-spinner{width:24px;height:24px;border:2px solid var(--ghl-border);border-top-color:var(--ghl-primary);border-radius:50%;animation:spin .8s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.ghl-empty{text-align:center;padding:40px;color:var(--ghl-text-muted)}.ghl-empty-icon{font-size:48px;margin-bottom:16px;opacity:.5}

File diff suppressed because one or more lines are too long

View File

@ -1,14 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Calendar Widget - GHL MCP</title>
<script type="module" crossorigin src="/assets/calendar-widget-CUbShwNj.js"></script>
<link rel="modulepreload" crossorigin href="/assets/styles-CphAgR3l.js">
<link rel="stylesheet" crossorigin href="/assets/style-BaFxk78P.css">
</head>
<body>
<div id="root"></div>
</body>
</html>

View File

@ -1,14 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Contact Card - GHL MCP</title>
<script type="module" crossorigin src="/assets/contact-card-CFJe96SR.js"></script>
<link rel="modulepreload" crossorigin href="/assets/styles-CphAgR3l.js">
<link rel="stylesheet" crossorigin href="/assets/style-BaFxk78P.css">
</head>
<body>
<div id="root"></div>
</body>
</html>

View File

@ -1,14 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Contact Grid - GHL MCP</title>
<script type="module" crossorigin src="/assets/contact-grid-C_Uxn-WJ.js"></script>
<link rel="modulepreload" crossorigin href="/assets/styles-CphAgR3l.js">
<link rel="stylesheet" crossorigin href="/assets/style-BaFxk78P.css">
</head>
<body>
<div id="root"></div>
</body>
</html>

View File

@ -1,14 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Conversation Thread - GHL MCP</title>
<script type="module" crossorigin src="/assets/conversation-thread-DBGTC45D.js"></script>
<link rel="modulepreload" crossorigin href="/assets/styles-CphAgR3l.js">
<link rel="stylesheet" crossorigin href="/assets/style-BaFxk78P.css">
</head>
<body>
<div id="root"></div>
</body>
</html>

View File

@ -1,14 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Invoice Preview - GHL MCP</title>
<script type="module" crossorigin src="/assets/invoice-preview-DphRvjHB.js"></script>
<link rel="modulepreload" crossorigin href="/assets/styles-CphAgR3l.js">
<link rel="stylesheet" crossorigin href="/assets/style-BaFxk78P.css">
</head>
<body>
<div id="root"></div>
</body>
</html>

View File

@ -1,14 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Pipeline Kanban - GHL MCP</title>
<script type="module" crossorigin src="/assets/opportunity-kanban-CSzRcmdW.js"></script>
<link rel="modulepreload" crossorigin href="/assets/styles-CphAgR3l.js">
<link rel="stylesheet" crossorigin href="/assets/style-BaFxk78P.css">
</head>
<body>
<div id="root"></div>
</body>
</html>

Some files were not shown because too many files have changed in this diff Show More