diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..95d815a --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Operator Uplift + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/app/(auth)/login/page.tsx b/app/(auth)/login/page.tsx new file mode 100644 index 0000000..c4a1271 --- /dev/null +++ b/app/(auth)/login/page.tsx @@ -0,0 +1,96 @@ +"use client"; + +import { useState } from 'react'; +import Link from 'next/link'; +import { Bot, ArrowRight, Mail, Sparkles, CheckCircle2 } from 'lucide-react'; +import { GlowButton } from '@/src/components/ui/GlowButton'; +import { Logo } from '@/src/components/Icons'; + +export default function LoginPage() { + const [email, setEmail] = useState(''); + const [isLoading, setIsLoading] = useState(false); + const [submitted, setSubmitted] = useState(false); + + const handleWaitlist = async (e: React.FormEvent) => { + e.preventDefault(); + setIsLoading(true); + try { + const res = await fetch('/api/waitlist', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ email }), + }); + if (res.ok) { + setSubmitted(true); + } + } catch { + // Fallback: still show success to not block UX + setSubmitted(true); + } + setIsLoading(false); + }; + + return ( +
+
+
+
+ + + OperatorUplift + +
+ + {submitted ? ( +
+
+ +
+

You're on the list

+

We'll notify you at {email} when early access opens.

+

Follow us for updates:

+
+ X (Twitter) + Discord + GitHub +
+ ← Back to home +
+ ) : ( + <> +
+
+ Early Access +
+

Join the Waitlist

+

Operator Uplift is currently in private beta. Sign up to get early access when we launch.

+
+
+
+ +
+ + setEmail(e.target.value)} placeholder="you@example.com" aria-label="Email address" className="w-full bg-white/5 border border-white/10 rounded-lg pl-12 pr-4 py-3 text-white focus:border-primary/50 focus:outline-none transition-colors" required /> +
+
+ + {isLoading ? 'Joining...' : 'Join Waitlist'} + +
+
+
+ Local-first + + Privacy-first + + Open source +
+
+ + )} +
+
+ ); +} diff --git a/app/(auth)/signup/page.tsx b/app/(auth)/signup/page.tsx new file mode 100644 index 0000000..94c1da4 --- /dev/null +++ b/app/(auth)/signup/page.tsx @@ -0,0 +1,10 @@ +"use client"; + +import { useEffect } from 'react'; +import { useRouter } from 'next/navigation'; + +export default function SignupPage() { + const router = useRouter(); + useEffect(() => { router.replace('/login'); }, [router]); + return null; +} diff --git a/app/(dashboard)/agents/builder/page.tsx b/app/(dashboard)/agents/builder/page.tsx new file mode 100644 index 0000000..4cfa3f2 --- /dev/null +++ b/app/(dashboard)/agents/builder/page.tsx @@ -0,0 +1,167 @@ +"use client"; + +import { useState } from 'react'; +import { Sparkles, ArrowRight, ArrowLeft, Check, Bot, Brain, Code, FileText, Globe, Shield, Zap, MessageSquare } from 'lucide-react'; +import { GlowButton } from '@/src/components/ui/GlowButton'; +import { Card, CardContent } from '@/src/components/ui/Card'; +import { Badge } from '@/src/components/ui/Badge'; +import { MobilePageWrapper } from '@/src/components/mobile'; +import { useToast } from '@/src/components/ui/Toast'; + +const TEMPLATES = [ + { id: 'general', name: 'General Assistant', icon: Sparkles, color: 'text-primary', desc: 'A versatile agent for everyday tasks', capabilities: ['Chat', 'Research', 'Writing'] }, + { id: 'code', name: 'Code Expert', icon: Code, color: 'text-emerald-400', desc: 'Specialized in code generation, review, and debugging', capabilities: ['Code Gen', 'Debug', 'Refactor'] }, + { id: 'research', name: 'Research Agent', icon: Brain, color: 'text-[#E77630]', desc: 'Deep research across papers, docs, and the web', capabilities: ['Papers', 'Citations', 'Summarize'] }, + { id: 'writer', name: 'Content Writer', icon: FileText, color: 'text-[#F59E0B]', desc: 'Blog posts, docs, social media, and more', capabilities: ['Blog', 'Social', 'Docs'] }, + { id: 'security', name: 'Security Analyst', icon: Shield, color: 'text-red-400', desc: 'Threat detection, vulnerability scanning, compliance', capabilities: ['OWASP', 'Audit', 'Monitor'] }, + { id: 'web', name: 'Web Agent', icon: Globe, color: 'text-amber-400', desc: 'Browse, scrape, and interact with the web', capabilities: ['Browse', 'Scrape', 'API'] }, +]; + +const MODELS = [ + { id: 'claude-opus-4-6', name: 'Claude Opus 4.6', provider: 'Anthropic', badge: 'RECOMMENDED' }, + { id: 'gpt-4.1', name: 'GPT-4.1', provider: 'OpenAI', badge: 'FAST' }, + { id: 'gemini-2.5-pro', name: 'Gemini 2.5 Pro', provider: 'Google', badge: 'LONG CTX' }, + { id: 'deepseek-v3', name: 'DeepSeek V3', provider: 'DeepSeek', badge: 'OPEN' }, +]; + +export default function AgentBuilderPage() { + const [step, setStep] = useState(0); + const [name, setName] = useState(''); + const [description, setDescription] = useState(''); + const [template, setTemplate] = useState(''); + const [model, setModel] = useState('claude-4-sonnet'); + const [systemPrompt, setSystemPrompt] = useState(''); + const { showToast } = useToast(); + + const steps = ['Template', 'Configure', 'Model', 'Review']; + const selectedTemplate = TEMPLATES.find(t => t.id === template); + + const handleDeploy = () => { + const agent = { name, description, template, model, systemPrompt, id: Date.now().toString(), createdAt: new Date().toISOString() }; + const existing = JSON.parse(localStorage.getItem('custom-agents') || '[]'); + existing.push(agent); + localStorage.setItem('custom-agents', JSON.stringify(existing)); + showToast(`Agent "${name}" deployed successfully!`, 'success'); + setStep(0); setName(''); setDescription(''); setTemplate(''); setSystemPrompt(''); + }; + + return ( + +
+
+ {/* Header */} +
+
+
+ +
+ WIZARD +
+

Agent Builder

+

Create and deploy custom AI agents in minutes

+
+ + {/* Step indicators */} +
+ {steps.map((s, i) => ( +
+
+ {i < step ? : i + 1} +
+ + {i < steps.length - 1 &&
} +
+ ))} +
+ + {/* Step content */} + + {step === 0 && ( +
+

Choose a template

+
+ {TEMPLATES.map(t => { + const Icon = t.icon; + return ( + + ); + })} +
+
+ )} + + {step === 1 && ( +
+

Configure your agent

+
setName(e.target.value)} className="w-full bg-white/5 border border-white/10 rounded-lg px-4 py-3 text-white focus:border-primary/50 focus:outline-none" placeholder="My Agent" />
+
setDescription(e.target.value)} className="w-full bg-white/5 border border-white/10 rounded-lg px-4 py-3 text-white focus:border-primary/50 focus:outline-none" placeholder="What does this agent do?" />
+