Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 31 additions & 12 deletions .env.local.example
Original file line number Diff line number Diff line change
@@ -1,22 +1,41 @@
# OpenRouter API key for LLM usage
# Sign up at https://openrouter.ai/
# =============================================================================
# API CONFIGURATION
# =============================================================================

# OpenRouter API Key (Required)
# Get your API key at: https://openrouter.ai/
OPENROUTER_API_KEY=

# E2B API key for secure code execution sandbox
# Sign up at https://e2b.dev/
E2B_API_KEY=
# =============================================================================
# TERMINAL EXECUTION SETTINGS
# =============================================================================

# Terminal execution mode: "local" (default) or "sandbox"
# TERMINAL_EXECUTION_MODE=local

# E2B API Key (Optional - only required for "sandbox" mode)
# Get your API key at: https://e2b.dev/
# E2B_API_KEY=

# Model configuration for AI providers
# Agent model used for main chat interactions (default: qwen/qwen3-coder)
# =============================================================================
# AI MODEL CONFIGURATION
# =============================================================================

# Agent model for main chat interactions (default: qwen/qwen3-coder)
# NEXT_PUBLIC_AGENT_MODEL=

# Title generation model - should be lightweight to reduce cost/latency (default: qwen/qwen3-30b-a3)
# Title generation model - lightweight for cost/latency (default: qwen/qwen3-30b-a3)
# NEXT_PUBLIC_TITLE_MODEL=

# WorkOS config for auth (Optional)
# =============================================================================
# AUTHENTICATION (Optional - WorkOS)
# =============================================================================

# WorkOS API credentials
# Get started at: https://workos.com/
# WORKOS_API_KEY='sk_example_123456789'
# WORKOS_CLIENT_ID='client_123456789'
# WORKOS_COOKIE_PASSWORD="<your password>" # generate a secure password here
# WORKOS_COOKIE_PASSWORD="<your password>" # generate a secure password

# configured in the WorkOS dashboard
# NEXT_PUBLIC_WORKOS_REDIRECT_URI="http://localhost:3000/callback"
# WorkOS redirect URI (configure in WorkOS dashboard)
# NEXT_PUBLIC_WORKOS_REDIRECT_URI="http://localhost:3000/callback"
21 changes: 14 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,24 @@ Before running the application, you need to obtain API keys for the following se

2. **Set up environment variables:**
- Copy `.env.local.example` to `.env.local`
- Add your API keys to the `.env.local` file:
- Add the following required configuration:
```
# OpenRouter API key (Required)
# Get your API key at: https://openrouter.ai/
OPENROUTER_API_KEY=your_openrouter_api_key_here
E2B_API_KEY=your_e2b_api_key_here
```
- Optionally customize the AI models:
```
NEXT_PUBLIC_AGENT_MODEL=qwen/qwen3-coder
NEXT_PUBLIC_TITLE_MODEL=qwen/qwen-turbo
```

HackerAI can execute terminal commands locally (default) or in sandbox. For sandbox mode, you'll need an E2B API key:

```
# Switch to sandbox mode (optional)
TERMINAL_EXECUTION_MODE=sandbox

# E2B API key for sandbox execution
# Get your API key at: https://e2b.dev/
E2B_API_KEY=your_e2b_api_key_here
```

3. **Run the development server:**

```bash
Expand Down
15 changes: 12 additions & 3 deletions app/api/chat/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ import { isWorkOSConfigured } from "@/lib/auth-utils";
import { authkit } from "@workos-inc/authkit-nextjs";
import { generateTitleFromUserMessage } from "@/lib/actions";
import { NextRequest } from "next/server";
import type { ChatMode } from "@/types/chat";
import { myProvider } from "@/lib/ai/providers";
import type { ChatMode, ExecutionMode } from "@/types";

// Allow streaming responses up to 300 seconds
export const maxDuration = 300;
Expand All @@ -42,13 +42,22 @@ export async function POST(req: NextRequest) {

const userID = await getUserID();

// Determine execution mode from environment variable
const executionMode: ExecutionMode =
(process.env.TERMINAL_EXECUTION_MODE as ExecutionMode) || "local";

// Truncate messages to stay within token limit (processing is now done on frontend)
const truncatedMessages = truncateMessagesToTokenLimit(messages);

const stream = createUIMessageStream({
execute: async ({ writer }) => {
// Create tools with user context, mode, and writer
const { tools, getSandbox } = createTools(userID, writer, mode);
const { tools, getSandbox } = createTools(
userID,
writer,
mode,
executionMode,
);

// Generate title in parallel if this is the start of a conversation
const titlePromise =
Expand All @@ -74,7 +83,7 @@ export async function POST(req: NextRequest) {

const result = streamText({
model: myProvider.languageModel("agent-model"),
system: systemPrompt(model),
system: systemPrompt(model, executionMode),
messages: convertToModelMessages(truncatedMessages),
tools,
abortSignal: req.signal,
Expand Down
2 changes: 1 addition & 1 deletion app/components/ComputerSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ export const ComputerSidebar: React.FC = () => {
};

return (
<div className="h-full w-full top-0 left-0 md:top-auto md:left-auto md:right-auto z-50 fixed md:relative md:h-full py-3 md:mr-4 flex-shrink-0">
<div className="h-full w-full top-0 left-0 desktop:top-auto desktop:left-auto desktop:right-auto z-50 fixed desktop:relative desktop:h-full py-3 desktop:mr-4 flex-shrink-0">
<div className="h-full w-full">
<div className="shadow-[0px_0px_8px_0px_rgba(0,0,0,0.02)] border border-border/20 dark:border-border flex h-full w-full bg-background rounded-[22px]">
<div className="flex-1 min-w-0 p-4 flex flex-col h-full">
Expand Down
1 change: 1 addition & 0 deletions app/components/MemoizedMarkdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ const MemoizedMarkdownBlock = memo(
href={href}
target="_blank"
rel="noopener noreferrer"
className="text-link hover:text-link/80 hover:underline transition-colors duration-200"
{...props}
>
{children}
Expand Down
Loading