diff --git a/.gitignore b/.gitignore
index db3aff8..486b0ec 100644
--- a/.gitignore
+++ b/.gitignore
@@ -40,3 +40,8 @@ yarn-error.log*
# typescript
*.tsbuildinfo
next-env.d.ts
+
+# local env
+.venv
+error.log
+__pycache__
diff --git a/app/api/generate/route.ts b/app/api/generate/route.ts
index eaffe00..512d13b 100644
--- a/app/api/generate/route.ts
+++ b/app/api/generate/route.ts
@@ -62,10 +62,20 @@ export async function POST(req: NextRequest) {
return NextResponse.json({ images });
} catch (error: any) {
- console.error("Generate API Error:", error);
+ console.error("Generate API Error Details:", {
+ message: error.message,
+ stack: error.stack,
+ fullError: error
+ });
+
+ const msg = error.message || "";
+ const isAuthError = msg.includes("401") || msg.includes("403") ||
+ msg.includes("Auth") || msg.includes("auth") ||
+ msg.includes("cookies") || msg.includes("expired");
+
return NextResponse.json(
{ error: error.message || "Generation failed" },
- { status: 500 }
+ { status: isAuthError ? 401 : 500 }
);
}
}
diff --git a/app/api/grok-chat/route.ts b/app/api/grok-chat/route.ts
new file mode 100644
index 0000000..b863a45
--- /dev/null
+++ b/app/api/grok-chat/route.ts
@@ -0,0 +1,51 @@
+import { NextRequest, NextResponse } from 'next/server';
+
+const CRAWL_SERVICE_URL = 'http://127.0.0.1:8000';
+
+export async function POST(req: NextRequest) {
+ try {
+ const body = await req.json();
+ const { message, history } = body;
+
+ console.log(`[Grok API] Incoming body:`, JSON.stringify(body, null, 2));
+
+ const proxyPayload = {
+ message,
+ history,
+ cookies: body.cookies
+ };
+ console.log(`[Grok API] Proxy payload:`, JSON.stringify(proxyPayload, null, 2));
+
+ const response = await fetch(`${CRAWL_SERVICE_URL}/grok/chat`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(proxyPayload),
+ });
+
+ if (!response.ok) {
+ const errorText = await response.text();
+ console.error(`[Grok API] Service error: ${response.status} ${errorText}`);
+ try {
+ const errorJson = JSON.parse(errorText);
+ return NextResponse.json(errorJson, { status: response.status });
+ } catch {
+ return NextResponse.json(
+ { error: `Service error: ${response.status} - ${errorText}` },
+ { status: response.status }
+ );
+ }
+ }
+
+ const data = await response.json();
+ return NextResponse.json(data);
+
+ } catch (error: any) {
+ console.error('[Grok API] Proxy error:', error);
+ return NextResponse.json(
+ { error: error.message || 'Internal Server Error' },
+ { status: 500 }
+ );
+ }
+}
diff --git a/app/api/grok-debug/route.ts b/app/api/grok-debug/route.ts
new file mode 100644
index 0000000..7215a74
--- /dev/null
+++ b/app/api/grok-debug/route.ts
@@ -0,0 +1,54 @@
+import { NextRequest, NextResponse } from 'next/server';
+
+const CRAWL_SERVICE_URL = 'http://127.0.0.1:8000';
+
+export async function POST(req: NextRequest) {
+ try {
+ const body = await req.json();
+ const { message, history } = body;
+
+ console.log(`[Grok Debug API] Incoming body:`, JSON.stringify(body, null, 2));
+
+ const proxyPayload = {
+ message,
+ history: history || [],
+ cookies: body.cookies || null,
+ user_agent: body.userAgent || null
+ };
+ console.log(`[Grok Debug API] Proxy payload:`, JSON.stringify(proxyPayload, null, 2));
+
+ const response = await fetch(`${CRAWL_SERVICE_URL}/grok/chat`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(proxyPayload),
+ });
+
+ if (!response.ok) {
+ const errorText = await response.text();
+ console.error(`[Grok Debug API] Service error: ${response.status} ${errorText}`);
+ try {
+ // Try to parse detailed JSON error from FastAPI
+ const errorJson = JSON.parse(errorText);
+ return NextResponse.json(errorJson, { status: response.status });
+ } catch {
+ // Fallback to text
+ return NextResponse.json(
+ { error: `Service error: ${response.status} - ${errorText}` },
+ { status: response.status }
+ );
+ }
+ }
+
+ const data = await response.json();
+ return NextResponse.json(data);
+
+ } catch (error: any) {
+ console.error('[Grok Debug API] Internal error:', error);
+ return NextResponse.json(
+ { error: error.message || 'Internal Server Error' },
+ { status: 500 }
+ );
+ }
+}
diff --git a/app/api/meta-crawl/route.ts b/app/api/meta-crawl/route.ts
new file mode 100644
index 0000000..b113d5e
--- /dev/null
+++ b/app/api/meta-crawl/route.ts
@@ -0,0 +1,130 @@
+import { NextRequest, NextResponse } from 'next/server';
+import { MetaCrawlClient } from '@/lib/providers/meta-crawl-client';
+
+/**
+ * API Route: /api/meta-crawl
+ *
+ * Proxies image generation requests to the Crawl4AI Python service
+ * which uses browser automation to interact with Meta AI.
+ */
+
+const client = new MetaCrawlClient();
+
+export async function POST(req: NextRequest) {
+ try {
+ const body = await req.json();
+ // Support both numImages (camelCase) and num_images (snake_case)
+ const { prompt, cookies, numImages, num_images, async = false } = body;
+ const imageCount = num_images || numImages || 4;
+
+ if (!prompt) {
+ return NextResponse.json(
+ { error: "Prompt is required" },
+ { status: 400 }
+ );
+ }
+
+ if (!cookies) {
+ return NextResponse.json(
+ { error: "Meta AI cookies are required. Please configure in settings." },
+ { status: 401 }
+ );
+ }
+
+ // Check if service is healthy
+ const isHealthy = await client.healthCheck();
+ if (!isHealthy) {
+ return NextResponse.json(
+ { error: "Crawl4AI service is not available. Please try again later." },
+ { status: 503 }
+ );
+ }
+
+ if (async) {
+ // Async mode: return task_id for polling
+ const taskId = await client.generateAsync(prompt, cookies, imageCount);
+ return NextResponse.json({
+ success: true,
+ task_id: taskId
+ });
+ }
+
+ // Sync mode: wait for completion
+ console.log(`[MetaCrawl API] Generating images for: "${prompt.substring(0, 50)}..."`);
+
+ const images = await client.generate(prompt, cookies, imageCount);
+
+ return NextResponse.json({
+ success: true,
+ images: images.map(img => ({
+ url: img.url,
+ data: img.data,
+ prompt: img.prompt,
+ model: img.model
+ }))
+ });
+
+ } catch (error: any) {
+ console.error("[MetaCrawl API] Error:", error);
+ return NextResponse.json(
+ { error: error.message || "Image generation failed" },
+ { status: 500 }
+ );
+ }
+}
+
+/**
+ * GET /api/meta-crawl?task_id=xxx
+ *
+ * Get status of an async generation task
+ */
+export async function GET(req: NextRequest) {
+ const taskId = req.nextUrl.searchParams.get('task_id');
+
+ if (!taskId) {
+ // Return rate limit status
+ try {
+ const status = await client.getRateLimitStatus();
+ return NextResponse.json(status);
+ } catch {
+ return NextResponse.json({ error: "Service not available" }, { status: 503 });
+ }
+ }
+
+ try {
+ const status = await client.getTaskStatus(taskId);
+ return NextResponse.json(status);
+ } catch (error: any) {
+ return NextResponse.json(
+ { error: error.message },
+ { status: error.message === 'Task not found' ? 404 : 500 }
+ );
+ }
+}
+
+/**
+ * DELETE /api/meta-crawl?task_id=xxx
+ *
+ * Clean up a completed task
+ */
+export async function DELETE(req: NextRequest) {
+ const taskId = req.nextUrl.searchParams.get('task_id');
+
+ if (!taskId) {
+ return NextResponse.json({ error: "task_id is required" }, { status: 400 });
+ }
+
+ try {
+ const response = await fetch(`${process.env.CRAWL4AI_URL || 'http://localhost:8000'}/status/${taskId}`, {
+ method: 'DELETE'
+ });
+
+ if (!response.ok) {
+ return NextResponse.json({ error: "Failed to delete task" }, { status: response.status });
+ }
+
+ return NextResponse.json({ deleted: true });
+ } catch (error: any) {
+ return NextResponse.json({ error: error.message }, { status: 500 });
+ }
+}
diff --git a/app/page.tsx b/app/page.tsx
index 24bf719..bd1fad9 100644
--- a/app/page.tsx
+++ b/app/page.tsx
@@ -9,6 +9,9 @@ import { PromptHero } from "@/components/PromptHero";
import { Settings } from "@/components/Settings";
import { PromptLibrary } from "@/components/PromptLibrary";
import { UploadHistory } from "@/components/UploadHistory";
+import { GrokChat } from "@/components/GrokChat";
+import { CookieExpiredDialog } from "@/components/CookieExpiredDialog";
+
export default function Home() {
const { currentView, setCurrentView, loadGallery } = useStore();
@@ -48,6 +51,10 @@ export default function Home() {
+
+ {/* Floating Chat */}
+ {/*
+ Your {providerName} session has timed out. + To continue generating images, please refresh your cookies. +
+ +