Files
hermes-mcp/product/app/index.html
Garfield 6604ab5d2b feat(connect): dedicated Claude.ai / ChatGPT browser connect picker
- Replace single 'Connect to Claude / ChatGPT' button with a modal picker
  offering Claude.ai web, Claude Desktop, Codex CLI, and ChatGPT/GPT Actions.
- Add /oauth/connect-claude-ai backend route that redirects to Anthropic's
  official https://claude.ai/api/mcp/auth_callback OAuth callback.
- Update MCP callback result page with browser-specific instructions for
  Claude.ai web, Claude Desktop, ChatGPT/GPT Actions, and Codex CLI.
- Deploy new app and hermes images to K8s.
2026-06-12 14:55:36 -04:00

369 lines
16 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SquareMCP — AI Social Gateway</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div id="app">
<!-- Password Reset Request View -->
<div id="reset-request-view" class="view hidden">
<div class="auth-card">
<div class="logo">
<div class="logo-mark">S</div>
<h1>Reset Password</h1>
<p>Enter your email to receive a reset link</p>
</div>
<form id="reset-request-form" class="auth-form">
<input type="email" name="email" placeholder="Email" aria-label="Email address" required>
<button type="submit" class="btn btn-primary">Send Reset Link</button>
<p class="error-msg" id="reset-request-error"></p>
<p class="success-msg" id="reset-request-success"></p>
<a href="#" id="back-to-login" style="color:#888;font-size:13px;text-align:center;display:block;margin-top:12px;">Back to login</a>
</form>
</div>
</div>
<!-- Password Reset Confirm View -->
<div id="reset-confirm-view" class="view hidden">
<div class="auth-card">
<div class="logo">
<div class="logo-mark">S</div>
<h1>New Password</h1>
<p>Enter your new password below</p>
</div>
<form id="reset-confirm-form" class="auth-form">
<input type="password" name="password" placeholder="New password (min 8 chars)" aria-label="New password" required minlength="8">
<button type="submit" class="btn btn-primary">Update Password</button>
<p class="error-msg" id="reset-confirm-error"></p>
<p class="success-msg" id="reset-confirm-success"></p>
</form>
</div>
</div>
<!-- Login View -->
<div id="login-view" class="view">
<div class="auth-card">
<div class="logo">
<div class="logo-mark">S</div>
<h1>SquareMCP</h1>
<p>AI Social Media Gateway</p>
</div>
<div class="tabs">
<button class="tab-btn active" data-tab="login">Sign In</button>
<button class="tab-btn" data-tab="signup">Create Account</button>
</div>
<form id="login-form" class="auth-form">
<input type="email" name="email" placeholder="Email" aria-label="Email address" required>
<input type="password" name="password" placeholder="Password" aria-label="Password" required minlength="8">
<button type="submit" class="btn btn-primary">Sign In</button>
<p class="error-msg" id="login-error"></p>
</form>
<form id="signup-form" class="auth-form hidden">
<input type="email" name="email" placeholder="Email" aria-label="Email address" required>
<input type="password" name="password" placeholder="Password (min 8 chars)" aria-label="Password" required minlength="8">
<button type="submit" class="btn btn-primary">Create Account</button>
<p class="error-msg" id="signup-error"></p>
</form>
</div>
</div>
<!-- Dashboard View -->
<div id="dashboard-view" class="view hidden">
<header class="app-header">
<div class="header-left">
<div class="logo-mark small">S</div>
<span class="app-title">SquareMCP</span>
</div>
<div class="header-right">
<nav class="header-nav">
<button class="nav-link active" data-view="platforms">Platforms</button>
<button class="nav-link" data-view="analytics">Analytics</button>
<button class="nav-link" data-view="invoices">Invoices</button>
<button class="nav-link" data-view="webhooks">Webhooks</button>
<button class="nav-link hidden" data-view="admin" id="admin-nav">Admin</button>
</nav>
<span id="user-email" class="user-email"></span>
<button id="logout-btn" class="btn btn-ghost">Logout</button>
</div>
</header>
<main class="dashboard">
<section class="welcome">
<h2>Connect your accounts</h2>
<p>Connect once. Then ask Claude or ChatGPT to post, search your notes, or send email — without touching any of these apps.</p>
<button id="connect-mcp-btn" class="btn btn-primary" style="margin-top:16px;" aria-label="Connect Claude.ai, ChatGPT, Claude Desktop, or Codex CLI" title="Connect Claude.ai, ChatGPT, Claude Desktop, or Codex CLI">Connect AI Client</button>
</section>
<section class="usage-bar" id="usage-bar">
<div class="usage-info">
<span class="plan-badge" id="plan-badge">Free</span>
<span class="usage-text" id="usage-text">0 / 100 calls this month</span>
</div>
<div class="usage-bar-track"><div class="usage-bar-fill" id="usage-bar-fill" style="width:0%"></div></div>
</section>
<section class="platform-grid">
<div class="onboarding-banner" id="onboarding-banner">
<strong>Start here</strong> — connect one of these four to get started with Claude and ChatGPT.
</div>
<!-- v1 platforms: Obsidian, Email, Facebook, Instagram -->
<div class="platform-card v1-platform" data-platform="obsidian">
<div class="platform-icon" style="background:#7c3aed;">&#x1F4D3;</div>
<div class="platform-info">
<h3>Obsidian</h3>
<p class="platform-desc">Search and edit your notes vault</p>
<span class="status-badge disconnected" id="status-obsidian">Not connected</span>
</div>
<button class="btn btn-connect" data-platform="obsidian" aria-label="Connect Obsidian" title="Connect Obsidian">Connect</button>
</div>
<div class="platform-card v1-platform" data-platform="email">
<div class="platform-icon" style="background:#ea4335;">&#x2709;&#xFE0F;</div>
<div class="platform-info">
<h3>Email</h3>
<p class="platform-desc">Gmail, Yahoo, and IMAP accounts</p>
<span class="status-badge disconnected" id="status-email">Not connected</span>
</div>
<button class="btn btn-connect" data-platform="email" aria-label="Connect Email" title="Connect Email">Connect</button>
</div>
<div class="platform-card v1-platform" data-platform="facebook">
<div class="platform-icon" style="background:#1877f2;">f</div>
<div class="platform-info">
<h3>Facebook</h3>
<p class="platform-desc">Post to pages and manage content</p>
<span class="status-badge disconnected" id="status-facebook">Not connected</span>
</div>
<button class="btn btn-connect" data-platform="facebook" aria-label="Connect Facebook" title="Connect Facebook">Connect</button>
</div>
<div class="platform-card v1-platform" data-platform="instagram">
<div class="platform-icon" style="background:linear-gradient(45deg,#f09433,#e6683c,#dc2743,#cc2366,#bc1888);">&#x1F4F7;</div>
<div class="platform-info">
<h3>Instagram</h3>
<p class="platform-desc">Publish reels and images</p>
<span class="status-badge disconnected" id="status-instagram">Not connected</span>
</div>
<button class="btn btn-connect" data-platform="instagram" aria-label="Connect Instagram" title="Connect Instagram">Connect</button>
</div>
<div class="platform-divider">
<span>More platforms</span>
</div>
<!-- Other platforms -->
<div class="platform-card" data-platform="linkedin">
<div class="platform-icon" style="background:#0a66c2;">in</div>
<div class="platform-info">
<h3>LinkedIn</h3>
<p class="platform-desc">Share posts, images, and videos</p>
<span class="status-badge disconnected" id="status-linkedin">Not connected</span>
</div>
<button class="btn btn-connect" data-platform="linkedin" aria-label="Connect LinkedIn" title="Connect LinkedIn">Connect</button>
</div>
<div class="platform-card" data-platform="twitter">
<div class="platform-icon" style="background:#000;">&#x1D54F;</div>
<div class="platform-info">
<h3>Twitter / X</h3>
<p class="platform-desc">Tweet with media support</p>
<span class="status-badge disconnected" id="status-twitter">Not connected</span>
</div>
<button class="btn btn-connect" data-platform="twitter" aria-label="Connect Twitter / X" title="Connect Twitter / X">Connect</button>
</div>
<div class="platform-card" data-platform="whatsapp">
<div class="platform-icon" style="background:#25d366;">&#x1F4AC;</div>
<div class="platform-info">
<h3>WhatsApp</h3>
<p class="platform-desc">Business messaging</p>
<span class="status-badge disconnected" id="status-whatsapp">Not connected</span>
</div>
<button class="btn btn-connect" data-platform="whatsapp" aria-label="Connect WhatsApp" title="Connect WhatsApp">Connect</button>
</div>
<div class="platform-card" data-platform="telegram">
<div class="platform-icon" style="background:#0088cc;">&#x2708;&#xFE0F;</div>
<div class="platform-info">
<h3>Telegram</h3>
<p class="platform-desc">Send messages via bot</p>
<span class="status-badge disconnected" id="status-telegram">Not connected</span>
</div>
<button class="btn btn-connect" data-platform="telegram" aria-label="Connect Telegram" title="Connect Telegram">Connect</button>
</div>
<div class="platform-card" data-platform="discord">
<div class="platform-icon" style="background:#5865f2;">&#x1F3AE;</div>
<div class="platform-info">
<h3>Discord</h3>
<p class="platform-desc">Send messages to channels</p>
<span class="status-badge disconnected" id="status-discord">Not connected</span>
</div>
<button class="btn btn-connect" data-platform="discord" aria-label="Connect Discord" title="Connect Discord">Connect</button>
</div>
<div class="platform-card" data-platform="slack">
<div class="platform-icon" style="background:#4a154b;">&#x1F4AC;</div>
<div class="platform-info">
<h3>Slack</h3>
<p class="platform-desc">Send messages to channels</p>
<span class="status-badge disconnected" id="status-slack">Not connected</span>
</div>
<button class="btn btn-connect" data-platform="slack" aria-label="Connect Slack" title="Connect Slack">Connect</button>
</div>
<div class="platform-card" data-platform="tiktok">
<div class="platform-icon" style="background:#000;">&#x1F3B5;</div>
<div class="platform-info">
<h3>TikTok</h3>
<p class="platform-desc">Publish videos and view analytics</p>
<span class="status-badge disconnected" id="status-tiktok">Not connected</span>
</div>
<button class="btn btn-connect" data-platform="tiktok" aria-label="Connect TikTok" title="Connect TikTok">Connect</button>
</div>
</section>
<section class="analytics-section hidden" id="analytics-section">
<h3 class="section-title">Analytics</h3>
<p class="section-subtitle">Tool calls this month, by platform and day</p>
<div class="charts-grid">
<div class="chart-card">
<h4>By platform</h4>
<div class="chart-container"><canvas id="platform-chart"></canvas></div>
</div>
<div class="chart-card">
<h4>Daily activity</h4>
<div class="chart-container"><canvas id="daily-chart"></canvas></div>
</div>
</div>
<div id="analytics-empty" class="analytics-empty hidden">
<p>No activity yet this month. Make your first tool call to see data here.</p>
</div>
</section>
<section class="invoices-section hidden" id="invoices-section">
<h3>Invoices</h3>
<div id="invoices-list" class="invoices-list"></div>
</section>
<section class="admin-section hidden" id="admin-section">
<h3>Admin Panel</h3>
<div id="admin-customers" class="admin-customers"></div>
</section>
<section class="webhooks-section hidden" id="webhooks-section">
<h3 class="section-title">Webhook</h3>
<p class="section-subtitle">Receive real-time events when messages arrive on your connected platforms.</p>
<div class="webhook-card" id="webhook-card">
<div id="webhook-status-row" class="webhook-status-row">
<span id="webhook-url-display" class="webhook-url-display">No webhook configured</span>
<button id="webhook-delete-btn" class="btn btn-ghost hidden" aria-label="Remove webhook URL" title="Remove webhook URL">Remove</button>
</div>
<form id="webhook-form" class="webhook-form">
<input type="url" id="webhook-url-input" placeholder="https://your-server.com/webhook" aria-label="Webhook URL" required>
<button type="submit" class="btn btn-primary">Save &amp; generate secret</button>
</form>
<div id="webhook-secret-box" class="webhook-secret-box hidden">
<p class="webhook-secret-label">Signing secret — copy now, not shown again:</p>
<div class="webhook-secret-value" id="webhook-secret-value"></div>
<button class="btn btn-ghost" onclick="copyWebhookSecret()">Copy secret</button>
</div>
<div class="webhook-instructions">
<p>Verify each request by computing <code>sha256=HMAC-SHA256(secret, rawBody)</code> and comparing to the <code>X-SquareMCP-Signature</code> header.</p>
</div>
</div>
</section>
</main>
</div>
<!-- Connection Modal -->
<div id="connect-modal" class="modal hidden">
<div class="modal-backdrop"></div>
<div class="modal-content">
<button class="modal-close" aria-label="Close" title="Close">&times;</button>
<div id="modal-body"></div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4/dist/chart.umd.min.js"></script>
<script src="app.js"></script>
<script src="https://hermes.squaremcp.com/chat-widget.js"></script>
<style>
.onboarding-banner {
grid-column: 1 / -1;
background: linear-gradient(135deg, #ede9fe 0%, #e0f2fe 100%);
border: 1px solid #c4b5fd;
border-radius: 10px;
padding: 14px 18px;
font-size: 0.9rem;
color: #4c1d95;
margin-bottom: 4px;
}
.platform-card.v1-platform {
border: 1.5px solid #c4b5fd;
}
.platform-divider {
grid-column: 1 / -1;
display: flex;
align-items: center;
gap: 12px;
margin: 8px 0 4px;
color: #94a3b8;
font-size: 0.8rem;
font-weight: 600;
letter-spacing: 0.05em;
text-transform: uppercase;
}
.platform-divider::before,
.platform-divider::after {
content: '';
flex: 1;
height: 1px;
background: #e2e8f0;
}
.status-badge.connected {
background: #dcfce7;
color: #166534;
border-color: #bbf7d0;
}
.status-badge.connected::before {
content: '✓ ';
}
</style>
<script>
/* After app.js loads, patch platform card connected states to show account name */
document.addEventListener('DOMContentLoaded', function() {
const origSetStatus = window.setPlatformStatus;
/* Override connect button handler to update badge with account name when connected */
document.querySelectorAll('.platform-card').forEach(function(card) {
const platform = card.dataset.platform;
const badge = card.querySelector('.status-badge');
const btn = card.querySelector('.btn-connect');
if (!badge || !btn) return;
/* app.js manages status via API; we observe class changes to update text */
const observer = new MutationObserver(function() {
if (badge.classList.contains('connected') && badge.textContent.trim() === 'Connected') {
/* badge was set to generic "Connected" — good enough for now */
}
});
observer.observe(badge, { attributes: true, childList: true, subtree: true });
});
/* Hide onboarding banner once any v1 platform is connected */
function checkOnboardingBanner() {
const banner = document.getElementById('onboarding-banner');
if (!banner) return;
const anyV1Connected = Array.from(document.querySelectorAll('.platform-card.v1-platform .status-badge'))
.some(function(b) { return b.classList.contains('connected'); });
if (anyV1Connected) banner.style.display = 'none';
}
document.querySelector('.platform-grid').addEventListener('DOMSubtreeModified', checkOnboardingBanner);
});
</script>
</body>
</html>