From 091343a132386c2d910e709b4cd16cb4962997c2 Mon Sep 17 00:00:00 2001 From: Siddharth Ganesan <33737564+Sg312@users.noreply.github.com> Date: Mon, 18 Aug 2025 13:57:31 -0700 Subject: [PATCH] fix(copilot): fix origin (#1015) * Fix v1 * Use env var * Lint --- README.md | 7 ++ apps/docs/content/docs/copilot/index.mdx | 94 +++++++++++++++++++++ apps/docs/content/docs/copilot/meta.json | 4 + apps/docs/content/docs/meta.json | 2 + apps/sim/app/api/copilot/chat/route.test.ts | 5 ++ apps/sim/app/api/copilot/chat/route.ts | 20 +++++ apps/sim/app/api/copilot/methods/route.ts | 4 +- apps/sim/lib/copilot/utils.ts | 19 +++++ 8 files changed, 153 insertions(+), 2 deletions(-) create mode 100644 apps/docs/content/docs/copilot/index.mdx create mode 100644 apps/docs/content/docs/copilot/meta.json diff --git a/README.md b/README.md index be3b0ec97c..13dce5253f 100644 --- a/README.md +++ b/README.md @@ -158,6 +158,13 @@ cd apps/sim bun run dev:sockets ``` +## Copilot API Keys + +Copilot is a Sim-managed service. To use Copilot on a self-hosted instance: + +- Go to https://sim.ai → Settings → Copilot and generate a Copilot API key +- Set `COPILOT_API_KEY` in your self-hosted environment to that value + ## Tech Stack - **Framework**: [Next.js](https://nextjs.org/) (App Router) diff --git a/apps/docs/content/docs/copilot/index.mdx b/apps/docs/content/docs/copilot/index.mdx new file mode 100644 index 0000000000..e4017816bf --- /dev/null +++ b/apps/docs/content/docs/copilot/index.mdx @@ -0,0 +1,94 @@ +--- +title: Copilot +description: Build and edit workflows with Sim Copilot +--- + +import { Callout } from 'fumadocs-ui/components/callout' +import { Card, Cards } from 'fumadocs-ui/components/card' +import { MessageCircle, Package, Zap, Infinity as InfinityIcon, Brain, BrainCircuit } from 'lucide-react' + +## What is Copilot + +Copilot is your in-editor assistant that helps you build, understand, and improve workflows. It can: + +- **Explain**: Answer questions about Sim and your current workflow +- **Guide**: Suggest edits and best practices +- **Edit**: Make changes to blocks, connections, and settings when you approve + + + Copilot is a Sim-managed service. For self-hosted deployments, generate a Copilot API key in the hosted app (sim.ai → Settings → Copilot) and set `COPILOT_API_KEY` in your environment. + + +## Modes + + + +
+ + + +
+

+ Q&A mode for explanations, guidance, and suggestions without making changes to your workflow. +

+
+
+
+ +
+ + + +
+

+ Build-and-edit mode. Copilot proposes specific edits (add blocks, wire variables, tweak settings) and applies them when you approve. +

+
+
+
+
+ +## Depth Levels + + + +
+ + + +
+

Quickest and cheapest. Best for small edits, simple workflows, and minor tweaks.

+
+
+
+ +
+ + + +
+

Balanced speed and reasoning. Recommended default for most tasks.

+
+
+
+ +
+ + + +
+

More reasoning for larger workflows and complex edits while staying performant.

+
+
+
+ +
+ + + +
+

Maximum reasoning for deep planning, debugging, and complex architectural changes.

+
+
+
+
\ No newline at end of file diff --git a/apps/docs/content/docs/copilot/meta.json b/apps/docs/content/docs/copilot/meta.json new file mode 100644 index 0000000000..5d83f94d73 --- /dev/null +++ b/apps/docs/content/docs/copilot/meta.json @@ -0,0 +1,4 @@ +{ + "title": "Copilot", + "pages": ["index"] +} diff --git a/apps/docs/content/docs/meta.json b/apps/docs/content/docs/meta.json index e62d335a2f..1aa9ce7084 100644 --- a/apps/docs/content/docs/meta.json +++ b/apps/docs/content/docs/meta.json @@ -12,6 +12,8 @@ "connections", "---Execution---", "execution", + "---Copilot---", + "copilot", "---Advanced---", "./variables/index", "yaml", diff --git a/apps/sim/app/api/copilot/chat/route.test.ts b/apps/sim/app/api/copilot/chat/route.test.ts index e3ad25c534..5206d7167a 100644 --- a/apps/sim/app/api/copilot/chat/route.test.ts +++ b/apps/sim/app/api/copilot/chat/route.test.ts @@ -105,6 +105,7 @@ describe('Copilot Chat API Route', () => { env: { SIM_AGENT_API_URL: 'http://localhost:8000', COPILOT_API_KEY: 'test-sim-agent-key', + BETTER_AUTH_URL: 'http://localhost:3000', }, })) @@ -225,6 +226,7 @@ describe('Copilot Chat API Route', () => { mode: 'agent', provider: 'openai', depth: 0, + origin: 'http://localhost:3000', }), }) ) @@ -288,6 +290,7 @@ describe('Copilot Chat API Route', () => { mode: 'agent', provider: 'openai', depth: 0, + origin: 'http://localhost:3000', }), }) ) @@ -343,6 +346,7 @@ describe('Copilot Chat API Route', () => { mode: 'agent', provider: 'openai', depth: 0, + origin: 'http://localhost:3000', }), }) ) @@ -438,6 +442,7 @@ describe('Copilot Chat API Route', () => { mode: 'ask', provider: 'openai', depth: 0, + origin: 'http://localhost:3000', }), }) ) diff --git a/apps/sim/app/api/copilot/chat/route.ts b/apps/sim/app/api/copilot/chat/route.ts index b13ed91667..925e876d55 100644 --- a/apps/sim/app/api/copilot/chat/route.ts +++ b/apps/sim/app/api/copilot/chat/route.ts @@ -28,6 +28,15 @@ const logger = createLogger('CopilotChatAPI') // Sim Agent API configuration const SIM_AGENT_API_URL = env.SIM_AGENT_API_URL || SIM_AGENT_API_URL_DEFAULT +function getRequestOrigin(_req: NextRequest): string { + try { + // Strictly use configured Better Auth URL + return env.BETTER_AUTH_URL || '' + } catch (_) { + return '' + } +} + function deriveKey(keyString: string): Buffer { return createHash('sha256').update(keyString, 'utf8').digest() } @@ -197,6 +206,14 @@ export async function POST(req: NextRequest) { conversationId, } = ChatMessageSchema.parse(body) + // Derive request origin for downstream service + const requestOrigin = getRequestOrigin(req) + + if (!requestOrigin) { + logger.error(`[${tracker.requestId}] Missing required configuration: BETTER_AUTH_URL`) + return createInternalServerErrorResponse('Missing required configuration: BETTER_AUTH_URL') + } + logger.info(`[${tracker.requestId}] Processing copilot chat request`, { userId: authenticatedUserId, workflowId, @@ -209,6 +226,7 @@ export async function POST(req: NextRequest) { provider: provider || 'openai', hasConversationId: !!conversationId, depth, + origin: requestOrigin, }) // Handle chat context @@ -386,6 +404,7 @@ export async function POST(req: NextRequest) { ...(effectiveConversationId ? { conversationId: effectiveConversationId } : {}), ...(typeof depth === 'number' ? { depth } : {}), ...(session?.user?.name && { userName: session.user.name }), + ...(requestOrigin ? { origin: requestOrigin } : {}), } // Log the payload being sent to the streaming endpoint @@ -399,6 +418,7 @@ export async function POST(req: NextRequest) { hasConversationId: !!effectiveConversationId, depth: typeof depth === 'number' ? depth : undefined, messagesCount: requestPayload.messages.length, + ...(requestOrigin ? { origin: requestOrigin } : {}), }) // Full payload as JSON string logger.info( diff --git a/apps/sim/app/api/copilot/methods/route.ts b/apps/sim/app/api/copilot/methods/route.ts index bde914dbf4..a0b989e4bc 100644 --- a/apps/sim/app/api/copilot/methods/route.ts +++ b/apps/sim/app/api/copilot/methods/route.ts @@ -2,7 +2,7 @@ import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { copilotToolRegistry } from '@/lib/copilot/tools/server-tools/registry' import type { NotificationStatus } from '@/lib/copilot/types' -import { checkInternalApiKey } from '@/lib/copilot/utils' +import { checkCopilotApiKey, checkInternalApiKey } from '@/lib/copilot/utils' import { createLogger } from '@/lib/logs/console/logger' import { getRedisClient } from '@/lib/redis' import { createErrorResponse } from '@/app/api/copilot/methods/utils' @@ -233,7 +233,7 @@ export async function POST(req: NextRequest) { try { // Check authentication (internal API key) - const authResult = checkInternalApiKey(req) + const authResult = checkInternalApiKey(req) || checkCopilotApiKey(req) if (!authResult.success) { return NextResponse.json(createErrorResponse(authResult.error || 'Authentication failed'), { status: 401, diff --git a/apps/sim/lib/copilot/utils.ts b/apps/sim/lib/copilot/utils.ts index 2983582196..07a4254cdc 100644 --- a/apps/sim/lib/copilot/utils.ts +++ b/apps/sim/lib/copilot/utils.ts @@ -19,3 +19,22 @@ export function checkInternalApiKey(req: NextRequest) { return { success: true } } + +export function checkCopilotApiKey(req: NextRequest) { + const apiKey = req.headers.get('x-api-key') + const expectedApiKey = env.COPILOT_API_KEY + + if (!expectedApiKey) { + return { success: false, error: 'Copilot API key not configured' } + } + + if (!apiKey) { + return { success: false, error: 'API key required' } + } + + if (apiKey !== expectedApiKey) { + return { success: false, error: 'Invalid API key' } + } + + return { success: true } +}