mirror of
https://github.com/harivansh-afk/sandbox-agent.git
synced 2026-04-16 20:01:27 +00:00
Configure lefthook formatter checks (#231)
* Add lefthook formatter checks * Fix SDK mode hydration * Stabilize SDK mode integration test
This commit is contained in:
parent
0471214d65
commit
d2346bafb3
282 changed files with 5840 additions and 8399 deletions
|
|
@ -1,42 +1,40 @@
|
|||
'use client';
|
||||
"use client";
|
||||
|
||||
import { useState } from 'react';
|
||||
import { ChevronDown } from 'lucide-react';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import { useState } from "react";
|
||||
import { ChevronDown } from "lucide-react";
|
||||
import { motion, AnimatePresence } from "framer-motion";
|
||||
|
||||
const faqs = [
|
||||
{
|
||||
question: 'Does this replace the Vercel AI SDK?',
|
||||
question: "Does this replace the Vercel AI SDK?",
|
||||
answer:
|
||||
"No, they're complementary. AI SDK is for building chat interfaces and calling LLMs. This SDK is for controlling autonomous coding agents that write code and run commands. Use AI SDK for your UI, use this when you need a coding agent to actually code.",
|
||||
},
|
||||
{
|
||||
question: 'Which coding agents are supported?',
|
||||
answer:
|
||||
'Claude Code, Codex, OpenCode, Amp, and Pi. The SDK normalizes their APIs so you can swap between them without changing your code.',
|
||||
question: "Which coding agents are supported?",
|
||||
answer: "Claude Code, Codex, OpenCode, Amp, and Pi. The SDK normalizes their APIs so you can swap between them without changing your code.",
|
||||
},
|
||||
{
|
||||
question: 'How is session data persisted?',
|
||||
question: "How is session data persisted?",
|
||||
answer:
|
||||
"This SDK does not handle persisting session data. Events stream in a universal JSON schema that you can persist anywhere. Consider using Postgres or <a href='https://rivet.gg' target='_blank' rel='noopener noreferrer' class='text-orange-400 hover:underline'>Rivet Actors</a> for data persistence.",
|
||||
},
|
||||
{
|
||||
question: 'Can I run this locally or does it require a sandbox provider?',
|
||||
answer:
|
||||
'Both. Run locally for development, deploy to E2B, Daytona, or Vercel Sandboxes for production.',
|
||||
question: "Can I run this locally or does it require a sandbox provider?",
|
||||
answer: "Both. Run locally for development, deploy to E2B, Daytona, or Vercel Sandboxes for production.",
|
||||
},
|
||||
{
|
||||
question: 'Does it support [platform]?',
|
||||
question: "Does it support [platform]?",
|
||||
answer:
|
||||
"The server is a single Rust binary that runs anywhere with a curl install. If your platform can run Linux binaries (Docker, VMs, etc.), it works. See the deployment guides for E2B, Daytona, and Vercel Sandboxes.",
|
||||
},
|
||||
{
|
||||
question: 'Can I use this with my personal API keys?',
|
||||
question: "Can I use this with my personal API keys?",
|
||||
answer:
|
||||
"Yes. Use <code>sandbox-agent credentials extract-env</code> to extract API keys from your local agent configs (Claude Code, Codex, OpenCode, Amp, Pi) and pass them to the sandbox environment.",
|
||||
},
|
||||
{
|
||||
question: 'Why Rust and not [language]?',
|
||||
question: "Why Rust and not [language]?",
|
||||
answer:
|
||||
"Rust gives us a single static binary, fast startup, and predictable memory usage. That makes it easy to run inside sandboxes or in CI without shipping a large runtime, such as Node.js.",
|
||||
},
|
||||
|
|
@ -51,7 +49,7 @@ const faqs = [
|
|||
"Official SDKs assume local execution. They spawn processes and expect interactive terminals. This SDK runs a server inside a sandbox that you connect to over HTTP — designed for remote control from the start.",
|
||||
},
|
||||
{
|
||||
question: 'Why not just SSH into the sandbox?',
|
||||
question: "Why not just SSH into the sandbox?",
|
||||
answer:
|
||||
"Coding agents expect interactive terminals with proper TTY handling. SSH with piped commands breaks tool confirmations, streaming output, and human-in-the-loop flows. The SDK handles all of this over a clean HTTP API.",
|
||||
},
|
||||
|
|
@ -62,22 +60,15 @@ function FAQItem({ question, answer }: { question: string; answer: string }) {
|
|||
|
||||
return (
|
||||
<div className="border-t border-white/10 first:border-t-0">
|
||||
<button
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
className="group flex w-full items-center justify-between py-5 text-left"
|
||||
>
|
||||
<button onClick={() => setIsOpen(!isOpen)} className="group flex w-full items-center justify-between py-5 text-left">
|
||||
<span className="text-base font-normal text-white pr-4 group-hover:text-zinc-300 transition-colors">{question}</span>
|
||||
<ChevronDown
|
||||
className={`h-4 w-4 shrink-0 text-zinc-500 transition-transform duration-200 ${
|
||||
isOpen ? 'rotate-180' : ''
|
||||
}`}
|
||||
/>
|
||||
<ChevronDown className={`h-4 w-4 shrink-0 text-zinc-500 transition-transform duration-200 ${isOpen ? "rotate-180" : ""}`} />
|
||||
</button>
|
||||
<AnimatePresence>
|
||||
{isOpen && (
|
||||
<motion.div
|
||||
initial={{ height: 0, opacity: 0 }}
|
||||
animate={{ height: 'auto', opacity: 1 }}
|
||||
animate={{ height: "auto", opacity: 1 }}
|
||||
exit={{ height: 0, opacity: 0 }}
|
||||
transition={{ duration: 0.2 }}
|
||||
className="overflow-hidden"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
'use client';
|
||||
"use client";
|
||||
|
||||
import { motion } from 'framer-motion';
|
||||
import { Workflow, Server, Database, Download, Globe, Plug } from 'lucide-react';
|
||||
import { motion } from "framer-motion";
|
||||
import { Workflow, Server, Database, Download, Globe, Plug } from "lucide-react";
|
||||
|
||||
export function FeatureGrid() {
|
||||
return (
|
||||
|
|
@ -44,8 +44,7 @@ export function FeatureGrid() {
|
|||
<h4 className="text-base font-normal text-white">Universal Agent API</h4>
|
||||
</div>
|
||||
<p className="text-zinc-500 leading-relaxed text-base max-w-2xl">
|
||||
Claude Code, Codex, OpenCode, and Amp each have different APIs. We provide a single,
|
||||
unified interface to control them all.
|
||||
Claude Code, Codex, OpenCode, and Amp each have different APIs. We provide a single, unified interface to control them all.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
|
@ -108,11 +107,11 @@ export function FeatureGrid() {
|
|||
<Plug className="h-4 w-4" />
|
||||
</div>
|
||||
<h4 className="text-base font-normal text-white">OpenCode Support</h4>
|
||||
<span className="rounded-full border border-white/10 px-2 py-0.5 text-[10px] font-medium text-zinc-500 transition-colors group-hover:text-pink-400 group-hover:border-pink-400/30">Experimental</span>
|
||||
<span className="rounded-full border border-white/10 px-2 py-0.5 text-[10px] font-medium text-zinc-500 transition-colors group-hover:text-pink-400 group-hover:border-pink-400/30">
|
||||
Experimental
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-zinc-500 text-sm leading-relaxed">
|
||||
Connect OpenCode CLI, SDK, or web UI to control agents through familiar OpenCode tooling.
|
||||
</p>
|
||||
<p className="text-zinc-500 text-sm leading-relaxed">Connect OpenCode CLI, SDK, or web UI to control agents through familiar OpenCode tooling.</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,26 +1,26 @@
|
|||
'use client';
|
||||
"use client";
|
||||
|
||||
import { motion } from 'framer-motion';
|
||||
import { motion } from "framer-motion";
|
||||
|
||||
const footer = {
|
||||
products: [
|
||||
{ name: 'Actors', href: 'https://rivet.dev/docs/actors' },
|
||||
{ name: 'Sandbox Agent SDK', href: '/docs' },
|
||||
{ name: "Actors", href: "https://rivet.dev/docs/actors" },
|
||||
{ name: "Sandbox Agent SDK", href: "/docs" },
|
||||
],
|
||||
developers: [
|
||||
{ name: 'Documentation', href: '/docs' },
|
||||
{ name: 'Changelog', href: 'https://github.com/rivet-dev/sandbox-agent/releases' },
|
||||
{ name: 'Blog', href: 'https://www.rivet.dev/blog/' },
|
||||
{ name: "Documentation", href: "/docs" },
|
||||
{ name: "Changelog", href: "https://github.com/rivet-dev/sandbox-agent/releases" },
|
||||
{ name: "Blog", href: "https://www.rivet.dev/blog/" },
|
||||
],
|
||||
legal: [
|
||||
{ name: 'Terms', href: 'https://rivet.dev/terms' },
|
||||
{ name: 'Privacy Policy', href: 'https://rivet.dev/privacy' },
|
||||
{ name: 'Acceptable Use', href: 'https://rivet.dev/acceptable-use' },
|
||||
{ name: "Terms", href: "https://rivet.dev/terms" },
|
||||
{ name: "Privacy Policy", href: "https://rivet.dev/privacy" },
|
||||
{ name: "Acceptable Use", href: "https://rivet.dev/acceptable-use" },
|
||||
],
|
||||
social: [
|
||||
{
|
||||
name: 'Discord',
|
||||
href: 'https://discord.gg/auCecybynK',
|
||||
name: "Discord",
|
||||
href: "https://discord.gg/auCecybynK",
|
||||
icon: (
|
||||
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.946 2.418-2.157 2.418z" />
|
||||
|
|
@ -28,8 +28,8 @@ const footer = {
|
|||
),
|
||||
},
|
||||
{
|
||||
name: 'GitHub',
|
||||
href: 'https://github.com/rivet-dev/sandbox-agent',
|
||||
name: "GitHub",
|
||||
href: "https://github.com/rivet-dev/sandbox-agent",
|
||||
icon: (
|
||||
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" />
|
||||
|
|
@ -37,8 +37,8 @@ const footer = {
|
|||
),
|
||||
},
|
||||
{
|
||||
name: 'Twitter',
|
||||
href: 'https://x.com/rivet_dev',
|
||||
name: "Twitter",
|
||||
href: "https://x.com/rivet_dev",
|
||||
icon: (
|
||||
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z" />
|
||||
|
|
@ -64,18 +64,10 @@ export function Footer() {
|
|||
<a href="https://rivet.dev" className="inline-block">
|
||||
<img src="/rivet-logo-text-white.svg" alt="Rivet" className="h-6 w-auto opacity-90 hover:opacity-100 transition-opacity" />
|
||||
</a>
|
||||
<p className="text-sm leading-6 text-zinc-500">
|
||||
Infrastructure for software that thinks
|
||||
</p>
|
||||
<p className="text-sm leading-6 text-zinc-500">Infrastructure for software that thinks</p>
|
||||
<div className="flex space-x-4">
|
||||
{footer.social.map((item) => (
|
||||
<a
|
||||
key={item.name}
|
||||
href={item.href}
|
||||
className="text-zinc-500 hover:text-white transition-colors"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<a key={item.name} href={item.href} className="text-zinc-500 hover:text-white transition-colors" target="_blank" rel="noopener noreferrer">
|
||||
<span className="sr-only">{item.name}</span>
|
||||
{item.icon}
|
||||
</a>
|
||||
|
|
@ -85,20 +77,12 @@ export function Footer() {
|
|||
|
||||
{/* Links */}
|
||||
<div className="mt-12 grid grid-cols-2 gap-8 md:grid-cols-3 xl:col-span-8 xl:mt-0">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: 0.1 }}
|
||||
>
|
||||
<motion.div initial={{ opacity: 0, y: 20 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true }} transition={{ duration: 0.5, delay: 0.1 }}>
|
||||
<h3 className="text-sm font-semibold leading-6 text-white">Products</h3>
|
||||
<ul role="list" className="mt-4 space-y-3">
|
||||
{footer.products.map((item) => (
|
||||
<li key={item.name}>
|
||||
<a
|
||||
href={item.href}
|
||||
className="text-sm leading-6 text-zinc-500 hover:text-white transition-colors"
|
||||
>
|
||||
<a href={item.href} className="text-sm leading-6 text-zinc-500 hover:text-white transition-colors">
|
||||
{item.name}
|
||||
</a>
|
||||
</li>
|
||||
|
|
@ -116,10 +100,7 @@ export function Footer() {
|
|||
<ul role="list" className="mt-4 space-y-3">
|
||||
{footer.developers.map((item) => (
|
||||
<li key={item.name}>
|
||||
<a
|
||||
href={item.href}
|
||||
className="text-sm leading-6 text-zinc-500 hover:text-white transition-colors"
|
||||
>
|
||||
<a href={item.href} className="text-sm leading-6 text-zinc-500 hover:text-white transition-colors">
|
||||
{item.name}
|
||||
</a>
|
||||
</li>
|
||||
|
|
@ -127,20 +108,12 @@ export function Footer() {
|
|||
</ul>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: 0.2 }}
|
||||
>
|
||||
<motion.div initial={{ opacity: 0, y: 20 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true }} transition={{ duration: 0.5, delay: 0.2 }}>
|
||||
<h3 className="text-sm font-semibold leading-6 text-white">Legal</h3>
|
||||
<ul role="list" className="mt-4 space-y-3">
|
||||
{footer.legal.map((item) => (
|
||||
<li key={item.name}>
|
||||
<a
|
||||
href={item.href}
|
||||
className="text-sm leading-6 text-zinc-500 hover:text-white transition-colors"
|
||||
>
|
||||
<a href={item.href} className="text-sm leading-6 text-zinc-500 hover:text-white transition-colors">
|
||||
{item.name}
|
||||
</a>
|
||||
</li>
|
||||
|
|
@ -158,9 +131,7 @@ export function Footer() {
|
|||
transition={{ duration: 0.5, delay: 0.3 }}
|
||||
className="mt-12 border-t border-white/10 pt-8"
|
||||
>
|
||||
<p className="text-xs text-zinc-600 text-center">
|
||||
© {new Date().getFullYear()} Rivet Gaming, Inc. All rights reserved.
|
||||
</p>
|
||||
<p className="text-xs text-zinc-600 text-center">© {new Date().getFullYear()} Rivet Gaming, Inc. All rights reserved.</p>
|
||||
</motion.div>
|
||||
</div>
|
||||
</footer>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
'use client';
|
||||
"use client";
|
||||
|
||||
import { motion } from 'framer-motion';
|
||||
import { Code, Server, GitBranch } from 'lucide-react';
|
||||
import { CopyButton } from './ui/CopyButton';
|
||||
import { motion } from "framer-motion";
|
||||
import { Code, Server, GitBranch } from "lucide-react";
|
||||
import { CopyButton } from "./ui/CopyButton";
|
||||
|
||||
const sdkCodeRaw = `import { SandboxAgent } from "sandbox-agent";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
'use client';
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
interface GitHubStarsProps extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
|
||||
repo?: string;
|
||||
|
|
@ -13,11 +13,7 @@ function formatNumber(num: number): string {
|
|||
return num.toString();
|
||||
}
|
||||
|
||||
export function GitHubStars({
|
||||
repo = 'rivet-dev/sandbox-agent',
|
||||
className,
|
||||
...props
|
||||
}: GitHubStarsProps) {
|
||||
export function GitHubStars({ repo = "rivet-dev/sandbox-agent", className, ...props }: GitHubStarsProps) {
|
||||
const [stars, setStars] = useState<number | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -35,7 +31,7 @@ export function GitHubStars({
|
|||
|
||||
fetch(`https://api.github.com/repos/${repo}`)
|
||||
.then((response) => {
|
||||
if (!response.ok) throw new Error('Failed to fetch');
|
||||
if (!response.ok) throw new Error("Failed to fetch");
|
||||
return response.json();
|
||||
})
|
||||
.then((data) => {
|
||||
|
|
@ -50,29 +46,16 @@ export function GitHubStars({
|
|||
);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('Failed to fetch stars', err);
|
||||
console.error("Failed to fetch stars", err);
|
||||
});
|
||||
}, [repo]);
|
||||
|
||||
return (
|
||||
<a
|
||||
href={`https://github.com/${repo}`}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className={className}
|
||||
{...props}
|
||||
>
|
||||
<svg
|
||||
className="w-5 h-5"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<a href={`https://github.com/${repo}`} target="_blank" rel="noreferrer" className={className} {...props}>
|
||||
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" />
|
||||
</svg>
|
||||
<span className="hidden md:inline">
|
||||
{stars ? `${formatNumber(stars)} Stars` : 'GitHub'}
|
||||
</span>
|
||||
<span className="hidden md:inline">{stars ? `${formatNumber(stars)} Stars` : "GitHub"}</span>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
'use client';
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { Terminal, Check, ArrowRight } from 'lucide-react';
|
||||
import { useState, useEffect } from "react";
|
||||
import { motion } from "framer-motion";
|
||||
import { Terminal, Check, ArrowRight } from "lucide-react";
|
||||
|
||||
const ADAPTERS = [
|
||||
{ label: 'Claude Code', color: '#D97757', x: 20, y: 70, logo: '/logos/claude.svg' },
|
||||
{ label: 'Codex', color: '#10A37F', x: 132, y: 70, logo: 'openai' },
|
||||
{ label: 'Pi', color: '#06B6D4', x: 244, y: 70, logo: 'pi' },
|
||||
{ label: 'Amp', color: '#F59E0B', x: 76, y: 155, logo: '/logos/amp.svg' },
|
||||
{ label: 'OpenCode', color: '#8B5CF6', x: 188, y: 155, logo: 'opencode' },
|
||||
{ label: "Claude Code", color: "#D97757", x: 20, y: 70, logo: "/logos/claude.svg" },
|
||||
{ label: "Codex", color: "#10A37F", x: 132, y: 70, logo: "openai" },
|
||||
{ label: "Pi", color: "#06B6D4", x: 244, y: 70, logo: "pi" },
|
||||
{ label: "Amp", color: "#F59E0B", x: 76, y: 155, logo: "/logos/amp.svg" },
|
||||
{ label: "OpenCode", color: "#8B5CF6", x: 188, y: 155, logo: "opencode" },
|
||||
];
|
||||
|
||||
function UniversalAPIDiagram() {
|
||||
|
|
@ -29,7 +29,7 @@ function UniversalAPIDiagram() {
|
|||
className="absolute inset-0 opacity-[0.15] pointer-events-none transition-all duration-1000"
|
||||
style={{
|
||||
backgroundImage: `radial-gradient(circle, ${ADAPTERS[activeIndex].color} 1px, transparent 1px)`,
|
||||
backgroundSize: '24px 24px',
|
||||
backgroundSize: "24px 24px",
|
||||
}}
|
||||
/>
|
||||
|
||||
|
|
@ -46,9 +46,7 @@ function UniversalAPIDiagram() {
|
|||
|
||||
{/* YOUR APP NODE - Glass dark effect with backdrop blur */}
|
||||
<foreignObject x="60" y="175" width="180" height="100">
|
||||
<div
|
||||
className="w-full h-full rounded-2xl border border-white/10 bg-black/40 backdrop-blur-md flex items-center justify-center"
|
||||
>
|
||||
<div className="w-full h-full rounded-2xl border border-white/10 bg-black/40 backdrop-blur-md flex items-center justify-center">
|
||||
<span className="text-white text-xl font-bold">Your App</span>
|
||||
</div>
|
||||
</foreignObject>
|
||||
|
|
@ -72,80 +70,73 @@ function UniversalAPIDiagram() {
|
|||
{/* SANDBOX BOUNDARY - Glass dark effect with backdrop blur */}
|
||||
<foreignObject x="360" y="45" width="410" height="360">
|
||||
<div className="w-full h-full rounded-3xl border border-white/10 bg-black/40 backdrop-blur-md">
|
||||
<div className="text-white text-sm font-extrabold tracking-[0.2em] text-center pt-4">
|
||||
SANDBOX
|
||||
</div>
|
||||
<div className="text-white text-sm font-extrabold tracking-[0.2em] text-center pt-4">SANDBOX</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
|
||||
{/* SANDBOX AGENT SDK */}
|
||||
<g transform="translate(385, 110)">
|
||||
<rect width="360" height="270" rx="20" fill="rgba(0,0,0,0.4)" stroke="rgba(255,255,255,0.2)" strokeWidth="1" />
|
||||
<text x="180" y="35" fill="#FFFFFF" textAnchor="middle" fontSize="18" fontWeight="800">
|
||||
Sandbox Agent Server
|
||||
</text>
|
||||
<rect width="360" height="270" rx="20" fill="rgba(0,0,0,0.4)" stroke="rgba(255,255,255,0.2)" strokeWidth="1" />
|
||||
<text x="180" y="35" fill="#FFFFFF" textAnchor="middle" fontSize="18" fontWeight="800">
|
||||
Sandbox Agent Server
|
||||
</text>
|
||||
|
||||
{/* PROVIDER ADAPTERS */}
|
||||
{ADAPTERS.map((p, i) => {
|
||||
const isActive = i === activeIndex;
|
||||
return (
|
||||
<g key={i} transform={`translate(${p.x}, ${p.y})`}>
|
||||
<rect
|
||||
width="95"
|
||||
height="58"
|
||||
rx="10"
|
||||
fill={isActive ? '#1A1A1E' : '#111'}
|
||||
stroke={isActive ? p.color : '#333'}
|
||||
strokeWidth={isActive ? 2 : 1.5}
|
||||
/>
|
||||
<g opacity={isActive ? 1 : 0.4}>
|
||||
{p.logo === 'openai' ? (
|
||||
<svg x="36.75" y="8" width="22" height="22" viewBox="0 0 24 24" fill="none">
|
||||
<path d="M22.2819 9.8211a5.9847 5.9847 0 0 0-.5157-4.9108 6.0462 6.0462 0 0 0-6.5098-2.9A6.0651 6.0651 0 0 0 4.9807 4.1818a5.9847 5.9847 0 0 0-3.9977 2.9 6.0462 6.0462 0 0 0 .7427 7.0966 5.98 5.98 0 0 0 .511 4.9107 6.051 6.051 0 0 0 6.5146 2.9001A5.9847 5.9847 0 0 0 13.2599 24a6.0557 6.0557 0 0 0 5.7718-4.2058 5.9894 5.9894 0 0 0 3.9977-2.9001 6.0557 6.0557 0 0 0-.7475-7.0729zm-9.022 12.6081a4.4755 4.4755 0 0 1-2.8764-1.0408l.1419-.0804 4.7783-2.7582a.7948.7948 0 0 0 .3927-.6813v-6.7369l2.02 1.1686a.071.071 0 0 1 .038.052v5.5826a4.504 4.504 0 0 1-4.4945 4.4944zm-9.6607-4.1254a4.4708 4.4708 0 0 1-.5346-3.0137l.142.0852 4.783 2.7582a.7712.7712 0 0 0 .7806 0l5.8428-3.3685v2.3324a.0804.0804 0 0 1-.0332.0615L9.74 19.9502a4.4992 4.4992 0 0 1-6.1408-1.6464zM2.3408 7.8956a4.485 4.485 0 0 1 2.3655-1.9728V11.6a.7664.7664 0 0 0 .3879.6765l5.8144 3.3543-2.0201 1.1685a.0757.0757 0 0 1-.071 0l-4.8303-2.7865A4.504 4.504 0 0 1 2.3408 7.872zm16.5963 3.8558L13.1038 8.364 15.1192 7.2a.0757.0757 0 0 1 .071 0l4.8303 2.7913a4.4944 4.4944 0 0 1-.6765 8.1042v-5.6772a.79.79 0 0 0-.407-.667zm2.0107-3.0231l-.142-.0852-4.7735-2.7818a.7759.7759 0 0 0-.7854 0L9.409 9.2297V6.8974a.0662.0662 0 0 1 .0284-.0615l4.8303-2.7866a4.4992 4.4992 0 0 1 6.6802 4.66zM8.3065 12.863l-2.02-1.1638a.0804.0804 0 0 1-.038-.0567V6.0742a4.4992 4.4992 0 0 1 7.3757-3.4537l-.142.0805L8.704 5.459a.7948.7948 0 0 0-.3927.6813zm1.0976-2.3654l2.602-1.4998 2.6069 1.4998v2.9994l-2.5974 1.4997-2.6067-1.4997Z" fill="#ffffff" />
|
||||
</svg>
|
||||
) : p.logo === 'opencode' ? (
|
||||
<svg x="38.5" y="8" width="17" height="22" viewBox="0 0 32 40" fill="none">
|
||||
<path d="M24 32H8V16H24V32Z" fill="#4B4646"/>
|
||||
<path d="M24 8H8V32H24V8ZM32 40H0V0H32V40Z" fill="#F1ECEC"/>
|
||||
</svg>
|
||||
) : p.logo === 'pi' ? (
|
||||
<svg x="36.75" y="8" width="22" height="22" viewBox="0 0 800 800" fill="none">
|
||||
<path fill="#fff" fillRule="evenodd" d="M165.29 165.29H517.36V400H400V517.36H282.65V634.72H165.29ZM282.65 282.65V400H400V282.65Z"/>
|
||||
<path fill="#fff" d="M517.36 400H634.72V634.72H517.36Z"/>
|
||||
</svg>
|
||||
) : (
|
||||
<image href={p.logo} x="36.75" y="8" width="22" height="22" filter="url(#invert-white)" />
|
||||
)}
|
||||
</g>
|
||||
<text
|
||||
x="47.5"
|
||||
y="46"
|
||||
fill="#FFFFFF"
|
||||
textAnchor="middle"
|
||||
fontSize="10"
|
||||
fontWeight="600"
|
||||
opacity={isActive ? 1 : 0.4}
|
||||
>
|
||||
{p.label}
|
||||
</text>
|
||||
{/* PROVIDER ADAPTERS */}
|
||||
{ADAPTERS.map((p, i) => {
|
||||
const isActive = i === activeIndex;
|
||||
return (
|
||||
<g key={i} transform={`translate(${p.x}, ${p.y})`}>
|
||||
<rect
|
||||
width="95"
|
||||
height="58"
|
||||
rx="10"
|
||||
fill={isActive ? "#1A1A1E" : "#111"}
|
||||
stroke={isActive ? p.color : "#333"}
|
||||
strokeWidth={isActive ? 2 : 1.5}
|
||||
/>
|
||||
<g opacity={isActive ? 1 : 0.4}>
|
||||
{p.logo === "openai" ? (
|
||||
<svg x="36.75" y="8" width="22" height="22" viewBox="0 0 24 24" fill="none">
|
||||
<path
|
||||
d="M22.2819 9.8211a5.9847 5.9847 0 0 0-.5157-4.9108 6.0462 6.0462 0 0 0-6.5098-2.9A6.0651 6.0651 0 0 0 4.9807 4.1818a5.9847 5.9847 0 0 0-3.9977 2.9 6.0462 6.0462 0 0 0 .7427 7.0966 5.98 5.98 0 0 0 .511 4.9107 6.051 6.051 0 0 0 6.5146 2.9001A5.9847 5.9847 0 0 0 13.2599 24a6.0557 6.0557 0 0 0 5.7718-4.2058 5.9894 5.9894 0 0 0 3.9977-2.9001 6.0557 6.0557 0 0 0-.7475-7.0729zm-9.022 12.6081a4.4755 4.4755 0 0 1-2.8764-1.0408l.1419-.0804 4.7783-2.7582a.7948.7948 0 0 0 .3927-.6813v-6.7369l2.02 1.1686a.071.071 0 0 1 .038.052v5.5826a4.504 4.504 0 0 1-4.4945 4.4944zm-9.6607-4.1254a4.4708 4.4708 0 0 1-.5346-3.0137l.142.0852 4.783 2.7582a.7712.7712 0 0 0 .7806 0l5.8428-3.3685v2.3324a.0804.0804 0 0 1-.0332.0615L9.74 19.9502a4.4992 4.4992 0 0 1-6.1408-1.6464zM2.3408 7.8956a4.485 4.485 0 0 1 2.3655-1.9728V11.6a.7664.7664 0 0 0 .3879.6765l5.8144 3.3543-2.0201 1.1685a.0757.0757 0 0 1-.071 0l-4.8303-2.7865A4.504 4.504 0 0 1 2.3408 7.872zm16.5963 3.8558L13.1038 8.364 15.1192 7.2a.0757.0757 0 0 1 .071 0l4.8303 2.7913a4.4944 4.4944 0 0 1-.6765 8.1042v-5.6772a.79.79 0 0 0-.407-.667zm2.0107-3.0231l-.142-.0852-4.7735-2.7818a.7759.7759 0 0 0-.7854 0L9.409 9.2297V6.8974a.0662.0662 0 0 1 .0284-.0615l4.8303-2.7866a4.4992 4.4992 0 0 1 6.6802 4.66zM8.3065 12.863l-2.02-1.1638a.0804.0804 0 0 1-.038-.0567V6.0742a4.4992 4.4992 0 0 1 7.3757-3.4537l-.142.0805L8.704 5.459a.7948.7948 0 0 0-.3927.6813zm1.0976-2.3654l2.602-1.4998 2.6069 1.4998v2.9994l-2.5974 1.4997-2.6067-1.4997Z"
|
||||
fill="#ffffff"
|
||||
/>
|
||||
</svg>
|
||||
) : p.logo === "opencode" ? (
|
||||
<svg x="38.5" y="8" width="17" height="22" viewBox="0 0 32 40" fill="none">
|
||||
<path d="M24 32H8V16H24V32Z" fill="#4B4646" />
|
||||
<path d="M24 8H8V32H24V8ZM32 40H0V0H32V40Z" fill="#F1ECEC" />
|
||||
</svg>
|
||||
) : p.logo === "pi" ? (
|
||||
<svg x="36.75" y="8" width="22" height="22" viewBox="0 0 800 800" fill="none">
|
||||
<path fill="#fff" fillRule="evenodd" d="M165.29 165.29H517.36V400H400V517.36H282.65V634.72H165.29ZM282.65 282.65V400H400V282.65Z" />
|
||||
<path fill="#fff" d="M517.36 400H634.72V634.72H517.36Z" />
|
||||
</svg>
|
||||
) : (
|
||||
<image href={p.logo} x="36.75" y="8" width="22" height="22" filter="url(#invert-white)" />
|
||||
)}
|
||||
</g>
|
||||
);
|
||||
})}
|
||||
<text x="47.5" y="46" fill="#FFFFFF" textAnchor="middle" fontSize="10" fontWeight="600" opacity={isActive ? 1 : 0.4}>
|
||||
{p.label}
|
||||
</text>
|
||||
</g>
|
||||
);
|
||||
})}
|
||||
|
||||
{/* Active Agent Label */}
|
||||
<text
|
||||
x="180"
|
||||
y="250"
|
||||
fill={ADAPTERS[activeIndex].color}
|
||||
textAnchor="middle"
|
||||
fontSize="12"
|
||||
fontWeight="800"
|
||||
fontFamily="monospace"
|
||||
letterSpacing="0.1em"
|
||||
>
|
||||
CONNECTED TO {ADAPTERS[activeIndex].label.toUpperCase()}
|
||||
</text>
|
||||
</g>
|
||||
{/* Active Agent Label */}
|
||||
<text
|
||||
x="180"
|
||||
y="250"
|
||||
fill={ADAPTERS[activeIndex].color}
|
||||
textAnchor="middle"
|
||||
fontSize="12"
|
||||
fontWeight="800"
|
||||
fontFamily="monospace"
|
||||
letterSpacing="0.1em"
|
||||
>
|
||||
CONNECTED TO {ADAPTERS[activeIndex].label.toUpperCase()}
|
||||
</text>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
);
|
||||
|
|
@ -153,8 +144,8 @@ function UniversalAPIDiagram() {
|
|||
|
||||
const CopyInstallButton = () => {
|
||||
const [copied, setCopied] = useState(false);
|
||||
const installCommand = 'npx skills add rivet-dev/skills -s sandbox-agent';
|
||||
const shortCommand = 'npx skills add rivet-dev/skills';
|
||||
const installCommand = "npx skills add rivet-dev/skills -s sandbox-agent";
|
||||
const shortCommand = "npx skills add rivet-dev/skills";
|
||||
|
||||
const handleCopy = async () => {
|
||||
try {
|
||||
|
|
@ -162,7 +153,7 @@ const CopyInstallButton = () => {
|
|||
setCopied(true);
|
||||
setTimeout(() => setCopied(false), 2000);
|
||||
} catch (err) {
|
||||
console.error('Failed to copy:', err);
|
||||
console.error("Failed to copy:", err);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -211,11 +202,11 @@ export function Hero() {
|
|||
|
||||
updateViewportMode();
|
||||
handleScroll();
|
||||
window.addEventListener('resize', updateViewportMode);
|
||||
window.addEventListener('scroll', handleScroll);
|
||||
window.addEventListener("resize", updateViewportMode);
|
||||
window.addEventListener("scroll", handleScroll);
|
||||
return () => {
|
||||
window.removeEventListener('resize', updateViewportMode);
|
||||
window.removeEventListener('scroll', handleScroll);
|
||||
window.removeEventListener("resize", updateViewportMode);
|
||||
window.removeEventListener("scroll", handleScroll);
|
||||
};
|
||||
}, []);
|
||||
|
||||
|
|
@ -250,7 +241,8 @@ export function Hero() {
|
|||
transition={{ duration: 0.5, delay: 0.1 }}
|
||||
className="mb-8 text-lg text-zinc-500 leading-relaxed"
|
||||
>
|
||||
The Sandbox Agent SDK is a server that runs inside your sandbox. Your app connects remotely to control Claude Code, Codex, OpenCode, Amp, or Pi — streaming events, handling permissions, managing sessions.
|
||||
The Sandbox Agent SDK is a server that runs inside your sandbox. Your app connects remotely to control Claude Code, Codex, OpenCode, Amp, or Pi
|
||||
— streaming events, handling permissions, managing sessions.
|
||||
</motion.p>
|
||||
|
||||
<motion.div
|
||||
|
|
@ -282,7 +274,6 @@ export function Hero() {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
'use client';
|
||||
"use client";
|
||||
|
||||
import { motion } from 'framer-motion';
|
||||
import { motion } from "framer-motion";
|
||||
|
||||
export function Inspector() {
|
||||
return (
|
||||
|
|
@ -34,11 +34,7 @@ export function Inspector() {
|
|||
transition={{ duration: 0.5, delay: 0.2 }}
|
||||
className="overflow-hidden rounded-2xl border border-white/10"
|
||||
>
|
||||
<img
|
||||
src="/images/inspector.png"
|
||||
alt="Sandbox Agent Inspector"
|
||||
className="w-full"
|
||||
/>
|
||||
<img src="/images/inspector.png" alt="Sandbox Agent Inspector" className="w-full" />
|
||||
</motion.div>
|
||||
</div>
|
||||
</section>
|
||||
|
|
|
|||
|
|
@ -1,18 +1,6 @@
|
|||
'use client';
|
||||
"use client";
|
||||
|
||||
const integrations = [
|
||||
'Daytona',
|
||||
'E2B',
|
||||
'AI SDK',
|
||||
'Anthropic',
|
||||
'OpenAI',
|
||||
'Docker',
|
||||
'Fly.io',
|
||||
'AWS Nitro',
|
||||
'Postgres',
|
||||
'ClickHouse',
|
||||
'Rivet',
|
||||
];
|
||||
const integrations = ["Daytona", "E2B", "AI SDK", "Anthropic", "OpenAI", "Docker", "Fly.io", "AWS Nitro", "Postgres", "ClickHouse", "Rivet"];
|
||||
|
||||
export function Integrations() {
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -1,15 +1,12 @@
|
|||
'use client';
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
import { Menu, X } from 'lucide-react';
|
||||
import { GitHubStars } from './GitHubStars';
|
||||
import { useState, useEffect } from "react";
|
||||
import { Menu, X } from "lucide-react";
|
||||
import { GitHubStars } from "./GitHubStars";
|
||||
|
||||
function NavItem({ href, children }: { href: string; children: React.ReactNode }) {
|
||||
return (
|
||||
<a
|
||||
href={href}
|
||||
className="px-3 py-2 text-sm font-medium text-zinc-400 transition-colors duration-200 hover:text-white"
|
||||
>
|
||||
<a href={href} className="px-3 py-2 text-sm font-medium text-zinc-400 transition-colors duration-200 hover:text-white">
|
||||
{children}
|
||||
</a>
|
||||
);
|
||||
|
|
@ -38,9 +35,7 @@ export function Navigation() {
|
|||
{/* Background with blur */}
|
||||
<div
|
||||
className={`absolute inset-0 -z-[1] hidden overflow-hidden rounded-2xl transition-all duration-300 ease-in-out md:block ${
|
||||
isScrolled
|
||||
? "bg-black/80 backdrop-blur-lg"
|
||||
: "bg-transparent backdrop-blur-none"
|
||||
isScrolled ? "bg-black/80 backdrop-blur-lg" : "bg-transparent backdrop-blur-none"
|
||||
}`}
|
||||
/>
|
||||
|
||||
|
|
@ -76,12 +71,7 @@ export function Navigation() {
|
|||
className="inline-flex items-center justify-center whitespace-nowrap rounded-md border border-white/10 px-4 py-2 h-10 text-sm hover:border-white/20 text-white/90 hover:text-white transition-colors"
|
||||
aria-label="Discord"
|
||||
>
|
||||
<svg
|
||||
className="w-5 h-5"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.946 2.418-2.157 2.418z" />
|
||||
</svg>
|
||||
</a>
|
||||
|
|
@ -92,10 +82,7 @@ export function Navigation() {
|
|||
</div>
|
||||
|
||||
{/* Mobile menu button */}
|
||||
<button
|
||||
className="md:hidden text-zinc-400 hover:text-white p-2 transition-colors"
|
||||
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
|
||||
>
|
||||
<button className="md:hidden text-zinc-400 hover:text-white p-2 transition-colors" onClick={() => setMobileMenuOpen(!mobileMenuOpen)}>
|
||||
{mobileMenuOpen ? <X className="w-6 h-6" /> : <Menu className="w-6 h-6" />}
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -127,12 +114,7 @@ export function Navigation() {
|
|||
onClick={() => setMobileMenuOpen(false)}
|
||||
aria-label="Discord"
|
||||
>
|
||||
<svg
|
||||
className="w-5 h-5"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.946 2.418-2.157 2.418z" />
|
||||
</svg>
|
||||
<span className="font-medium">Discord</span>
|
||||
|
|
|
|||
|
|
@ -1,29 +1,28 @@
|
|||
'use client';
|
||||
"use client";
|
||||
|
||||
import { motion } from 'framer-motion';
|
||||
import { Shield, Layers, Database, X, Check } from 'lucide-react';
|
||||
import { motion } from "framer-motion";
|
||||
import { Shield, Layers, Database, X, Check } from "lucide-react";
|
||||
|
||||
const frictions = [
|
||||
{
|
||||
icon: Shield,
|
||||
title: 'Coding Agents Need Sandboxes',
|
||||
title: "Coding Agents Need Sandboxes",
|
||||
problem:
|
||||
"You can't let AI execute arbitrary code on your production servers. Coding agents need isolated environments, but existing SDKs assume local execution.",
|
||||
solution: 'A server that runs inside the sandbox and exposes HTTP/SSE.',
|
||||
solution: "A server that runs inside the sandbox and exposes HTTP/SSE.",
|
||||
},
|
||||
{
|
||||
icon: Layers,
|
||||
title: 'Every Coding Agent is Different',
|
||||
title: "Every Coding Agent is Different",
|
||||
problem:
|
||||
'Claude Code, Codex, OpenCode, Amp, and Pi each have proprietary APIs, event formats, and behaviors. Swapping coding agents means rewriting your entire integration.',
|
||||
solution: 'One HTTP API. Write your code once, swap coding agents with a config change.',
|
||||
"Claude Code, Codex, OpenCode, Amp, and Pi each have proprietary APIs, event formats, and behaviors. Swapping coding agents means rewriting your entire integration.",
|
||||
solution: "One HTTP API. Write your code once, swap coding agents with a config change.",
|
||||
},
|
||||
{
|
||||
icon: Database,
|
||||
title: 'Sessions Are Ephemeral',
|
||||
problem:
|
||||
'Coding agent transcripts live in the sandbox. When the process ends, you lose everything. Debugging and replay become impossible.',
|
||||
solution: 'Universal event schema streams to your storage. Persist to Postgres or Rivet, replay later, audit everything.',
|
||||
title: "Sessions Are Ephemeral",
|
||||
problem: "Coding agent transcripts live in the sandbox. When the process ends, you lose everything. Debugging and replay become impossible.",
|
||||
solution: "Universal event schema streams to your storage. Persist to Postgres or Rivet, replay later, audit everything.",
|
||||
},
|
||||
];
|
||||
|
||||
|
|
@ -48,7 +47,8 @@ export function PainPoints() {
|
|||
transition={{ duration: 0.5, delay: 0.1 }}
|
||||
className="max-w-2xl text-base leading-relaxed text-zinc-500"
|
||||
>
|
||||
The Sandbox Agent SDK is a server that runs inside your sandbox. Your app connects remotely to control Claude Code, Codex, OpenCode, Amp, or Pi — streaming events, handling permissions, managing sessions.
|
||||
The Sandbox Agent SDK is a server that runs inside your sandbox. Your app connects remotely to control Claude Code, Codex, OpenCode, Amp, or Pi —
|
||||
streaming events, handling permissions, managing sessions.
|
||||
</motion.p>
|
||||
</div>
|
||||
|
||||
|
|
@ -70,18 +70,14 @@ export function PainPoints() {
|
|||
<X className="h-3 w-3 text-zinc-600" />
|
||||
<span className="text-[10px] font-medium uppercase tracking-wider text-zinc-600">Problem</span>
|
||||
</div>
|
||||
<p className="text-sm leading-relaxed text-zinc-500">
|
||||
{friction.problem}
|
||||
</p>
|
||||
<p className="text-sm leading-relaxed text-zinc-500">{friction.problem}</p>
|
||||
</div>
|
||||
<div className="mt-auto border-t border-white/5 pt-4">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<Check className="h-3 w-3 text-green-400" />
|
||||
<span className="text-[10px] font-medium uppercase tracking-wider text-zinc-400">Solution</span>
|
||||
</div>
|
||||
<p className="text-sm leading-relaxed text-zinc-300">
|
||||
{friction.solution}
|
||||
</p>
|
||||
<p className="text-sm leading-relaxed text-zinc-300">{friction.solution}</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
|
|
|||
|
|
@ -1,26 +1,26 @@
|
|||
'use client';
|
||||
"use client";
|
||||
|
||||
import { Workflow, Database, Server } from 'lucide-react';
|
||||
import { FeatureIcon } from './ui/FeatureIcon';
|
||||
import { Workflow, Database, Server } from "lucide-react";
|
||||
import { FeatureIcon } from "./ui/FeatureIcon";
|
||||
|
||||
const problems = [
|
||||
{
|
||||
title: 'Universal Agent API',
|
||||
desc: 'Claude Code, Codex, OpenCode, Amp, and Pi each have different APIs. We provide a single interface to control them all.',
|
||||
title: "Universal Agent API",
|
||||
desc: "Claude Code, Codex, OpenCode, Amp, and Pi each have different APIs. We provide a single interface to control them all.",
|
||||
icon: Workflow,
|
||||
color: 'text-accent',
|
||||
color: "text-accent",
|
||||
},
|
||||
{
|
||||
title: 'Universal Transcripts',
|
||||
desc: 'Every agent has its own event format. Our universal schema normalizes them all — stream, store, and replay with ease.',
|
||||
title: "Universal Transcripts",
|
||||
desc: "Every agent has its own event format. Our universal schema normalizes them all — stream, store, and replay with ease.",
|
||||
icon: Database,
|
||||
color: 'text-purple-400',
|
||||
color: "text-purple-400",
|
||||
},
|
||||
{
|
||||
title: 'Run Anywhere',
|
||||
desc: 'Lightweight Rust daemon runs locally or in any environment. One command to bridge coding agents to your system.',
|
||||
title: "Run Anywhere",
|
||||
desc: "Lightweight Rust daemon runs locally or in any environment. One command to bridge coding agents to your system.",
|
||||
icon: Server,
|
||||
color: 'text-green-400',
|
||||
color: "text-green-400",
|
||||
},
|
||||
];
|
||||
|
||||
|
|
@ -30,17 +30,12 @@ export function ProblemsSolved() {
|
|||
<div className="max-w-7xl mx-auto px-6">
|
||||
<div className="text-center mb-16">
|
||||
<h2 className="text-3xl font-bold text-white mb-4">Why Coding Agent SDK?</h2>
|
||||
<p className="text-zinc-400 max-w-xl mx-auto">
|
||||
Solving the three fundamental friction points of agentic software development.
|
||||
</p>
|
||||
<p className="text-zinc-400 max-w-xl mx-auto">Solving the three fundamental friction points of agentic software development.</p>
|
||||
</div>
|
||||
|
||||
<div className="grid md:grid-cols-3 gap-8">
|
||||
{problems.map((item, idx) => (
|
||||
<div
|
||||
key={idx}
|
||||
className="group p-8 rounded-2xl bg-zinc-900/40 border border-white/5 hover:border-accent/30 transition-all duration-300"
|
||||
>
|
||||
<div key={idx} className="group p-8 rounded-2xl bg-zinc-900/40 border border-white/5 hover:border-accent/30 transition-all duration-300">
|
||||
<FeatureIcon icon={item.icon} color={item.color} />
|
||||
<h3 className="text-xl font-bold text-white mb-3">{item.title}</h3>
|
||||
<p className="text-zinc-400 text-sm leading-relaxed">{item.desc}</p>
|
||||
|
|
|
|||
|
|
@ -3,9 +3,5 @@ interface BadgeProps {
|
|||
}
|
||||
|
||||
export function Badge({ children }: BadgeProps) {
|
||||
return (
|
||||
<span className="inline-flex px-3 py-1 rounded-full bg-accent/10 border border-accent/20 text-accent text-xs font-mono font-medium">
|
||||
{children}
|
||||
</span>
|
||||
);
|
||||
return <span className="inline-flex px-3 py-1 rounded-full bg-accent/10 border border-accent/20 text-accent text-xs font-mono font-medium">{children}</span>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,34 +1,27 @@
|
|||
import type { ReactNode } from 'react';
|
||||
import type { ReactNode } from "react";
|
||||
|
||||
interface ButtonProps {
|
||||
children: ReactNode;
|
||||
variant?: 'primary' | 'secondary' | 'ghost';
|
||||
size?: 'sm' | 'md' | 'lg';
|
||||
variant?: "primary" | "secondary" | "ghost";
|
||||
size?: "sm" | "md" | "lg";
|
||||
href?: string;
|
||||
onClick?: () => void;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function Button({
|
||||
children,
|
||||
variant = 'primary',
|
||||
size = 'md',
|
||||
href,
|
||||
onClick,
|
||||
className = ''
|
||||
}: ButtonProps) {
|
||||
const baseStyles = 'inline-flex items-center justify-center font-bold rounded-lg transition-all';
|
||||
export function Button({ children, variant = "primary", size = "md", href, onClick, className = "" }: ButtonProps) {
|
||||
const baseStyles = "inline-flex items-center justify-center font-bold rounded-lg transition-all";
|
||||
|
||||
const variants = {
|
||||
primary: 'bg-white text-black hover:bg-zinc-200',
|
||||
secondary: 'bg-zinc-900 border border-white/10 text-white hover:bg-zinc-800',
|
||||
ghost: 'text-zinc-400 hover:text-white',
|
||||
primary: "bg-white text-black hover:bg-zinc-200",
|
||||
secondary: "bg-zinc-900 border border-white/10 text-white hover:bg-zinc-800",
|
||||
ghost: "text-zinc-400 hover:text-white",
|
||||
};
|
||||
|
||||
const sizes = {
|
||||
sm: 'h-9 px-4 text-sm',
|
||||
md: 'h-12 px-8 text-sm',
|
||||
lg: 'h-14 px-10 text-base',
|
||||
sm: "h-9 px-4 text-sm",
|
||||
md: "h-12 px-8 text-sm",
|
||||
lg: "h-14 px-10 text-base",
|
||||
};
|
||||
|
||||
const classes = `${baseStyles} ${variants[variant]} ${sizes[size]} ${className}`;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
'use client';
|
||||
"use client";
|
||||
|
||||
import { useState } from 'react';
|
||||
import { Copy, CheckCircle2 } from 'lucide-react';
|
||||
import { useState } from "react";
|
||||
import { Copy, CheckCircle2 } from "lucide-react";
|
||||
|
||||
interface CopyButtonProps {
|
||||
text: string;
|
||||
|
|
@ -17,16 +17,8 @@ export function CopyButton({ text }: CopyButtonProps) {
|
|||
};
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={handleCopy}
|
||||
className="p-2 hover:bg-white/10 rounded-md transition-colors text-zinc-500 hover:text-white"
|
||||
aria-label="Copy to clipboard"
|
||||
>
|
||||
{copied ? (
|
||||
<CheckCircle2 className="w-4 h-4 text-green-400" />
|
||||
) : (
|
||||
<Copy className="w-4 h-4" />
|
||||
)}
|
||||
<button onClick={handleCopy} className="p-2 hover:bg-white/10 rounded-md transition-colors text-zinc-500 hover:text-white" aria-label="Copy to clipboard">
|
||||
{copied ? <CheckCircle2 className="w-4 h-4 text-green-400" /> : <Copy className="w-4 h-4" />}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import type { LucideIcon } from 'lucide-react';
|
||||
import type { LucideIcon } from "lucide-react";
|
||||
|
||||
interface FeatureIconProps {
|
||||
icon: LucideIcon;
|
||||
|
|
@ -8,12 +8,12 @@ interface FeatureIconProps {
|
|||
glowShadow?: string;
|
||||
}
|
||||
|
||||
export function FeatureIcon({
|
||||
icon: Icon,
|
||||
color = 'text-accent',
|
||||
bgColor = 'bg-accent/10',
|
||||
hoverBgColor = 'group-hover:bg-accent/20',
|
||||
glowShadow = 'group-hover:shadow-[0_0_15px_rgba(59,130,246,0.5)]'
|
||||
export function FeatureIcon({
|
||||
icon: Icon,
|
||||
color = "text-accent",
|
||||
bgColor = "bg-accent/10",
|
||||
hoverBgColor = "group-hover:bg-accent/20",
|
||||
glowShadow = "group-hover:shadow-[0_0_15px_rgba(59,130,246,0.5)]",
|
||||
}: FeatureIconProps) {
|
||||
return (
|
||||
<div className={`rounded ${bgColor} p-2 ${color} transition-all duration-500 ${hoverBgColor} ${glowShadow}`}>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue