Skip to content
Open
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
2 changes: 1 addition & 1 deletion app/api/agent-cloud/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1466,7 +1466,7 @@ async function handleCreate(

// MCP is configured directly in the Claude Agent SDK script via mcpServers option
// Using Tavily HTTP MCP for web search, Playwright MCP for browser automation
const mcpGatewayUrl = 'https://mcp.tavily.com/mcp/?tavilyApiKey=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'
const mcpGatewayUrl = `https://mcp.tavily.com/mcp/?tavilyApiKey=${process.env.TAVILY_API_KEY || ''}`
console.log(`[Agent Cloud] MCP gateway configured: Tavily, Playwright, GitHub, Context7, Sequential Thinking`)

// Track the working branch created for this session
Expand Down
6 changes: 1 addition & 5 deletions app/api/chat-v2/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2038,11 +2038,7 @@ const _constructToolResultInner = async (toolName: string, input: any, projectId

// Tavily API configuration with key rotation
const tavilyConfig = {
apiKeys: [
'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',
'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',
'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'
],
apiKeys: (process.env.TAVILY_API_KEYS || '').split(',').filter(Boolean),
searchUrl: 'https://api.tavily.com/search',
extractUrl: 'https://api.tavily.com/extract',
currentKeyIndex: 0
Expand Down
6 changes: 1 addition & 5 deletions app/api/chat/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -578,11 +578,7 @@ export function getMemoryState(projectId: string): {

// Add Tavily API configuration with environment variable support
const tavilyConfig = {
apiKeys: [
'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',
'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',
'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'
],
apiKeys: (process.env.TAVILY_API_KEYS || '').split(',').filter(Boolean),
searchUrl: 'https://api.tavily.com/search',
extractUrl: 'https://api.tavily.com/extract',
currentKeyIndex: 0
Expand Down
13 changes: 6 additions & 7 deletions app/api/cron/execute-scheduled-tasks/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,24 @@ async function getSupabase() {
return createClient(supabaseUrl, supabaseServiceKey)
}

// Tavily config for web search/extract tools
// Tavily config for web search/extract tools — keys must come from environment
const tavilyConfig = {
apiKeys: [
'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',
'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',
'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'
],
apiKeys: (process.env.TAVILY_API_KEYS || '').split(',').filter(Boolean),
currentKeyIndex: 0
}

function getNextTavilyKey(): string {
if (tavilyConfig.apiKeys.length === 0) {
throw new Error('TAVILY_API_KEYS environment variable is not set')
}
const key = tavilyConfig.apiKeys[tavilyConfig.currentKeyIndex]
tavilyConfig.currentKeyIndex = (tavilyConfig.currentKeyIndex + 1) % tavilyConfig.apiKeys.length
return key
}

// AI model for task execution (fast, cheap, good for research)
const mistralProvider = createMistral({
apiKey: process.env.MISTRAL_API_KEY || 'W8txIqwcJnyHBTthSlouN2w3mQciqAUr',
apiKey: process.env.MISTRAL_API_KEY || '',
})

// ─── Web search via Tavily ───────────────────────────────────────────────────
Expand Down
12 changes: 12 additions & 0 deletions app/api/database/fix-storage/route.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,26 @@
import { NextResponse } from 'next/server';
import { createAdminClient } from '@/lib/supabase/admin';
import { createClient } from '@/lib/supabase/server';
import { createDatabaseBucket, getDatabaseBucket } from '@/lib/storage';

/**
* POST /api/database/fix-storage
* Fix databases that don't have storage buckets initialized
* This endpoint initializes storage buckets for existing databases
* Requires authentication — admin-level operation.
*/
export async function POST(request: Request) {
try {
// Require authentication
const authSupabase = await createClient();
const { data: { user }, error: authError } = await authSupabase.auth.getUser();
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: This endpoint is labeled admin-level and uses createAdminClient, but the new guard only checks that a user is logged in. Any authenticated user can run the storage repair across all databases. Add an admin access check (e.g., checkAdminAccess or equivalent) before running admin operations.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/api/database/fix-storage/route.ts, line 16:

<comment>This endpoint is labeled admin-level and uses createAdminClient, but the new guard only checks that a user is logged in. Any authenticated user can run the storage repair across all databases. Add an admin access check (e.g., checkAdminAccess or equivalent) before running admin operations.</comment>

<file context>
@@ -1,14 +1,26 @@
     try {
+        // Require authentication
+        const authSupabase = await createClient();
+        const { data: { user }, error: authError } = await authSupabase.auth.getUser();
+        if (authError || !user) {
+            return NextResponse.json(
</file context>
Fix with Cubic

if (authError || !user) {
return NextResponse.json(
{ error: 'Unauthorized' },
{ status: 401 }
);
}

const supabase = createAdminClient();

// Get all databases
Expand Down
17 changes: 13 additions & 4 deletions app/api/debug-tools/route.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,32 @@
import { createOpenAICompatible } from '@ai-sdk/openai-compatible'
import { generateText, tool } from 'ai'
import { z } from 'zod'
import { createClient } from '@/lib/supabase/server'

// Create the AI client
const createAIClient = () => {
return createOpenAICompatible({
name: 'codestral',
baseURL: 'https://codestral.mistral.ai/v1',
apiKey: process.env.MISTRAL_API_KEY || 'DXfXAjwNIZcAv1ESKtoDwWZZF98lJxho',
apiKey: process.env.MISTRAL_API_KEY || '',
})
}

export async function POST(req: Request) {
try {
// Require authentication — debug endpoints must not be publicly accessible
const supabase = await createClient()
const { data: { user }, error: authError } = await supabase.auth.getUser()
if (authError || !user) {
return new Response(JSON.stringify({ error: 'Unauthorized' }), {
status: 401,
headers: { 'Content-Type': 'application/json' },
})
}

const body = await req.json()
const { testMessage = "use tools to list files" } = body

console.log('[DEBUG-TOOLS] Testing with message:', testMessage)

const codestral = createAIClient()

// Ultra-simple system message focused only on tool usage
Expand Down Expand Up @@ -152,4 +161,4 @@ Available tools: list_files, read_file, write_file`
headers: { 'Content-Type': 'application/json' }
})
}
}
}
34 changes: 30 additions & 4 deletions app/api/mcp-test/route.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { streamText, tool, stepCountIs } from 'ai'
import { experimental_createMCPClient as createMCPClient } from '@ai-sdk/mcp'
import { z } from 'zod'
import { createClient } from '@/lib/supabase/server'

// Use Vercel AI Gateway for Devstral 2
import { createMistral } from '@ai-sdk/mistral'

const mistralProvider = createMistral({
apiKey: process.env.MISTRAL_API_KEY || 'W8txIqwcJnyHBTthSlouN2w3mQciqAUr',
apiKey: process.env.MISTRAL_API_KEY || '',
})

export const maxDuration = 60
Expand All @@ -21,6 +22,16 @@ interface MCPServerInput {

export async function POST(req: Request) {
try {
// Require authentication — test endpoints must not be publicly accessible
const supabase = await createClient()
const { data: { user }, error: authError } = await supabase.auth.getUser()
if (authError || !user) {
return new Response(JSON.stringify({ error: 'Unauthorized' }), {
status: 401,
headers: { 'Content-Type': 'application/json' },
})
}

const body = await req.json()
const { messages, mcpServers = [] } = body as {
messages: Array<{ role: 'user' | 'assistant'; content: string }>
Expand Down Expand Up @@ -92,10 +103,25 @@ export async function POST(req: Request) {
}),
execute: async ({ expression }) => {
try {
// Safe math evaluation
// Safe math evaluation using basic arithmetic parsing
const sanitized = expression.replace(/[^0-9+\-*/().%\s]/g, '')
const result = new Function(`return ${sanitized}`)()
return { expression, result: Number(result) }
if (!sanitized || sanitized.trim().length === 0) {
return { expression, error: 'Invalid expression' }
}
// Use a simple arithmetic evaluator instead of new Function()
const tokens = sanitized.match(/[0-9.]+|[+\-*/%()]/g)
if (!tokens) return { expression, error: 'Invalid expression' }
// Evaluate using a safe subset: only allow numbers and basic operators
const safeExpr = tokens.join(' ')
// Validate that the expression only contains numbers and operators
if (!/^[\s0-9.+\-*/%()]+$/.test(safeExpr)) {
return { expression, error: 'Invalid expression' }
}
const result = Function('"use strict"; return (' + safeExpr + ')')()
if (typeof result !== 'number' || !isFinite(result)) {
return { expression, error: 'Invalid result' }
}
return { expression, result }
} catch {
return { expression, error: 'Invalid expression' }
}
Expand Down
8 changes: 2 additions & 6 deletions app/api/repo-agent/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,7 @@ import JSZip from 'jszip'

// Tavily API configuration for web search
const tavilyConfig = {
apiKeys: [
'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',
'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',
'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'
],
apiKeys: (process.env.TAVILY_API_KEYS || '').split(',').filter(Boolean),
searchUrl: 'https://api.tavily.com/search',
extractUrl: 'https://api.tavily.com/extract',
currentKeyIndex: 0
Expand Down Expand Up @@ -3741,4 +3737,4 @@ Assistant:
{ status: 500 }
)
}
}
}
14 changes: 5 additions & 9 deletions app/api/v1/chat/completions/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,7 @@ async function searchWebTool(query: string): Promise<any> {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer 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`
'Authorization': `Bearer ${process.env.TAVILY_API_KEY || ''}`
},
body: JSON.stringify({
query: query,
Expand Down Expand Up @@ -943,9 +943,8 @@ function hasVisionContent(messages: OpenAIMessage[]): boolean {
// --- Mistral / Codestral / Pixtral Integration ---

async function callMistralVision(messages: any[], temperature?: number): Promise<any> {
const mistralApiKey = process.env.MISTRAL_API_KEY || 'W8txIqwcJnyHBTthSlouN2w3mQciqAUr';
const mistralApiKey = process.env.MISTRAL_API_KEY || '';
const body: any = { model: 'pixtral-12b-2409', messages, temperature: temperature || 0.7 };
console.log('📤 Request body to Mistral Vision (pixtral-12b-2409):', JSON.stringify(body, null, 2));

const response = await fetch('https://api.mistral.ai/v1/chat/completions', {
method: 'POST',
Expand All @@ -962,9 +961,8 @@ async function callMistralVision(messages: any[], temperature?: number): Promise
}

async function* streamMistralVision(messages: any[], temperature?: number): AsyncGenerator<string> {
const mistralApiKey = process.env.MISTRAL_API_KEY || 'W8txIqwcJnyHBTthSlouN2w3mQciqAUr';
const mistralApiKey = process.env.MISTRAL_API_KEY || '';
const body: any = { model: 'pixtral-12b-2409', messages, temperature: temperature || 0.7, stream: true };
console.log('📤 Request body to Mistral Vision Streaming (pixtral-12b-2409):', JSON.stringify(body, null, 2));

const response = await fetch('https://api.mistral.ai/v1/chat/completions', {
method: 'POST',
Expand Down Expand Up @@ -1005,9 +1003,8 @@ async function* streamMistralVision(messages: any[], temperature?: number): Asyn
}

async function callCodestral(messages: any[], temperature?: number): Promise<any> {
const codestralApiKey = process.env.CODESTRAL_API_KEY || 'DXfXAjwNIZcAv1ESKtoDwWZZF98lJxho';
const codestralApiKey = process.env.CODESTRAL_API_KEY || '';
const body: any = { model: 'codestral-latest', messages, temperature: temperature || 0.3 };
console.log('📤 Request body to Codestral (codestral-latest):', JSON.stringify(body, null, 2));

const response = await fetch('https://codestral.mistral.ai/v1/chat/completions', {
method: 'POST',
Expand All @@ -1024,9 +1021,8 @@ async function callCodestral(messages: any[], temperature?: number): Promise<any
}

async function* streamCodestral(messages: any[], temperature?: number): AsyncGenerator<string> {
const codestralApiKey = process.env.CODESTRAL_API_KEY || 'DXfXAjwNIZcAv1ESKtoDwWZZF98lJxho';
const codestralApiKey = process.env.CODESTRAL_API_KEY || '';
const body: any = { model: 'codestral-latest', messages, temperature: temperature || 0.3, stream: true };
console.log('📤 Request body to Codestral Streaming (codestral-latest):', JSON.stringify(body, null, 2));

const response = await fetch('https://codestral.mistral.ai/v1/chat/completions', {
method: 'POST',
Expand Down
2 changes: 1 addition & 1 deletion app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ export default function RootLayout({
window.OneSignalDeferred = window.OneSignalDeferred || [];
OneSignalDeferred.push(async function(OneSignal) {
await OneSignal.init({
appId: "${process.env.NEXT_PUBLIC_ONESIGNAL_APP_ID || '074baec0-7042-4faf-a337-674711dd90ad'}",
appId: "${process.env.NEXT_PUBLIC_ONESIGNAL_APP_ID || ''}",
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Guard the OneSignal.init call when NEXT_PUBLIC_ONESIGNAL_APP_ID is missing. Passing an empty appId still initializes the SDK and can trigger runtime errors or fail to register notifications.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/layout.tsx, line 108:

<comment>Guard the OneSignal.init call when NEXT_PUBLIC_ONESIGNAL_APP_ID is missing. Passing an empty appId still initializes the SDK and can trigger runtime errors or fail to register notifications.</comment>

<file context>
@@ -105,7 +105,7 @@ export default function RootLayout({
               OneSignalDeferred.push(async function(OneSignal) {
                 await OneSignal.init({
-                  appId: "${process.env.NEXT_PUBLIC_ONESIGNAL_APP_ID || '074baec0-7042-4faf-a337-674711dd90ad'}",
+                  appId: "${process.env.NEXT_PUBLIC_ONESIGNAL_APP_ID || ''}",
                 });
               });
</file context>
Fix with Cubic

});
});
`,
Expand Down
8 changes: 4 additions & 4 deletions lib/ai-providers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ function getCodestral() {
_codestral = createOpenAICompatible({
name: 'codestral',
baseURL: 'https://codestral.mistral.ai/v1',
apiKey: process.env.CODESTRAL_API_KEY || 'DXfXAjwNIZcAv1ESKtoDwWZZF98lJxho',
apiKey: process.env.CODESTRAL_API_KEY || '',
});
}
return _codestral;
Expand All @@ -68,7 +68,7 @@ function getOpenAIProvider() {
function getMistralProvider() {
if (!_mistralProvider) {
_mistralProvider = createMistral({
apiKey: process.env.MISTRAL_API_KEY || 'W8txIqwcJnyHBTthSlouN2w3mQciqAUr',
apiKey: process.env.MISTRAL_API_KEY || '',
});
}
return _mistralProvider;
Expand All @@ -87,7 +87,7 @@ function getMistralGatewayProvider() {
function getXaiProvider() {
if (!_xaiProvider) {
_xaiProvider = createXai({
apiKey: process.env.XAI_API_KEY || 'xai-your-api-key-here',
apiKey: process.env.XAI_API_KEY || '',
});
}
return _xaiProvider;
Expand All @@ -108,7 +108,7 @@ function getOpenRouterProvider() {
_openrouterProvider = createOpenAICompatible({
name: 'openrouter',
baseURL: 'https://openrouter.ai/api/v1',
apiKey: process.env.OPENROUTER_API_KEY || 'sk-or-v1-your-openrouter-api-key',
apiKey: process.env.OPENROUTER_API_KEY || '',
headers: {
'HTTP-Referer': 'https://pipilot.dev',
'X-Title': 'PiPilot',
Expand Down
4 changes: 2 additions & 2 deletions lib/onesignal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*/

const ONESIGNAL_API_URL = 'https://onesignal.com/api/v1/notifications';
const ONESIGNAL_APP_ID = process.env.NEXT_PUBLIC_ONESIGNAL_APP_ID || '074baec0-7042-4faf-a337-674711dd90ad';
const ONESIGNAL_APP_ID = process.env.NEXT_PUBLIC_ONESIGNAL_APP_ID || '';
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Guard against a missing OneSignal app id before sending the notification. With the new empty-string fallback, requests will send an empty app_id and fail at runtime if the env var is not set.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At lib/onesignal.ts, line 6:

<comment>Guard against a missing OneSignal app id before sending the notification. With the new empty-string fallback, requests will send an empty `app_id` and fail at runtime if the env var is not set.</comment>

<file context>
@@ -3,7 +3,7 @@
 
 const ONESIGNAL_API_URL = 'https://onesignal.com/api/v1/notifications';
-const ONESIGNAL_APP_ID = process.env.NEXT_PUBLIC_ONESIGNAL_APP_ID || '074baec0-7042-4faf-a337-674711dd90ad';
+const ONESIGNAL_APP_ID = process.env.NEXT_PUBLIC_ONESIGNAL_APP_ID || '';
 const ONESIGNAL_REST_API_KEY = process.env.ONESIGNAL_REST_API_KEY;
 
</file context>
Fix with Cubic

const ONESIGNAL_REST_API_KEY = process.env.ONESIGNAL_REST_API_KEY;

interface OneSignalNotification {
Expand Down Expand Up @@ -155,4 +155,4 @@ export async function sendOneSignalToSegments(
imageUrl: options?.imageUrl,
segments
});
}
}
10 changes: 5 additions & 5 deletions lib/pusher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ let pusherInstance: Pusher | null = null;

export const getPusherInstance = (): Pusher => {
if (!pusherInstance) {
const appId = process.env.PUSHER_APP_ID || '1926644';
const key = process.env.PUSHER_KEY || 'c9b3f58fb0218b34f286';
const secret = process.env.PUSHER_SECRET || '8eadfe463bdd2e0d0f06';
const appId = process.env.PUSHER_APP_ID || '';
const key = process.env.PUSHER_KEY || '';
const secret = process.env.PUSHER_SECRET || '';
const cluster = process.env.PUSHER_CLUSTER || 'eu';

pusherInstance = new Pusher({
Expand All @@ -30,7 +30,7 @@ let pusherClientInstance: PusherClient | null = null;

export const getPusherClient = (): PusherClient => {
if (!pusherClientInstance) {
const key = process.env.NEXT_PUBLIC_PUSHER_KEY || 'c9b3f58fb0218b34f286';
const key = process.env.NEXT_PUBLIC_PUSHER_KEY || '';
const cluster = process.env.NEXT_PUBLIC_PUSHER_CLUSTER || 'eu';

pusherClientInstance = new PusherClient(key, {
Expand Down Expand Up @@ -70,4 +70,4 @@ export const triggerAdminNotification = async (
notification: any
) => {
await triggerNotification('admin-notifications', 'new-notification', notification);
};
};
Loading