#!/usr/bin/env node import { writeFileSync, mkdirSync } from 'fs'; import { join } from 'path'; const apps = [ { name: 'contact-detail', title: 'Contact Detail', tool: 'keap_get_contact' }, { name: 'contact-grid', title: 'Contact Grid', tool: 'keap_list_contacts' }, { name: 'deal-detail', title: 'Deal Detail', tool: 'keap_get_opportunity' }, { name: 'campaign-dashboard', title: 'Campaign Dashboard', tool: 'keap_list_campaigns' }, { name: 'campaign-detail', title: 'Campaign Detail', tool: 'keap_get_campaign' }, { name: 'order-dashboard', title: 'Order Dashboard', tool: 'keap_list_orders' }, { name: 'order-detail', title: 'Order Detail', tool: 'keap_get_order' }, { name: 'appointment-calendar', title: 'Appointment Calendar', tool: 'keap_list_appointments' }, { name: 'tag-manager', title: 'Tag Manager', tool: 'keap_list_tags' }, { name: 'email-composer', title: 'Email Composer', tool: 'keap_send_email' }, { name: 'automation-builder', title: 'Automation Builder', tool: 'keap_list_hooks' }, { name: 'affiliate-dashboard', title: 'Affiliate Dashboard', tool: 'keap_list_affiliates' }, { name: 'product-catalog', title: 'Product Catalog', tool: 'keap_list_products' }, { name: 'subscription-manager', title: 'Subscription Manager', tool: 'keap_list_subscriptions' }, { name: 'file-browser', title: 'File Browser', tool: 'keap_list_files' }, { name: 'settings-panel', title: 'Settings Panel', tool: 'keap_get_account_profile' }, { name: 'analytics-dashboard', title: 'Analytics Dashboard', tool: 'keap_list_contacts' }, ]; const baseDir = './src/ui/react-app/src/apps'; apps.forEach(app => { const appDir = join(baseDir, app.name); try { mkdirSync(appDir, { recursive: true }); } catch (e) {} // App.tsx const appTsx = `import React, { useState, useEffect } from 'react'; import { useCallTool } from '../../hooks/useCallTool'; import '../../styles/global.css'; export default function ${app.name.split('-').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join('')}() { const { callTool, loading, error } = useCallTool(); const [data, setData] = useState(null); useEffect(() => { loadData(); }, []); const loadData = async () => { try { const result = await callTool('${app.tool}', {}); setData(result); } catch (err) { console.error('Failed to load data:', err); } }; return (

${app.title}

{error &&
{error}
} {loading ? (
Loading...
) : (
            {JSON.stringify(data, null, 2)}
          
)}
); } `; // index.html const indexHtml = ` ${app.title} - Keap
`; // main.tsx const mainTsx = `import React from 'react'; import ReactDOM from 'react-dom/client'; import App from './App'; ReactDOM.createRoot(document.getElementById('root')!).render( ); `; // vite.config.ts const viteConfig = `import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; import { resolve } from 'path'; export default defineConfig({ plugins: [react()], build: { outDir: '../../dist/${app.name}', emptyOutDir: true, rollupOptions: { input: resolve(__dirname, 'index.html'), output: { entryFileNames: 'assets/[name].js', chunkFileNames: 'assets/[name].js', assetFileNames: 'assets/[name].[ext]' } } } }); `; writeFileSync(join(appDir, 'App.tsx'), appTsx); writeFileSync(join(appDir, 'index.html'), indexHtml); writeFileSync(join(appDir, 'main.tsx'), mainTsx); writeFileSync(join(appDir, 'vite.config.ts'), viteConfig); console.log(`✓ Generated ${app.name}`); }); // Also generate for pipeline-kanban and task-manager if they don't have all files ['pipeline-kanban', 'task-manager'].forEach(appName => { const appDir = join(baseDir, appName); const mainTsx = `import React from 'react'; import ReactDOM from 'react-dom/client'; import App from './App'; ReactDOM.createRoot(document.getElementById('root')!).render( ); `; const indexHtml = ` ${appName.split('-').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' ')} - Keap
`; const viteConfig = `import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; import { resolve } from 'path'; export default defineConfig({ plugins: [react()], build: { outDir: '../../dist/${appName}', emptyOutDir: true, rollupOptions: { input: resolve(__dirname, 'index.html'), output: { entryFileNames: 'assets/[name].js', chunkFileNames: 'assets/[name].js', assetFileNames: 'assets/[name].[ext]' } } } }); `; try { writeFileSync(join(appDir, 'main.tsx'), mainTsx); writeFileSync(join(appDir, 'index.html'), indexHtml); writeFileSync(join(appDir, 'vite.config.ts'), viteConfig); console.log(`✓ Completed ${appName}`); } catch (e) { console.log(`! ${appName} already has custom files`); } }); console.log(`\nGenerated ${apps.length + 2} apps successfully!`);