Skip to main content

hermes-marketing-dashboard

Open-source marketing operations control center for AI agent teams with CRM, outreach, content ops, and analytics powered by OpenClaw + SQLite

Zur Installation springen

Quellinformationen

Repository
reason-machines/marketing-skills
Letzte Quellaktivität
16. Mai 2026 um 20:48
Erkannte Sprache von SKILL.md
Englisch
Sterne
10
Forks
1

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
hermes-marketing-dashboard
description
Open-source marketing operations control center for AI agent teams with CRM, outreach, content ops, and analytics powered by OpenClaw + SQLite
triggers
["set up hermes marketing dashboard","configure openclaw marketing operations","build ai agent marketing control center","integrate crm with openclaw agents","create marketing automation dashboard","deploy hermes dashboard with sqlite","set up marketing ops for ai teams","configure hermes openclaw integration"]
# Hermes Marketing Dashboard > Skill by [ara.so](https://ara.so) — Marketing Skills collection. Hermes Dashboard is an open-source marketing operations control center designed for AI agent teams. It provides CRM, outreach sequencing, content operations, analytics, and automation workflows in a single Next.js application powered by OpenClaw integration and local SQLite storage. ## Installation ### Prerequisites - Node.js 18+ - pnpm (required) - install with `npm install -g pnpm` or `corepack enable` - OpenClaw CLI (optional but recommended for agent integration) ### Quick Start ```bash git clone https://github.com/builderz-labs/marketing-dashboard.git cd marketing-dashboard pnpm install pnpm env:bootstrap pnpm dev ``` The application will start at `http://localhost:3000`. ## Configuration ### Required Environment Variables Create a `.env.local` file: ```bash # Authentication (required) AUTH_USER=admin AUTH_PASS=your-secure-password-min-10-chars API_KEY=your-api-key-for-programmatic-access # Cookie security (false for HTTP local, true for HTTPS production) AUTH_COOKIE_SECURE=false # Database (auto-created in ./state) DATABASE_URL=./state/hermes.db ``` ### OpenClaw Integration For AI agent integration with OpenClaw: ```bash # OpenClaw home directory HERMES_OPENCLAW_HOME=/path/to/openclaw # Default instance name HERMES_DEFAULT_INSTANCE=main # Multi-instance support (optional JSON array) HERMES_OPENCLAW_INSTANCES='[{"name":"prod","path":"/openclaw/prod"},{"name":"dev","path":"/openclaw/dev"}]' ``` ### Security Configuration ```bash # Host access lock (default: local-only) HERMES_HOST_LOCK=local # or 'off' or 'host1,host2' # Writeback protection (keep false unless explicitly needed) HERMES_ALLOW_POLICY_WRITE=false HERMES_ALLOW_CRON_WRITE=false HERMES_ALLOW_WORKSPACE_WRITE=false ``` ### Optional Analytics Integration ```bash # Plausible Analytics NEXT_PUBLIC_PLAUSIBLE_DOMAIN=yourdomain.com PLAUSIBLE_API_KEY=your-plausible-api-key # Google Analytics 4 NEXT_PUBLIC_GA4_ID=G-XXXXXXXXXX ``` ### 1Password Runtime Overlay (Optional) ```bash HERMES_1PASSWORD_MODE=auto # off|auto|required HERMES_OP_ENV_FILE=/etc/hermes-dashboard/hermes-dashboard.op.env ``` ## Key Commands ### Development ```bash # Start development server pnpm dev # Build for production pnpm build # Start production server pnpm start # Type checking pnpm typecheck # Linting pnpm lint # Run tests pnpm test # Run end-to-end tests pnpm test:e2e ``` ### Database Management ```bash # Bootstrap environment and database pnpm env:bootstrap # Reset database (warning: destructive) pnpm db:reset ``` ### Template Export ```bash # Audit template for sensitive data ./scripts/template-audit.sh # Export clean template ./scripts/template-export.sh /path/to/output ``` ## Core API Patterns ### CRM Lead Management ```typescript // app/api/crm/leads/route.ts import { NextRequest, NextResponse } from 'next/server'; import { getDatabase } from '@/lib/db'; export async function GET(request: NextRequest) { const db = getDatabase(); const leads = db.prepare(` SELECT id, email, name, source, status, created_at FROM crm_leads WHERE status = ? ORDER BY created_at DESC `).all('active'); return NextResponse.json({ leads }); } export async function POST(request: NextRequest) { const db = getDatabase(); const { email, name, source, metadata } = await request.json(); const result = db.prepare(` INSERT INTO crm_leads (email, name, source, metadata, status) VALUES (?, ?, ?, ?, 'new') `).run(email, name, source, JSON.stringify(metadata)); return NextResponse.json({ id: result.lastInsertRowid }, { status: 201 }); } ``` ### Outreach Sequencing ```typescript // lib/outreach/sequence.ts import { getDatabase } from '@/lib/db'; export interface OutreachSequence { id: number; name: string; steps: OutreachStep[]; status: 'active' | 'paused' | 'archived'; } export interface OutreachStep { order: number; delay_hours: number; template_id: string; channel: 'email' | 'linkedin' | 'twitter'; } export function createSequence( name: string, steps: OutreachStep[] ): number { const db = getDatabase(); const result = db.prepare(` INSERT INTO outreach_sequences (name, steps, status) VALUES (?, ?, 'active') `).run(name, JSON.stringify(steps)); return result.lastInsertRowid as number; } export function enrollInSequence( leadId: number, sequenceId: number ): void { const db = getDatabase(); db.prepare(` INSERT INTO outreach_enrollments (lead_id, sequence_id, current_step, status) VALUES (?, ?, 0, 'active') `).run(leadId, sequenceId); } export function pauseSequence(sequenceId: number): void { const db = getDatabase(); db.prepare(` UPDATE outreach_sequences SET status = 'paused' WHERE id = ? `).run(sequenceId); } ``` ### Content Operations ```typescript // lib/content/calendar.ts import { getDatabase } from '@/lib/db'; export interface ContentItem { id: number; title: string; type: 'blog' | 'social' | 'email' | 'video'; status: 'draft' | 'scheduled' | 'published'; scheduled_at?: Date; published_at?: Date; metadata: Record<string, any>; } export function getContentCalendar( startDate: Date, endDate: Date ): ContentItem[] { const db = getDatabase(); const items = db.prepare(` SELECT id, title, type, status, scheduled_at, published_at, metadata FROM content_items WHERE scheduled_at BETWEEN ? AND ? ORDER BY scheduled_at ASC `).all(startDate.toISOString(), endDate.toISOString()); return items.map(item => ({ ...item, metadata: JSON.parse(item.metadata as string), scheduled_at: item.scheduled_at ? new Date(item.scheduled_at) : undefined, published_at: item.published_at ? new Date(item.published_at) : undefined, })); } export function createContentItem(item: Omit<ContentItem, 'id'>): number { const db = getDatabase(); const result = db.prepare(` INSERT INTO content_items (title, type, status, scheduled_at, metadata) VALUES (?, ?, ?, ?, ?) `).run( item.title, item.type, item.status, item.scheduled_at?.toISOString(), JSON.stringify(item.metadata) ); return result.lastInsertRowid as number; } ``` ### OpenClaw Agent Integration ```typescript // lib/openclaw/agents.ts import { readdir, readFile } from 'fs/promises'; import { join } from 'path'; export interface OpenClawAgent { name: string; description: string; type: 'agent' | 'squad'; capabilities: string[]; config: Record<string, any>; } export async function discoverAgents( openclawHome: string ): Promise<OpenClawAgent[]> { const agentsDir = join(openclawHome, 'agents'); const agents: OpenClawAgent[] = []; try { const entries = await readdir(agentsDir, { withFileTypes: true }); for (const entry of entries) { if (entry.isDirectory()) { const configPath = join(agentsDir, entry.name, 'agent.json'); try { const configData = await readFile(configPath, 'utf-8'); const config = JSON.parse(configData); agents.push({ name: entry.name, description: config.description || '', type: config.type || 'agent', capabilities: config.capabilities || [], config, }); } catch (err) { console.warn(`Could not load agent config for ${entry.name}`); } } } } catch (err) { console.error('Failed to discover agents:', err); } return agents; } ``` ### Cron Job Management ```typescript // lib/cron/scheduler.ts import { getDatabase } from '@/lib/db'; export interface CronJob { id: number; name: string; schedule: string; // cron expression, 'every 1h', or 'at 09:00' agent: string; task: string; enabled: boolean; last_run?: Date; } export function createCronJob(job: Omit<CronJob, 'id'>): number { const db = getDatabase(); const result = db.prepare(` INSERT INTO cron_jobs (name, schedule, agent, task, enabled) VALUES (?, ?, ?, ?, ?) `).run(job.name, job.schedule, job.agent, job.task, job.enabled ? 1 : 0); return result.lastInsertRowid as number; } export function getCronJobs(): CronJob[] { const db = getDatabase(); const jobs = db.prepare(` SELECT id, name, schedule, agent, task, enabled, last_run FROM cron_jobs ORDER BY name ASC `).all(); return jobs.map(job => ({ ...job, enabled: Boolean(job.enabled), last_run: job.last_run ? new Date(job.last_run) : undefined, })); } export function updateLastRun(jobId: number): void { const db = getDatabase(); db.prepare(` UPDATE cron_jobs SET last_run = ? WHERE id = ? `).run(new Date().toISOString(), jobId); } ``` ## Authentication Patterns ### Session-Based Auth ```typescript // middleware.ts import { NextResponse } from 'next/server'; import type { NextRequest } from 'next/server'; export function middleware(request: NextRequest) { const session = request.cookies.get('hermes-session'); if (!session && request.nextUrl.pathname.startsWith('/api/')) { return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); } return NextResponse.next(); } export const config = { matcher: '/api/:path*', }; ``` ### API Key Auth ```typescript // lib/auth/api-key.ts import { NextRequest } from 'next/server'; export function validateApiKey(request: NextRequest): boolean { const authHeader = request.headers.get('authorization'); if (!authHeader || !authHeader.startsWith('Bearer ')) { return false; } const token = authHeader.substring(7); return token === process.env.API_KEY; } // Usage in API route export async function GET(request: NextRequest) { if (!validateApiKey(request)) { return NextResponse.json({ error: 'Invalid API key' }, { status: 401 }); } // Continue with request... } ``` ## Analytics Integration ### Track Events ```typescript // lib/analytics/events.ts import { getDatabase } from '@/lib/db'; export interface AnalyticsEvent { event_type: string; properties: Record<string, any>; user_id?: string; session_id?: string; } export function trackEvent(event: AnalyticsEvent): void { const db = getDatabase(); db.prepare(` INSERT INTO analytics_events (event_type, properties, user_id, session_id, timestamp) VALUES (?, ?, ?, ?, ?) `).run( event.event_type, JSON.stringify(event.properties), event.user_id, event.session_id, new Date().toISOString() ); } export function getKPIs(startDate: Date, endDate: Date) { const db = getDatabase(); return { leads: db.prepare(` SELECT COUNT(*) as count FROM crm_leads
Auf GitHub ansehen
Diese SKILL.md ist sehr gross, daher zeigt SkillsMP hier nur den ersten Abschnitt. Auf GitHub ansehen