Compare commits

..

8 Commits

Author SHA1 Message Date
marko-kraemer 653e98475d agents query 2025-04-24 18:43:05 +01:00
marko-kraemer 63f2d8e5ec rem maintenance popup, auth redirect on dashboard, free tier intro again temp 2025-04-24 17:31:19 +01:00
marko-kraemer 0fc3c4a573 print 2025-04-24 17:05:05 +01:00
Adam Cohen Hillel b77243d69d do it 2025-04-24 17:04:04 +01:00
marko-kraemer 38dd730bbc wip 2025-04-24 14:36:50 +01:00
Marko Kraemer f6dd36d08a
Merge pull request #104 from kortix-ai/fix_streaming
fix empty uuid
2025-04-24 05:56:28 -07:00
LE Quoc Dat d7b95b24a7 fix empty uuid 2025-04-24 13:36:56 +01:00
LE Quoc Dat 034ea37745 add models, better logs 2025-04-24 12:46:22 +01:00
16 changed files with 243 additions and 163 deletions

2
.gitignore vendored
View File

@ -179,3 +179,5 @@ state.json
.DS_Store .DS_Store
**/.DS_Store **/.DS_Store
.aider* .aider*
**/.prompts/
**/__pycache__/

View File

@ -52,45 +52,29 @@ Handles data persistence with authentication, user management, conversation hist
## Use Cases ## Use Cases
1. **Competitor Analysis** ([Watch](https://suna.so/use-case/competitor-analysis)) - *"Analyze the market for my next company in the healthcare industry, located in the UK. Give me the major players, their market size, strengths, and weaknesses, and add their website URLs. Once done, generate a PDF report."* 1. **Competitor Analysis** ([Watch](https://www.suna.so/share/5ee791ac-e19c-4986-a61c-6d0659d0e5bc)) - *"Analyze the market for my next company in the healthcare industry, located in the UK. Give me the major players, their market size, strengths, and weaknesses, and add their website URLs. Once done, generate a PDF report."*
2. **VC List** ([Watch](https://suna.so/use-case/vc-list)) - *"Give me the list of the most important VC Funds in the United States based on Assets Under Management. Give me website URLs, and if possible an email to reach them out."* 2. **VC List** ([Watch](https://www.suna.so/share/804d20a3-cf1c-4adb-83bb-0e77cc6adeac)) - *"Give me the list of the most important VC Funds in the United States based on Assets Under Management. Give me website URLs, and if possible an email to reach them out."*
3. **Insurance Policies** ([Watch](https://suna.so/use-case/insurance-policies)) - *"Find me the best insurance policy per pricing for my house, located in Milan, Italy. Scrape Italian companies in the insurance market for houses."* 3. **Looking for Candidates** ([Watch](https://www.suna.so/share/3ae581b0-2db8-4c63-b324-3b8d29762e74)) - *"Go on LinkedIn, and find me 10 profiles available - they are not working right now - for a junior software engineer position, who are located in Munich, Germany. They should have at least one bachelor's degree in Computer Science or anything related to it, and 1-year of experience in any field/role."*
4. **Looking for Candidates** ([Watch](https://suna.so/use-case/looking-for-candidates)) - *"Go on LinkedIn, and find me 10 profiles available - they are not working right now - for a junior software engineer position, who are located in Munich, Germany. They should have at least one bachelor's degree in Computer Science or anything related to it, and 1-year of experience in any field/role."* 4. **Planning Company Trip** ([Watch](https://www.suna.so/share/725e64a0-f1e2-4bb6-8a1f-703c2833fd72)) - *"Generate me a route plan for my company. We should go to California. We'll be in 8 people. Compose the trip from the departure (Paris, France) to the activities we can do considering that the trip will be 7 days long - departure on the 21st of Apr 2025. Check the weather forecast and temperature for the upcoming days, and based on that, you can plan our activities (outdoor vs indoor)."*
5. **Writing Report** ([Watch](https://suna.so/use-case/writing-report)) - *"Write me a detailed report about what's happened in the US stock market in the last 2 weeks. Analyze the S&P 500 trend, and tell me what the market is expecting to see in the upcoming weeks. This is a report analysis for a Bank CFO."* 5. **Working on Excel** ([Watch](https://www.suna.so/share/128f23a4-51cd-42a6-97a0-0b458b32010e)) - *"My company asked me to set up an Excel spreadsheet with all the information about Italian lottery games (Lotto, 10eLotto, and Million Day). Based on that, generate and send me a spreadsheet with all the basic information (public ones)."*
6. **Product Reviews** ([Watch](https://suna.so/use-case/product-reviews)) - *"Go on Amazon, and find me the most common product issues related to the Nespresso Machine - you can find them by reading the reviews. Once done, write me a short report about common issues that could be converted into competitive advantage for a new Nespresso Machine."* 6. **Automate Event Speaker Prospecting** ([Watch](https://www.suna.so/share/7a7592ea-ed44-4c69-bcb5-5f9bb88c188c)) - *"Find 20 AI ethics speakers from Europe who've spoken at conferences in the past year. Scrapes conference sites, cross-references LinkedIn and YouTube, and outputs contact info + talk summaries."*
7. **Game Generation** ([Watch](https://suna.so/use-case/game-generation)) - *"Generate a Mini Game where the Player needs to drive a spaceship and fight against interstellar aliens. The aliens should be green, while the main player should be white. Make it with a 90' style."* 7. **Summarize and Cross-Reference Scientific Papers** ([Watch](https://www.suna.so/share/c2081b3c-786e-4e7c-9bf4-46e9b23bb662)) - *"Research and compare scientific papers talking about Alcohol effects on our bodies during the last 5 years. Generate a report about the most important scientific papers talking about the topic I wrote before."*
8. **Planning Company Trip** ([Watch](https://suna.so/use-case/planning-company-trip)) - *"Generate me a route plan for my company. We should go to California. We'll be in 8 people. Compose the trip from the departure (Paris, France) to the activities we can do considering that the trip will be 7 days long - departure on the 21st of Apr 2025. Check the weather forecast and temperature for the upcoming days, and based on that, you can plan our activities (outdoor vs indoor)."* 8. **Research + First Contact Draft** ([Watch](https://www.suna.so/share/6b6296a6-8683-49e5-9ad0-a32952d12c44)) - *"Research my potential customers (B2B) on LinkedIn. They should be in the clean tech industry. Find their websites and their email addresses. After that, based on the company profile, generate a personalized first contact email where I present my company which is offering consulting services to cleantech companies to maximize their profits and reduce their costs."*
9. **Working on Excel** ([Watch](https://suna.so/use-case/working-on-excel)) - *"My company asked me to set up an Excel spreadsheet with all the information about Italian lottery games (Lotto, 10eLotto, and Million Day). Based on that, generate and send me a spreadsheet with all the basic information (public ones)."* 9. **SEO Analysis** ([Watch](https://www.suna.so/share/43491cb0-cd6c-45f0-880c-66ddc8c4b842)) - *"Based on my website suna.so, generate an SEO report analysis, find top-ranking pages by keyword clusters, and identify topics I'm missing."*
10. **Scraping Databases** ([Watch](https://suna.so/use-case/scraping-databases)) - *"Search for open tender databases (e.g. EU TED, US SAM.gov), find relevant procurement calls in the clean tech industry, summarize requirements, and generate a report about it."* 10. **Generate a Personal Trip** ([Watch](https://www.suna.so/share/37b31907-8349-4f63-b0e5-27ca597ed02a)) - *"Generate a personal trip to London, with departure from Bangkok on the 1st of May. The trip will last 10 days. Find an accommodation in the center of London, with a rating on Google reviews of at least 4.5. Find me interesting outdoor activities to do during the journey. Generate a detailed itinerary plan."*
11. **Automate Event Speaker Prospecting** ([Watch](https://suna.so/use-case/automate-event-speaker-prospecting)) - *"Find 20 AI ethics speakers from Europe who've spoken at conferences in the past year. Scrapes conference sites, cross-references LinkedIn and YouTube, and outputs contact info + talk summaries."* 11. **Recently Funded Startups** ([Watch](https://www.suna.so/share/8b2a897e-985a-4d5e-867b-15239274f764)) - *"Go on Crunchbase, Dealroom, and TechCrunch, filter by Series A funding rounds in the SaaS Finance Space, and build a report with company data, founders, and contact info for outbound sales."*
12. **Summarize and Cross-Reference Scientific Papers** ([Watch](https://suna.so/use-case/summarize-cross-reference-scientific-papers)) - *"Research and compare scientific papers talking about Alcohol effects on our bodies during the last 5 years. Generate a report about the most important scientific papers talking about the topic I wrote before."* 12. **Scrape Forum Discussions** ([Watch](https://www.suna.so/share/7d7a5d93-a20d-48b0-82cc-e9a876e9fd04)) - *"I need to find the best beauty centers in Rome, but I want to find them by using open forums that speak about this topic. Go on Google, and scrape the forums by looking for beauty center discussions located in Rome. Then generate a list of 5 beauty centers with the best comments about them."*
13. **Generating Leads** ([Watch](https://suna.so/use-case/generating-leads)) - *"I need to generate at least 20 B2B leads to reach out for my new AI tool. It's a customer support tool, then I'll need to have companies located in Spain, Barcelona, with 10-50 employees (find a way to get the number of employees). List me their names, websites, size (employees), and contact information if public."*
14. **Research + First Contact Draft** ([Watch](https://suna.so/use-case/research-first-contact-draft)) - *"Research my potential customers (B2B) on LinkedIn. They should be in the clean tech industry. Find their websites and their email addresses. After that, based on the company profile, generate a personalized first contact email where I present my company which is offering consulting services to cleantech companies to maximize their profits and reduce their costs."*
15. **SEO Analysis** ([Watch](https://suna.so/use-case/seo-analysis)) - *"Based on my website suna.so, generate an SEO report analysis, find top-ranking pages by keyword clusters, and identify topics I'm missing."*
16. **Clustering Public Reviews** ([Watch](https://suna.so/use-case/clustering-public-reviews)) - *"Clusterize public reviews for McDonald's by scraping them on public pages like Google Reviews, then generate a detailed report about the most common feedback and reviews (from 5 to 1 star), generate cluster to obtain insights about what can be improved and what is producing good results for the McDonald's brand."*
17. **Generate a Personal Trip** ([Watch](https://suna.so/use-case/generate-personal-trip)) - *"Generate a personal trip to London, with departure from Bangkok on the 1st of May. The trip will last 10 days. Find an accommodation in the center of London, with a rating on Google reviews of at least 4.5. Find me interesting outdoor activities to do during the journey. Generate a detailed itinerary plan."*
18. **Scrape and Monitor Stocks** ([Watch](https://suna.so/use-case/scrape-monitor-stocks)) - *"I want to monitor the 10 biggest public companies in Portugal. Scrape them on the internet, and find the public ones with the price/share available in the last 30 trading days. Generate a report based on the data you find."*
19. **Recently Funded Startups** ([Watch](https://suna.so/use-case/recently-funded-startups)) - *"Go on Crunchbase, Dealroom, and TechCrunch, filter by Series A funding rounds in the SaaS Finance Space, and build a report with company data, founders, and contact info for outbound sales."*
20. **Scrape Forum Discussions** ([Watch](https://suna.so/use-case/scrape-forum-discussions)) - *"I need to find the best beauty centers in Rome, but I want to find them by using open forums that speak about this topic. Go on Google, and scrape the forums by looking for beauty center discussions located in Rome. Then generate a list of 5 beauty centers with the best comments about them."*
## Run Locally / Self-Hosting ## Run Locally / Self-Hosting

View File

@ -34,6 +34,9 @@ MODEL_NAME_ALIASES = {
"sonnet-3.7": "anthropic/claude-3-7-sonnet-latest", "sonnet-3.7": "anthropic/claude-3-7-sonnet-latest",
"gpt-4.1": "openai/gpt-4.1-2025-04-14", "gpt-4.1": "openai/gpt-4.1-2025-04-14",
"gemini-flash-2.5": "openrouter/google/gemini-2.5-flash-preview", "gemini-flash-2.5": "openrouter/google/gemini-2.5-flash-preview",
"grok-3": "xai/grok-3-fast-latest",
"deepseek": "deepseek/deepseek-chat",
"grok-3-mini": "xai/grok-3-mini-fast-beta",
} }
class AgentStartRequest(BaseModel): class AgentStartRequest(BaseModel):
@ -293,7 +296,7 @@ async def get_or_create_project_sandbox(client, project_id: str):
logger.info(f"Creating new sandbox for project {project_id}") logger.info(f"Creating new sandbox for project {project_id}")
sandbox_pass = str(uuid.uuid4()) sandbox_pass = str(uuid.uuid4())
sandbox = create_sandbox(sandbox_pass) sandbox = create_sandbox(sandbox_pass, project_id)
sandbox_id = sandbox.id sandbox_id = sandbox.id
logger.info(f"Created new sandbox {sandbox_id}") logger.info(f"Created new sandbox {sandbox_id}")

View File

@ -123,6 +123,7 @@ class ResponseProcessor:
finish_reason = None finish_reason = None
last_assistant_message_object = None # Store the final saved assistant message object last_assistant_message_object = None # Store the final saved assistant message object
tool_result_message_objects = {} # tool_index -> full saved message object tool_result_message_objects = {} # tool_index -> full saved message object
has_printed_thinking_prefix = False # Flag for printing thinking prefix only once
logger.info(f"Streaming Config: XML={config.xml_tool_calling}, Native={config.native_tool_calling}, " logger.info(f"Streaming Config: XML={config.xml_tool_calling}, Native={config.native_tool_calling}, "
f"Execute on stream={config.execute_on_stream}, Strategy={config.tool_execution_strategy}") f"Execute on stream={config.execute_on_stream}, Strategy={config.tool_execution_strategy}")
@ -156,13 +157,17 @@ class ResponseProcessor:
# Check for and log Anthropic thinking content # Check for and log Anthropic thinking content
if delta and hasattr(delta, 'reasoning_content') and delta.reasoning_content: if delta and hasattr(delta, 'reasoning_content') and delta.reasoning_content:
logger.info(f"[THINKING]: {delta.reasoning_content}") if not has_printed_thinking_prefix:
# print("[THINKING]: ", end='', flush=True)
has_printed_thinking_prefix = True
# print(delta.reasoning_content, end='', flush=True)
# Append reasoning to main content to be saved in the final message # Append reasoning to main content to be saved in the final message
accumulated_content += delta.reasoning_content accumulated_content += delta.reasoning_content
# Process content chunk # Process content chunk
if delta and hasattr(delta, 'content') and delta.content: if delta and hasattr(delta, 'content') and delta.content:
chunk_content = delta.content chunk_content = delta.content
# print(chunk_content, end='', flush=True)
accumulated_content += chunk_content accumulated_content += chunk_content
current_xml_content += chunk_content current_xml_content += chunk_content
@ -281,6 +286,8 @@ class ResponseProcessor:
logger.info("Stopping stream processing after loop due to XML tool call limit") logger.info("Stopping stream processing after loop due to XML tool call limit")
break break
# print() # Add a final newline after the streaming loop finishes
# --- After Streaming Loop --- # --- After Streaming Loop ---
# Wait for pending tool executions from streaming phase # Wait for pending tool executions from streaming phase

View File

@ -113,7 +113,7 @@ app = FastAPI(lifespan=lifespan)
# return await call_next(request) # return await call_next(request)
# Define allowed origins based on environment # Define allowed origins based on environment
allowed_origins = ["https://www.suna.so", "https://suna.so", "https://staging.suna.so"] allowed_origins = ["https://www.suna.so", "https://suna.so", "https://staging.suna.so", "http://localhost:3000"] #"http://localhost:3000"
# Add staging-specific origins # Add staging-specific origins
if config.ENV_MODE == EnvMode.STAGING: if config.ENV_MODE == EnvMode.STAGING:

View File

@ -86,16 +86,16 @@ def start_supervisord_session(sandbox: Sandbox):
logger.error(f"Error starting supervisord session: {str(e)}") logger.error(f"Error starting supervisord session: {str(e)}")
raise e raise e
def create_sandbox(password: str, sandbox_id: str = None): def create_sandbox(password: str, project_id: str = None):
"""Create a new sandbox with all required services configured and running.""" """Create a new sandbox with all required services configured and running."""
logger.debug("Creating new Daytona sandbox environment") logger.debug("Creating new Daytona sandbox environment")
logger.debug("Configuring sandbox with browser-use image and environment variables") logger.debug("Configuring sandbox with browser-use image and environment variables")
labels = None labels = None
if sandbox_id: if project_id:
logger.debug(f"Using sandbox_id as label: {sandbox_id}") logger.debug(f"Using sandbox_id as label: {project_id}")
labels = {'id': sandbox_id} labels = {'id': project_id}
params = CreateSandboxParams( params = CreateSandboxParams(
image="adamcohenhillel/kortix-suna:0.0.20", image="adamcohenhillel/kortix-suna:0.0.20",
@ -114,14 +114,6 @@ def create_sandbox(password: str, sandbox_id: str = None):
"CHROME_DEBUGGING_HOST": "localhost", "CHROME_DEBUGGING_HOST": "localhost",
"CHROME_CDP": "" "CHROME_CDP": ""
}, },
ports=[
6080, # noVNC web interface
5900, # VNC port
5901, # VNC port
9222, # Chrome remote debugging port
8080, # HTTP website port
8002, # The browser api port
],
resources={ resources={
"cpu": 2, "cpu": 2,
"memory": 4, "memory": 4,

View File

@ -5,7 +5,7 @@ from utils.config import config, EnvMode
# Define subscription tiers and their monthly limits (in minutes) # Define subscription tiers and their monthly limits (in minutes)
SUBSCRIPTION_TIERS = { SUBSCRIPTION_TIERS = {
'price_1RGJ9GG6l1KZGqIroxSqgphC': {'name': 'free', 'minutes': 0}, 'price_1RGJ9GG6l1KZGqIroxSqgphC': {'name': 'free', 'minutes': 8},
'price_1RGJ9LG6l1KZGqIrd9pwzeNW': {'name': 'base', 'minutes': 300}, 'price_1RGJ9LG6l1KZGqIrd9pwzeNW': {'name': 'base', 'minutes': 300},
'price_1RGJ9JG6l1KZGqIrVUU4ZRv6': {'name': 'extra', 'minutes': 2400} 'price_1RGJ9JG6l1KZGqIrVUU4ZRv6': {'name': 'extra', 'minutes': 2400}
} }
@ -91,11 +91,11 @@ async def check_billing_status(client, account_id: str) -> Tuple[bool, str, Opti
if not subscription: if not subscription:
subscription = { subscription = {
'price_id': 'price_1RGJ9GG6l1KZGqIroxSqgphC', # Free tier 'price_id': 'price_1RGJ9GG6l1KZGqIroxSqgphC', # Free tier
'plan_name': 'Free' 'plan_name': 'free'
} }
if not subscription or subscription.get('price_id') is None or subscription.get('price_id') == 'price_1RGJ9GG6l1KZGqIroxSqgphC': # if not subscription or subscription.get('price_id') is None or subscription.get('price_id') == 'price_1RGJ9GG6l1KZGqIroxSqgphC':
return False, "You are not subscribed to any plan. Please upgrade your plan to continue.", subscription # return False, "You are not subscribed to any plan. Please upgrade your plan to continue.", subscription
# Get tier info # Get tier info
tier_info = SUBSCRIPTION_TIERS.get(subscription['price_id']) tier_info = SUBSCRIPTION_TIERS.get(subscription['price_id'])

View File

@ -66,12 +66,6 @@ def setup_logger(name: str = 'agentpress') -> logging.Logger:
logging.Logger: Configured logger instance logging.Logger: Configured logger instance
""" """
logger = logging.getLogger(name) logger = logging.getLogger(name)
# Set console logging level based on environment
if config.ENV_MODE == EnvMode.PRODUCTION:
logger.setLevel(logging.WARNING)
else:
logger.setLevel(logging.INFO)
# Create logs directory if it doesn't exist # Create logs directory if it doesn't exist
log_dir = 'logs' log_dir = 'logs'
@ -91,9 +85,8 @@ def setup_logger(name: str = 'agentpress') -> logging.Logger:
# Console handler # Console handler
console_handler = logging.StreamHandler(sys.stdout) console_handler = logging.StreamHandler(sys.stdout)
# Set console logging level based on environment
if config.ENV_MODE == EnvMode.PRODUCTION: if config.ENV_MODE == EnvMode.PRODUCTION:
console_handler.setLevel(logging.INFO) console_handler.setLevel(logging.WARNING)
else: else:
console_handler.setLevel(logging.DEBUG) console_handler.setLevel(logging.DEBUG)

2
frontend/.gitignore vendored
View File

@ -42,3 +42,5 @@ next-env.d.ts
supabase/.branches supabase/.branches
supabase/.temp supabase/.temp
supabase/**/*.env supabase/**/*.env
**/.prompts/
**/__pycache__/

View File

@ -5,7 +5,8 @@ import { Skeleton } from "@/components/ui/skeleton";
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import { Menu } from "lucide-react"; import { Menu } from "lucide-react";
import { ChatInput, ChatInputHandles } from '@/components/thread/chat-input'; import { ChatInput, ChatInputHandles } from '@/components/thread/chat-input';
import { initiateAgent, createThread, addUserMessage, startAgent } from "@/lib/api"; import { initiateAgent, createThread, addUserMessage, startAgent, createProject } from "@/lib/api";
import { generateThreadName } from "@/lib/actions/threads";
import { useIsMobile } from "@/hooks/use-mobile"; import { useIsMobile } from "@/hooks/use-mobile";
import { useSidebar } from "@/components/ui/sidebar"; import { useSidebar } from "@/components/ui/sidebar";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
@ -32,58 +33,70 @@ function DashboardContent() {
const chatInputRef = useRef<ChatInputHandles>(null); const chatInputRef = useRef<ChatInputHandles>(null);
const handleSubmit = async (message: string, options?: { model_name?: string; enable_thinking?: boolean }) => { const handleSubmit = async (message: string, options?: { model_name?: string; enable_thinking?: boolean }) => {
if (!message.trim() && !chatInputRef.current?.getPendingFiles().length || isSubmitting) return; if ((!message.trim() && !(chatInputRef.current?.getPendingFiles().length)) || isSubmitting) return;
setIsSubmitting(true); setIsSubmitting(true);
try { try {
// Check if any files are attached // Check if any files are attached
const files = chatInputRef.current?.getPendingFiles() || []; const files = chatInputRef.current?.getPendingFiles() || [];
// Clear localStorage if this is a successful submission // Clear localStorage if this is a successful submission
localStorage.removeItem(PENDING_PROMPT_KEY); localStorage.removeItem(PENDING_PROMPT_KEY);
if (files.length > 0) { if (files.length > 0) {
// Create a FormData instance // Create a FormData instance
const formData = new FormData(); const formData = new FormData();
// Append the message // Append the message
formData.append('message', message); formData.append('message', message);
// Append all files // Append all files
files.forEach(file => { files.forEach(file => {
formData.append('files', file); formData.append('files', file);
}); });
// Add any additional options // Add any additional options
if (options) { if (options) {
formData.append('options', JSON.stringify(options)); formData.append('options', JSON.stringify(options));
} }
// Call initiateAgent API // Call initiateAgent API
const result = await initiateAgent(formData); const result = await initiateAgent(formData);
console.log('Agent initiated:', result); console.log('Agent initiated with files:', result);
// Navigate to the thread // Navigate to the thread
if (result.thread_id) { if (result.thread_id) {
router.push(`/agents/${result.thread_id}`); router.push(`/agents/${result.thread_id}`);
} }
} else { } else {
// For text-only messages, first create a thread // ---- Text-only messages ----
const thread = await createThread(""); // 1. Generate a project name
const projectName = await generateThreadName(message);
// Then add the user message
// 2. Create the project
// Assuming createProject gets the account_id from the logged-in user
const newProject = await createProject({
name: projectName,
description: "", // Or derive a description if desired
});
// 3. Create the thread using the new project ID
const thread = await createThread(newProject.id); // <-- Pass the actual project ID
// 4. Then add the user message
await addUserMessage(thread.thread_id, message); await addUserMessage(thread.thread_id, message);
// Start the agent on this thread with the options // 5. Start the agent on this thread with the options
await startAgent(thread.thread_id, options); await startAgent(thread.thread_id, options);
// Navigate to thread // 6. Navigate to thread
router.push(`/agents/${thread.thread_id}`); router.push(`/agents/${thread.thread_id}`);
} }
} catch (error: any) { } catch (error: any) {
// Log line 85 might be here if createThread or initiateAgent fails
console.error('Error creating thread or initiating agent:', error); console.error('Error creating thread or initiating agent:', error);
// Skip billing error checks in local development mode // Skip billing error checks in local development mode
if (isLocalMode()) { if (isLocalMode()) {
console.log("Running in local development mode - billing checks are disabled"); console.log("Running in local development mode - billing checks are disabled");
@ -91,17 +104,17 @@ function DashboardContent() {
// Check specifically for billing errors (402 Payment Required) // Check specifically for billing errors (402 Payment Required)
if (error.message?.includes('(402)') || error?.status === 402) { if (error.message?.includes('(402)') || error?.status === 402) {
console.log("Billing error detected:", error); console.log("Billing error detected:", error);
// Try to extract the error details from the error object // Try to extract the error details from the error object
try { try {
// Try to parse the error.response or the error itself // Try to parse the error.response or the error itself
let errorDetails; let errorDetails;
// First attempt: check if error.data exists and has a detail property // First attempt: check if error.data exists and has a detail property
if (error.data?.detail) { if (error.data?.detail) {
errorDetails = error.data.detail; errorDetails = error.data.detail;
console.log("Extracted billing error details from error.data.detail:", errorDetails); console.log("Extracted billing error details from error.data.detail:", errorDetails);
} }
// Second attempt: check if error.detail exists directly // Second attempt: check if error.detail exists directly
else if (error.detail) { else if (error.detail) {
errorDetails = error.detail; errorDetails = error.detail;
@ -120,7 +133,7 @@ function DashboardContent() {
console.log("Error text is not valid JSON"); console.log("Error text is not valid JSON");
} }
} }
// If we still don't have details, try to extract from the error message // If we still don't have details, try to extract from the error message
if (!errorDetails && error.message) { if (!errorDetails && error.message) {
const match = error.message.match(/Monthly limit of (\d+) minutes reached/); const match = error.message.match(/Monthly limit of (\d+) minutes reached/);
@ -138,7 +151,7 @@ function DashboardContent() {
console.log("Extracted billing error details from error message:", errorDetails); console.log("Extracted billing error details from error message:", errorDetails);
} }
} }
// Handle the billing error with the details we extracted // Handle the billing error with the details we extracted
if (errorDetails) { if (errorDetails) {
console.log("Handling billing error with extracted details:", errorDetails); console.log("Handling billing error with extracted details:", errorDetails);
@ -161,19 +174,20 @@ function DashboardContent() {
message: "You've reached your monthly usage limit. Please upgrade your plan." message: "You've reached your monthly usage limit. Please upgrade your plan."
}); });
} }
// Don't rethrow - we've handled this error with the billing alert // Don't rethrow - we've handled this error with the billing alert
setIsSubmitting(false); setIsSubmitting(false); // Stop submission process on billing error
return; // Exit handleSubmit return; // Exit handleSubmit
} }
} }
// Handle other errors or rethrow // Handle other errors or rethrow
// The second log (line 174) might happen here if startAgent fails, for example
toast.error(error.message || "An error occurred"); toast.error(error.message || "An error occurred");
console.error("Error creating agent:", error); console.error("Error creating agent:", error);
setIsSubmitting(false); setIsSubmitting(false); // Reset submitting state on other errors too
} }
// Removed finally block as catch now handles resetting isSubmitting
}; };
// Check for pending prompt in localStorage on mount // Check for pending prompt in localStorage on mount

View File

@ -9,6 +9,9 @@ import {
import { PricingAlert } from "@/components/billing/pricing-alert" import { PricingAlert } from "@/components/billing/pricing-alert"
import { MaintenanceAlert } from "@/components/maintenance-alert" import { MaintenanceAlert } from "@/components/maintenance-alert"
import { useAccounts } from "@/hooks/use-accounts" import { useAccounts } from "@/hooks/use-accounts"
import { useAuth } from "@/components/AuthProvider"
import { useRouter } from "next/navigation"
import { Loader2 } from "lucide-react"
interface DashboardLayoutProps { interface DashboardLayoutProps {
children: React.ReactNode children: React.ReactNode
@ -21,12 +24,35 @@ export default function DashboardLayout({
const [showMaintenanceAlert, setShowMaintenanceAlert] = useState(false) const [showMaintenanceAlert, setShowMaintenanceAlert] = useState(false)
const { data: accounts } = useAccounts() const { data: accounts } = useAccounts()
const personalAccount = accounts?.find(account => account.personal_account) const personalAccount = accounts?.find(account => account.personal_account)
const { user, isLoading } = useAuth()
const router = useRouter()
useEffect(() => { useEffect(() => {
setShowPricingAlert(true) setShowPricingAlert(false)
setShowMaintenanceAlert(false) setShowMaintenanceAlert(false)
}, []) }, [])
// Check authentication status
useEffect(() => {
if (!isLoading && !user) {
router.push('/auth')
}
}, [user, isLoading, router])
// Show loading state while checking auth
if (isLoading) {
return (
<div className="flex items-center justify-center min-h-screen">
<Loader2 className="h-8 w-8 animate-spin text-primary" />
</div>
)
}
// Don't render anything if not authenticated
if (!user) {
return null
}
return ( return (
<SidebarProvider> <SidebarProvider>
<SidebarLeft /> <SidebarLeft />
@ -50,4 +76,4 @@ export default function DashboardLayout({
/> />
</SidebarProvider> </SidebarProvider>
) )
} }

View File

@ -14,7 +14,7 @@ export default function Home() {
<main className="flex flex-col items-center justify-center min-h-screen w-full"> <main className="flex flex-col items-center justify-center min-h-screen w-full">
<div className="w-full divide-y divide-border"> <div className="w-full divide-y divide-border">
<HeroSection /> <HeroSection />
{/* <UseCasesSection /> */} <UseCasesSection />
{/* <CompanyShowcase /> */} {/* <CompanyShowcase /> */}
{/* <BentoSection /> */} {/* <BentoSection /> */}
{/* <QuoteSection /> */} {/* <QuoteSection /> */}

View File

@ -20,8 +20,7 @@ interface UseCase {
export function UseCasesSection() { export function UseCasesSection() {
// Get featured use cases from siteConfig and limit to 8 // Get featured use cases from siteConfig and limit to 8
const featuredUseCases: UseCase[] = (siteConfig.useCases || []) const featuredUseCases: UseCase[] = (siteConfig.useCases || [])
.filter((useCase: UseCase) => useCase.featured) .filter((useCase: UseCase) => useCase.featured);
.slice(0, 8);
return ( return (
<section <section

View File

@ -72,6 +72,12 @@ export function NavAgents() {
const projects = await getProjects() as Project[] const projects = await getProjects() as Project[]
console.log("Projects loaded:", projects.length, projects.map(p => ({ id: p.id, name: p.name }))); console.log("Projects loaded:", projects.length, projects.map(p => ({ id: p.id, name: p.name })));
// If no projects are found, the user might not be logged in
if (projects.length === 0) {
setThreads([])
return
}
// Create a map of projects by ID for faster lookups // Create a map of projects by ID for faster lookups
const projectsById = new Map<string, Project>(); const projectsById = new Map<string, Project>();
projects.forEach(project => { projects.forEach(project => {
@ -113,6 +119,8 @@ export function NavAgents() {
setThreads(sortThreads(threadsWithProjects)) setThreads(sortThreads(threadsWithProjects))
} catch (err) { } catch (err) {
console.error("Error loading threads with projects:", err) console.error("Error loading threads with projects:", err)
// Set empty threads array on error
setThreads([])
} finally { } finally {
if (showLoading) { if (showLoading) {
setIsLoading(false) setIsLoading(false)

View File

@ -294,8 +294,25 @@ export const deleteProject = async (projectId: string): Promise<void> => {
// Thread APIs // Thread APIs
export const getThreads = async (projectId?: string): Promise<Thread[]> => { export const getThreads = async (projectId?: string): Promise<Thread[]> => {
const supabase = createClient(); const supabase = createClient();
// Get the current user's ID to filter threads
const { data: userData, error: userError } = await supabase.auth.getUser();
if (userError) {
console.error('Error getting current user:', userError);
return [];
}
// If no user is logged in, return an empty array
if (!userData.user) {
console.log('[API] No user logged in, returning empty threads array');
return [];
}
let query = supabase.from('threads').select('*'); let query = supabase.from('threads').select('*');
// Always filter by the current user's account ID
query = query.eq('account_id', userData.user.id);
if (projectId) { if (projectId) {
console.log('[API] Filtering threads by project_id:', projectId); console.log('[API] Filtering threads by project_id:', projectId);
query = query.eq('project_id', projectId); query = query.eq('project_id', projectId);

View File

@ -83,9 +83,9 @@ export const siteConfig = {
buttonText: "Hire Suna", buttonText: "Hire Suna",
buttonColor: "bg-secondary text-white", buttonColor: "bg-secondary text-white",
isPopular: false, isPopular: false,
hours: "no free usage at this time", hours: "10 min",
features: [ features: [
"no free usage", "10 minutes",
// "Community support", // "Community support",
// "Single user", // "Single user",
// "Standard response time", // "Standard response time",
@ -1088,26 +1088,10 @@ export const siteConfig = {
}, },
], ],
useCases: [ useCases: [
{
id: "lead-enrichment",
title: "Enrich company leads with LinkedIn data",
description: "Enriched a list of company leads by automatically finding LinkedIn profiles, identifying CEOs and Heads of Sales/Marketing, and generating clear one-line company descriptions.",
category: "research",
featured: true,
icon: (
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9 6.75H7.75C6.64543 6.75 5.75 7.64543 5.75 8.75V17.25C5.75 18.3546 6.64543 19.25 7.75 19.25H16.25C17.3546 19.25 18.25 18.3546 18.25 17.25V8.75C18.25 7.64543 17.3546 6.75 16.25 6.75H15" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
<path d="M14 8.25H10C9.44772 8.25 9 7.80228 9 7.25V5.75C9 5.19772 9.44772 4.75 10 4.75H14C14.5523 4.75 15 5.19772 15 5.75V7.25C15 7.80228 14.5523 8.25 14 8.25Z" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
<path d="M9 12H12M9 15H15" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
),
image: "https://images.unsplash.com/photo-1522204523234-8729aa6e3d5f?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2400&q=80",
url: "https://www.suna.so/share/b289e34f-68af-40aa-8b46-62c2dcefa58a"
},
{ {
id: "competitor-analysis", id: "competitor-analysis",
title: "Healthcare market competitor analysis", title: "Competitor Analysis",
description: "Analyze the UK healthcare industry market, identifying major players with their market size, strengths, weaknesses, and website URLs, compiled into a comprehensive PDF report.", description: "Analyze the market for my next company in the healthcare industry, located in the UK. Give me the major players, their market size, strengths, and weaknesses, and add their website URLs. Once done, generate a PDF report.",
category: "research", category: "research",
featured: true, featured: true,
icon: ( icon: (
@ -1118,12 +1102,12 @@ export const siteConfig = {
</svg> </svg>
), ),
image: "https://images.unsplash.com/photo-1576091160550-2173dba999ef?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2400&q=80", image: "https://images.unsplash.com/photo-1576091160550-2173dba999ef?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2400&q=80",
url: "https://www.suna.so/share/792ab3ea-ab8e-4b26-b7b5-a19e2ea4c96b" url: "https://www.suna.so/share/5ee791ac-e19c-4986-a61c-6d0659d0e5bc"
}, },
{ {
id: "vc-list", id: "vc-list",
title: "Top US VC funds by AUM", title: "VC List",
description: "Comprehensive listing of the most significant venture capital funds in the United States ranked by Assets Under Management, including website URLs and contact information.", description: "Give me the list of the most important VC Funds in the United States based on Assets Under Management. Give me website URLs, and if possible an email to reach them out.",
category: "finance", category: "finance",
featured: true, featured: true,
icon: ( icon: (
@ -1133,29 +1117,14 @@ export const siteConfig = {
</svg> </svg>
), ),
image: "https://images.unsplash.com/photo-1444653614773-995cb1ef9efa?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2400&q=80", image: "https://images.unsplash.com/photo-1444653614773-995cb1ef9efa?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2400&q=80",
url: "https://www.suna.so/share/5645e4ea-3989-4977-8898-30a7e1d3c449" url: "https://www.suna.so/share/804d20a3-cf1c-4adb-83bb-0e77cc6adeac"
},
{
id: "insurance-policy",
title: "Best home insurance in Milan",
description: "Research and comparison of the most cost-effective home insurance policies in Milan, Italy, by comprehensively analyzing offerings from multiple Italian insurance providers.",
category: "research",
featured: false,
icon: (
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M19.25 5.75C19.25 5.19772 18.8023 4.75 18.25 4.75H14C12.8954 4.75 12 5.64543 12 6.75V19.25L12.8284 18.4216C13.5786 17.6714 14.596 17.25 15.6569 17.25H18.25C18.8023 17.25 19.25 16.8023 19.25 16.25V5.75Z" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
<path d="M4.75 5.75C4.75 5.19772 5.19772 4.75 5.75 4.75H10C11.1046 4.75 12 5.64543 12 6.75V19.25L11.1716 18.4216C10.4214 17.6714 9.40401 17.25 8.34315 17.25H5.75C5.19772 17.25 4.75 16.8023 4.75 16.25V5.75Z" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
),
image: "https://images.unsplash.com/photo-1560518883-ce09059eeffa?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2400&q=80",
url: "https://www.suna.so/share/1cf18fc9-3b74-4a76-a654-2d20d48c35ce"
}, },
{ {
id: "candidate-search", id: "candidate-search",
title: "Find available software engineers", title: "Looking for Candidates",
description: "Search for 10 available junior software engineers in Munich with Computer Science degrees and at least 1 year of experience by scanning LinkedIn profiles.", description: "Go on LinkedIn, and find me 10 profiles available - they are not working right now - for a junior software engineer position, who are located in Munich, Germany. They should have at least one bachelor's degree in Computer Science or anything related to it, and 1-year of experience in any field/role.",
category: "recruitment", category: "recruitment",
featured: false, featured: true,
icon: ( icon: (
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M17.25 10C17.25 12.8995 14.8995 15.25 12 15.25C9.10051 15.25 6.75 12.8995 6.75 10C6.75 7.10051 9.10051 4.75 12 4.75C14.8995 4.75 17.25 7.10051 17.25 10Z" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" /> <path d="M17.25 10C17.25 12.8995 14.8995 15.25 12 15.25C9.10051 15.25 6.75 12.8995 6.75 10C6.75 7.10051 9.10051 4.75 12 4.75C14.8995 4.75 17.25 7.10051 17.25 10Z" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
@ -1164,29 +1133,12 @@ export const siteConfig = {
</svg> </svg>
), ),
image: "https://images.unsplash.com/photo-1573496359142-b8d87734a5a2?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2400&q=80", image: "https://images.unsplash.com/photo-1573496359142-b8d87734a5a2?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2400&q=80",
url: "https://www.suna.so/share/2d76dff7-d7fa-4504-b685-befb0b11dd68" url: "https://www.suna.so/share/3ae581b0-2db8-4c63-b324-3b8d29762e74"
},
{
id: "stock-market-report",
title: "US stock market analysis report",
description: "Detailed report on US stock market performance over the past two weeks, including S&P 500 trend analysis and market predictions for a Bank CFO.",
category: "finance",
featured: true,
icon: (
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.75 11.75L10.25 6.25L14.75 10.75L19.25 6.25" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
<path d="M19.25 6.25V19.25" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
<path d="M4.75 6.25V19.25" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
<path d="M4.75 19.25H19.25" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
),
image: "https://images.unsplash.com/photo-1611974789855-9c2a0a7236a3?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2400&q=80",
url: "https://www.suna.so/share/e343e2fb-55d5-4fe2-a6e3-f9e1655eb8e6"
}, },
{ {
id: "company-trip", id: "company-trip",
title: "Plan company trip to California", title: "Planning Company Trip",
description: "Comprehensive 7-day itinerary for 8 people traveling from Paris to California, including weather-dependent indoor and outdoor activities from April 21-28, 2025.", description: "Generate me a route plan for my company. We should go to California. We'll be in 8 people. Compose the trip from the departure (Paris, France) to the activities we can do considering that the trip will be 7 days long - departure on the 21st of Apr 2025.",
category: "travel", category: "travel",
featured: true, featured: true,
icon: ( icon: (
@ -1198,12 +1150,28 @@ export const siteConfig = {
</svg> </svg>
), ),
image: "https://images.unsplash.com/photo-1507525428034-b723cf961d3e?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2400&q=80", image: "https://images.unsplash.com/photo-1507525428034-b723cf961d3e?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2400&q=80",
url: "https://www.suna.so/share/da71b44e-cd20-42d2-8084-f03861376eab" url: "https://www.suna.so/share/725e64a0-f1e2-4bb6-8a1f-703c2833fd72"
},
{
id: "excel-spreadsheet",
title: "Working on Excel",
description: "My company asked me to set up an Excel spreadsheet with all the information about Italian lottery games (Lotto, 10eLotto, and Million Day). Based on that, generate and send me a spreadsheet with all the basic information (public ones).",
category: "data",
featured: true,
icon: (
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.75 6.75C4.75 5.64543 5.64543 4.75 6.75 4.75H17.25C18.3546 4.75 19.25 5.64543 19.25 6.75V17.25C19.25 18.3546 18.3546 19.25 17.25 19.25H6.75C5.64543 19.25 4.75 18.3546 4.75 17.25V6.75Z" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
<path d="M9.75 8.75V19" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
<path d="M5 8.25H19" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
),
image: "https://images.unsplash.com/photo-1532153975070-2e9ab71f1b14?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2400&q=80",
url: "https://www.suna.so/share/128f23a4-51cd-42a6-97a0-0b458b32010e"
}, },
{ {
id: "speaker-prospecting", id: "speaker-prospecting",
title: "Find AI ethics conference speakers", title: "Automate Event Speaker Prospecting",
description: "Discover 20 AI ethics speakers from Europe who have presented at conferences in the past year by scraping event sites and cross-referencing with LinkedIn and YouTube.", description: "Find 20 AI ethics speakers from Europe who've spoken at conferences in the past year. Scrapes conference sites, cross-references LinkedIn and YouTube, and outputs contact info + talk summaries.",
category: "research", category: "research",
featured: true, featured: true,
icon: ( icon: (
@ -1214,12 +1182,12 @@ export const siteConfig = {
</svg> </svg>
), ),
image: "https://images.unsplash.com/photo-1523580494863-6f3031224c94?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2400&q=80", image: "https://images.unsplash.com/photo-1523580494863-6f3031224c94?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2400&q=80",
url: "https://www.suna.so/share/08a65777-41d8-44a7-95a1-ebd979ebb16e" url: "https://www.suna.so/share/7a7592ea-ed44-4c69-bcb5-5f9bb88c188c"
}, },
{ {
id: "scientific-papers", id: "scientific-papers",
title: "Research alcohol effects scientific papers", title: "Summarize and Cross-Reference Scientific Papers",
description: "Comprehensive analysis and comparison of scientific research papers from the last 5 years on alcohol's effects on the human body, presented as a detailed report.", description: "Research and compare scientific papers talking about Alcohol effects on our bodies during the last 5 years. Generate a report about the most important scientific papers talking about the topic I wrote before.",
category: "research", category: "research",
featured: true, featured: true,
icon: ( icon: (
@ -1230,12 +1198,12 @@ export const siteConfig = {
</svg> </svg>
), ),
image: "https://images.unsplash.com/photo-1532153975070-2e9ab71f1b14?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2400&q=80", image: "https://images.unsplash.com/photo-1532153975070-2e9ab71f1b14?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2400&q=80",
url: "https://www.suna.so/share/aa414558-bfdf-4d7c-9b56-37dee72e4610" url: "https://www.suna.so/share/c2081b3c-786e-4e7c-9bf4-46e9b23bb662"
}, },
{ {
id: "lead-generation", id: "lead-generation",
title: "Generate B2B leads in Barcelona", title: "Research + First Contact Draft",
description: "Identify at least 20 potential B2B leads for an AI customer support tool, specifically targeting companies in Barcelona with 10-50 employees, including company names, websites, size, and contact information.", description: "Research my potential customers (B2B) on LinkedIn. They should be in the clean tech industry. Find their websites and their email addresses. After that, based on the company profile, generate a personalized first contact email.",
category: "sales", category: "sales",
featured: true, featured: true,
icon: ( icon: (
@ -1246,7 +1214,72 @@ export const siteConfig = {
</svg> </svg>
), ),
image: "https://images.unsplash.com/photo-1552581234-26160f608093?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2400&q=80", image: "https://images.unsplash.com/photo-1552581234-26160f608093?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2400&q=80",
url: "https://www.suna.so/share/cfd491ea-f3cd-42a9-a775-baff938edcef" url: "https://www.suna.so/share/6b6296a6-8683-49e5-9ad0-a32952d12c44"
},
{
id: "seo-analysis",
title: "SEO Analysis",
description: "Based on my website suna.so, generate an SEO report analysis, find top-ranking pages by keyword clusters, and identify topics I'm missing.",
category: "marketing",
featured: true,
icon: (
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.75 11.75L10.25 6.25L14.75 10.75L19.25 6.25" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
<path d="M19.25 6.25V19.25" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
<path d="M4.75 6.25V19.25" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
<path d="M4.75 19.25H19.25" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
),
image: "https://images.unsplash.com/photo-1611974789855-9c2a0a7236a3?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2400&q=80",
url: "https://www.suna.so/share/43491cb0-cd6c-45f0-880c-66ddc8c4b842"
},
{
id: "personal-trip",
title: "Generate a Personal Trip",
description: "Generate a personal trip to London, with departure from Bangkok on the 1st of May. The trip will last 10 days. Find an accommodation in the center of London, with a rating on Google reviews of at least 4.5.",
category: "travel",
featured: true,
icon: (
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.75 8.75C4.75 7.64543 5.64543 6.75 6.75 6.75H17.25C18.3546 6.75 19.25 7.64543 19.25 8.75V17.25C19.25 18.3546 18.3546 19.25 17.25 19.25H6.75C5.64543 19.25 4.75 18.3546 4.75 17.25V8.75Z" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
<path d="M8 4.75V8.25" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
<path d="M16 4.75V8.25" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
<path d="M7.75 10.75H16.25" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
),
image: "https://images.unsplash.com/photo-1507525428034-b723cf961d3e?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2400&q=80",
url: "https://www.suna.so/share/37b31907-8349-4f63-b0e5-27ca597ed02a"
},
{
id: "funded-startups",
title: "Recently Funded Startups",
description: "Go on Crunchbase, Dealroom, and TechCrunch, filter by Series A funding rounds in the SaaS Finance Space, and build a report with company data, founders, and contact info for outbound sales.",
category: "finance",
featured: true,
icon: (
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 4.75L19.25 9L12 13.25L4.75 9L12 4.75Z" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
<path d="M9.25 11.5L4.75 14L12 18.25L19.25 14L14.6722 11.5" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
),
image: "https://images.unsplash.com/photo-1444653614773-995cb1ef9efa?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2400&q=80",
url: "https://www.suna.so/share/8b2a897e-985a-4d5e-867b-15239274f764"
},
{
id: "scrape-forums",
title: "Scrape Forum Discussions",
description: "I need to find the best beauty centers in Rome, but I want to find them by using open forums that speak about this topic. Go on Google, and scrape the forums by looking for beauty center discussions located in Rome.",
category: "research",
featured: true,
icon: (
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.75 19.2502H18.25C18.8023 19.2502 19.25 18.8025 19.25 18.2502V5.75C19.25 5.19772 18.8023 4.75 18.25 4.75H5.75C5.19772 4.75 4.75 5.19772 4.75 5.75V18.2502C4.75 18.8025 5.19772 19.2502 5.75 19.2502Z" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
<path d="M9.75 8.75C9.75 9.44036 9.19036 10 8.5 10C7.80964 10 7.25 9.44036 7.25 8.75C7.25 8.05964 7.80964 7.5 8.5 7.5C9.19036 7.5 9.75 8.05964 9.75 8.75Z" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
<path d="M19.25 13.75L14.75 9.25L7.25 16.75" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
),
image: "https://images.unsplash.com/photo-1523580494863-6f3031224c94?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2400&q=80",
url: "https://www.suna.so/share/7d7a5d93-a20d-48b0-82cc-e9a876e9fd04"
} }
], ],
}; };