AUXteam commited on
Commit
e6b89d5
·
verified ·
1 Parent(s): 1262001

Upload folder using huggingface_hub

Browse files
app/api/create-ai-sandbox-v2/route.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { NextResponse } from 'next/server';
2
  import { SandboxFactory } from '@/lib/sandbox/factory';
3
  // SandboxProvider type is used through SandboxFactory
4
  import type { SandboxState } from '@/types/sandbox';
@@ -12,9 +12,10 @@ declare global {
12
  var sandboxState: SandboxState;
13
  }
14
 
15
- export async function POST() {
16
  try {
17
- console.log('[create-ai-sandbox-v2] Creating sandbox...');
 
18
 
19
  // Clean up all existing sandboxes
20
  console.log('[create-ai-sandbox-v2] Cleaning up existing sandboxes...');
@@ -41,8 +42,13 @@ export async function POST() {
41
  const provider = SandboxFactory.create();
42
  const sandboxInfo = await provider.createSandbox();
43
 
44
- console.log('[create-ai-sandbox-v2] Setting up Vite React app...');
45
- await provider.setupViteApp();
 
 
 
 
 
46
 
47
  // Register with sandbox manager
48
  sandboxManager.registerSandbox(sandboxInfo.sandboxId, provider);
@@ -75,7 +81,7 @@ export async function POST() {
75
  sandboxId: sandboxInfo.sandboxId,
76
  url: sandboxInfo.url,
77
  provider: sandboxInfo.provider,
78
- message: 'Sandbox created and Vite React app initialized'
79
  });
80
 
81
  } catch (error) {
 
1
+ import { NextRequest, NextResponse } from 'next/server';
2
  import { SandboxFactory } from '@/lib/sandbox/factory';
3
  // SandboxProvider type is used through SandboxFactory
4
  import type { SandboxState } from '@/types/sandbox';
 
12
  var sandboxState: SandboxState;
13
  }
14
 
15
+ export async function POST(request: NextRequest) {
16
  try {
17
+ const { runtime = 'react' } = await request.json() || {};
18
+ console.log(`[create-ai-sandbox-v2] Creating sandbox for ${runtime}...`);
19
 
20
  // Clean up all existing sandboxes
21
  console.log('[create-ai-sandbox-v2] Cleaning up existing sandboxes...');
 
42
  const provider = SandboxFactory.create();
43
  const sandboxInfo = await provider.createSandbox();
44
 
45
+ if (runtime === 'python') {
46
+ console.log('[create-ai-sandbox-v2] Setting up Python environment...');
47
+ await provider.setupPythonEnv();
48
+ } else {
49
+ console.log('[create-ai-sandbox-v2] Setting up Vite React app...');
50
+ await provider.setupViteApp();
51
+ }
52
 
53
  // Register with sandbox manager
54
  sandboxManager.registerSandbox(sandboxInfo.sandboxId, provider);
 
81
  sandboxId: sandboxInfo.sandboxId,
82
  url: sandboxInfo.url,
83
  provider: sandboxInfo.provider,
84
+ message: `Sandbox created and ${runtime} initialized`
85
  });
86
 
87
  } catch (error) {
app/api/submit-job/route.ts CHANGED
@@ -4,6 +4,7 @@ import { getProviderForModel } from "@/lib/ai/provider-manager";
4
  import { extractFilesFromAI, sanitizeBranchName } from "@/lib/ai-utils";
5
  import { pushToGitHub } from "@/lib/github";
6
  import { appConfig } from "@/config/app.config";
 
7
 
8
  export async function GET() {
9
  return NextResponse.json({ message: "Submit job endpoint is active. Use POST to submit a paper." });
@@ -15,7 +16,7 @@ export async function POST(request: NextRequest) {
15
  arxivUrl,
16
  paperContent,
17
  repo,
18
- paperName,
19
  model = appConfig.ai.defaultModel
20
  } = await request.json();
21
 
@@ -23,37 +24,41 @@ export async function POST(request: NextRequest) {
23
  return NextResponse.json({ error: "Target GitHub repository is required (e.g. 'owner/repo')" }, { status: 400 });
24
  }
25
 
26
- if (!paperName) {
27
- return NextResponse.json({ error: "Paper name is required for branch naming" }, { status: 400 });
28
- }
29
-
30
  const githubToken = process.env.PERSONAL_ACCESS_TOKEN;
31
  if (!githubToken) {
32
  return NextResponse.json({ error: "GitHub PERSONAL_ACCESS_TOKEN not configured in environment" }, { status: 500 });
33
  }
34
 
35
  let finalPaperContent = paperContent || "";
 
36
 
37
- // 1. Get paper content from Arxiv if URL is provided
38
  if (arxivUrl && !paperContent) {
39
- console.log(`[submit-job] Scraping Arxiv: ${arxivUrl}`);
40
- try {
41
- const scrapeResponse = await fetch(`${process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000'}/api/scrape-website`, {
42
- method: 'POST',
43
- headers: { 'Content-Type': 'application/json' },
44
- body: JSON.stringify({ url: arxivUrl })
45
- });
46
-
47
- if (scrapeResponse.ok) {
48
- const scrapeData = await scrapeResponse.json();
49
- if (scrapeData.success) {
50
- finalPaperContent = scrapeData.data.content;
51
- } else {
52
- console.warn(`[submit-job] Scrape failed: ${scrapeData.error}`);
 
 
 
 
 
 
 
53
  }
 
 
54
  }
55
- } catch (error) {
56
- console.error(`[submit-job] Error calling scrape API:`, error);
57
  }
58
  }
59
 
 
4
  import { extractFilesFromAI, sanitizeBranchName } from "@/lib/ai-utils";
5
  import { pushToGitHub } from "@/lib/github";
6
  import { appConfig } from "@/config/app.config";
7
+ import { fetchArxivPaper } from "@/lib/arxiv";
8
 
9
  export async function GET() {
10
  return NextResponse.json({ message: "Submit job endpoint is active. Use POST to submit a paper." });
 
16
  arxivUrl,
17
  paperContent,
18
  repo,
19
+ paperName: providedPaperName,
20
  model = appConfig.ai.defaultModel
21
  } = await request.json();
22
 
 
24
  return NextResponse.json({ error: "Target GitHub repository is required (e.g. 'owner/repo')" }, { status: 400 });
25
  }
26
 
 
 
 
 
27
  const githubToken = process.env.PERSONAL_ACCESS_TOKEN;
28
  if (!githubToken) {
29
  return NextResponse.json({ error: "GitHub PERSONAL_ACCESS_TOKEN not configured in environment" }, { status: 500 });
30
  }
31
 
32
  let finalPaperContent = paperContent || "";
33
+ let paperName = providedPaperName || "paper-to-code";
34
 
35
+ // 1. Get paper content from Arxiv API if URL or query is provided
36
  if (arxivUrl && !paperContent) {
37
+ console.log(`[submit-job] Fetching Arxiv paper: ${arxivUrl}`);
38
+ const paper = await fetchArxivPaper(arxivUrl);
39
+ if (paper) {
40
+ finalPaperContent = `Title: ${paper.title}\n\nAuthors: ${paper.authors.join(', ')}\n\nSummary: ${paper.summary}`;
41
+ paperName = paper.title;
42
+ console.log(`[submit-job] Successfully fetched: ${paper.title}`);
43
+ } else {
44
+ console.warn(`[submit-job] Arxiv API failed for ${arxivUrl}, falling back to scraping`);
45
+ // Fallback to scrape if API fails
46
+ try {
47
+ const scrapeResponse = await fetch(`${process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000'}/api/scrape-website`, {
48
+ method: 'POST',
49
+ headers: { 'Content-Type': 'application/json' },
50
+ body: JSON.stringify({ url: arxivUrl })
51
+ });
52
+
53
+ if (scrapeResponse.ok) {
54
+ const scrapeData = await scrapeResponse.json();
55
+ if (scrapeData.success) {
56
+ finalPaperContent = scrapeData.data.content;
57
+ }
58
  }
59
+ } catch (error) {
60
+ console.error(`[submit-job] Error calling scrape API:`, error);
61
  }
 
 
62
  }
63
  }
64
 
app/generation/page.tsx CHANGED
@@ -61,6 +61,7 @@ interface ScrapeData {
61
  }
62
 
63
  function AISandboxPage() {
 
64
  const [sandboxData, setSandboxData] = useState<SandboxData | null>(null);
65
  const [loading, setLoading] = useState(false);
66
  const [status, setStatus] = useState({ text: 'Not connected', active: false });
@@ -529,18 +530,20 @@ function AISandboxPage() {
529
 
530
  const sandboxCreationRef = useRef<boolean>(false);
531
 
532
- const createSandbox = async (fromHomeScreen = false) => {
533
  // Prevent duplicate sandbox creation
534
  if (sandboxCreationRef.current) {
535
  console.log('[createSandbox] Sandbox creation already in progress, skipping...');
536
  return null;
537
  }
538
 
 
 
539
  sandboxCreationRef.current = true;
540
- console.log('[createSandbox] Starting sandbox creation...');
541
  setLoading(true);
542
  setShowLoadingBackground(true);
543
- updateStatus('Creating sandbox...', false);
544
  setResponseArea([]);
545
  setScreenshotError(null);
546
 
@@ -548,7 +551,7 @@ function AISandboxPage() {
548
  const response = await fetch('/api/create-ai-sandbox-v2', {
549
  method: 'POST',
550
  headers: { 'Content-Type': 'application/json' },
551
- body: JSON.stringify({})
552
  });
553
 
554
  const data = await response.json();
@@ -3282,7 +3285,33 @@ Focus on the key sections and content, making it clean and modern.`;
3282
  <HeaderProvider>
3283
  <div className="font-sans bg-background text-foreground h-screen flex flex-col">
3284
  <div className="bg-white py-[15px] py-[8px] border-b border-border-faint flex items-center justify-between shadow-sm">
3285
- <HeaderBrandKit />
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3286
  <div className="flex items-center gap-2">
3287
  {/* Model Selector - Left side */}
3288
  <select
 
61
  }
62
 
63
  function AISandboxPage() {
64
+ const [mode, setMode] = useState<'website' | 'paper'>('website');
65
  const [sandboxData, setSandboxData] = useState<SandboxData | null>(null);
66
  const [loading, setLoading] = useState(false);
67
  const [status, setStatus] = useState({ text: 'Not connected', active: false });
 
530
 
531
  const sandboxCreationRef = useRef<boolean>(false);
532
 
533
+ const createSandbox = async (fromHomeScreen = false, runtimeOverride?: string) => {
534
  // Prevent duplicate sandbox creation
535
  if (sandboxCreationRef.current) {
536
  console.log('[createSandbox] Sandbox creation already in progress, skipping...');
537
  return null;
538
  }
539
 
540
+ const runtime = runtimeOverride || (mode === 'paper' ? 'python' : 'react');
541
+
542
  sandboxCreationRef.current = true;
543
+ console.log(`[createSandbox] Starting sandbox creation for ${runtime}...`);
544
  setLoading(true);
545
  setShowLoadingBackground(true);
546
+ updateStatus(`Creating ${runtime} sandbox...`, false);
547
  setResponseArea([]);
548
  setScreenshotError(null);
549
 
 
551
  const response = await fetch('/api/create-ai-sandbox-v2', {
552
  method: 'POST',
553
  headers: { 'Content-Type': 'application/json' },
554
+ body: JSON.stringify({ runtime })
555
  });
556
 
557
  const data = await response.json();
 
3285
  <HeaderProvider>
3286
  <div className="font-sans bg-background text-foreground h-screen flex flex-col">
3287
  <div className="bg-white py-[15px] py-[8px] border-b border-border-faint flex items-center justify-between shadow-sm">
3288
+ <div className="flex items-center gap-4">
3289
+ <HeaderBrandKit />
3290
+ <div className="bg-black-alpha-4 p-2 rounded-8 flex gap-2 ml-4">
3291
+ <button
3292
+ onClick={() => {
3293
+ if (mode !== 'website') {
3294
+ setMode('website');
3295
+ setSandboxData(null); // Reset sandbox when switching modes
3296
+ }
3297
+ }}
3298
+ className={`px-12 py-4 rounded-6 text-xs font-medium transition-all ${mode === 'website' ? 'bg-white shadow-sm text-accent-black' : 'text-black-alpha-48 hover:text-black-alpha-72'}`}
3299
+ >
3300
+ Website2Code
3301
+ </button>
3302
+ <button
3303
+ onClick={() => {
3304
+ if (mode !== 'paper') {
3305
+ setMode('paper');
3306
+ setSandboxData(null); // Reset sandbox when switching modes
3307
+ }
3308
+ }}
3309
+ className={`px-12 py-4 rounded-6 text-xs font-medium transition-all ${mode === 'paper' ? 'bg-white shadow-sm text-accent-black' : 'text-black-alpha-48 hover:text-black-alpha-72'}`}
3310
+ >
3311
+ Paper2Code
3312
+ </button>
3313
+ </div>
3314
+ </div>
3315
  <div className="flex items-center gap-2">
3316
  {/* Model Selector - Left side */}
3317
  <select
app/page.tsx CHANGED
@@ -38,6 +38,7 @@ interface SearchResult {
38
  }
39
 
40
  export default function HomePage() {
 
41
  const [url, setUrl] = useState<string>("");
42
  const [selectedStyle, setSelectedStyle] = useState<string>("1");
43
  const [selectedModel, setSelectedModel] = useState<string>(appConfig.ai.defaultModel);
@@ -87,7 +88,34 @@ export default function HomePage() {
87
  const inputValue = url.trim();
88
 
89
  if (!inputValue) {
90
- toast.error("Please enter a URL or search term");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
  return;
92
  }
93
 
@@ -260,10 +288,29 @@ export default function HomePage() {
260
  <HomeHeroBackground />
261
 
262
  <div className="relative container px-16">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
263
  <HomeHeroBadge />
264
  <HomeHeroTitle />
265
  <p className="text-center text-body-large">
266
- Clone brand format or re-imagine any website, in seconds.
 
 
267
  </p>
268
  <Link
269
  className="bg-black-alpha-4 hover:bg-black-alpha-6 rounded-6 px-8 lg:px-6 text-label-large h-30 lg:h-24 block mt-8 mx-auto w-max gap-4 transition-all"
@@ -378,7 +425,7 @@ export default function HomePage() {
378
  )}
379
  <input
380
  className="flex-1 bg-transparent text-body-input text-accent-black placeholder:text-black-alpha-48 focus:outline-none focus:ring-0 focus:border-transparent"
381
- placeholder="Enter URL or search term..."
382
  type="text"
383
  value={url}
384
  disabled={isSearching}
@@ -416,7 +463,7 @@ export default function HomePage() {
416
  >
417
  <HeroInputSubmitButton
418
  dirty={url.length > 0}
419
- buttonText={isURL(url) ? 'Scrape Site' : 'Search'}
420
  disabled={isSearching}
421
  />
422
  </div>
@@ -424,9 +471,9 @@ export default function HomePage() {
424
  )}
425
  </div>
426
 
427
- {/* Options Section - Only show when valid URL */}
428
  <div className={`overflow-hidden transition-all duration-500 ease-in-out ${
429
- isValidUrl ? (extendBrandStyles ? 'max-h-[400px]' : 'max-h-[300px]') + ' opacity-100' : 'max-h-0 opacity-0'
430
  }`}>
431
  <div className="px-[28px] pt-0 pb-[28px]">
432
  <div className="border-t border-gray-100 bg-white">
 
38
  }
39
 
40
  export default function HomePage() {
41
+ const [mode, setMode] = useState<'website' | 'paper'>('website');
42
  const [url, setUrl] = useState<string>("");
43
  const [selectedStyle, setSelectedStyle] = useState<string>("1");
44
  const [selectedModel, setSelectedModel] = useState<string>(appConfig.ai.defaultModel);
 
88
  const inputValue = url.trim();
89
 
90
  if (!inputValue) {
91
+ toast.error(mode === 'website' ? "Please enter a URL or search term" : "Please enter an Arxiv link or paper query");
92
+ return;
93
+ }
94
+
95
+ if (mode === 'paper') {
96
+ toast.info("Transforming paper to code...");
97
+ try {
98
+ const response = await fetch('/api/submit-job', {
99
+ method: 'POST',
100
+ headers: { 'Content-Type': 'application/json' },
101
+ body: JSON.stringify({
102
+ arxivUrl: inputValue,
103
+ repo: 'AUXteam/Paper2Code-Jobs', // Default repo for paper jobs
104
+ paperName: inputValue.split('/').pop() || 'paper'
105
+ })
106
+ });
107
+ const data = await response.json();
108
+ if (data.success) {
109
+ toast.success("Job submitted! Code will be pushed to GitHub.");
110
+ if (data.githubUrl) {
111
+ window.open(data.githubUrl, '_blank');
112
+ }
113
+ } else {
114
+ toast.error(data.error || "Failed to submit job");
115
+ }
116
+ } catch (error) {
117
+ toast.error("An error occurred while submitting the paper");
118
+ }
119
  return;
120
  }
121
 
 
288
  <HomeHeroBackground />
289
 
290
  <div className="relative container px-16">
291
+ <div className="flex justify-center mb-12">
292
+ <div className="bg-black-alpha-4 p-4 rounded-12 flex gap-4">
293
+ <button
294
+ onClick={() => setMode('website')}
295
+ className={`px-16 py-8 rounded-8 text-label-medium transition-all ${mode === 'website' ? 'bg-white shadow-sm text-accent-black' : 'text-black-alpha-48 hover:text-black-alpha-72'}`}
296
+ >
297
+ Website2Code
298
+ </button>
299
+ <button
300
+ onClick={() => setMode('paper')}
301
+ className={`px-16 py-8 rounded-8 text-label-medium transition-all ${mode === 'paper' ? 'bg-white shadow-sm text-accent-black' : 'text-black-alpha-48 hover:text-black-alpha-72'}`}
302
+ >
303
+ Paper2Code
304
+ </button>
305
+ </div>
306
+ </div>
307
+
308
  <HomeHeroBadge />
309
  <HomeHeroTitle />
310
  <p className="text-center text-body-large">
311
+ {mode === 'website'
312
+ ? "Clone brand format or re-imagine any website, in seconds."
313
+ : "Transform research papers into functional Python or React code."}
314
  </p>
315
  <Link
316
  className="bg-black-alpha-4 hover:bg-black-alpha-6 rounded-6 px-8 lg:px-6 text-label-large h-30 lg:h-24 block mt-8 mx-auto w-max gap-4 transition-all"
 
425
  )}
426
  <input
427
  className="flex-1 bg-transparent text-body-input text-accent-black placeholder:text-black-alpha-48 focus:outline-none focus:ring-0 focus:border-transparent"
428
+ placeholder={mode === 'website' ? "Enter URL or search term..." : "Enter Arxiv link or reference..."}
429
  type="text"
430
  value={url}
431
  disabled={isSearching}
 
463
  >
464
  <HeroInputSubmitButton
465
  dirty={url.length > 0}
466
+ buttonText={mode === 'website' ? (isURL(url) ? 'Scrape Site' : 'Search') : 'Submit Paper'}
467
  disabled={isSearching}
468
  />
469
  </div>
 
471
  )}
472
  </div>
473
 
474
+ {/* Options Section - Only show when valid URL and in website mode */}
475
  <div className={`overflow-hidden transition-all duration-500 ease-in-out ${
476
+ mode === 'website' && isValidUrl ? (extendBrandStyles ? 'max-h-[400px]' : 'max-h-[300px]') + ' opacity-100' : 'max-h-0 opacity-0'
477
  }`}>
478
  <div className="px-[28px] pt-0 pb-[28px]">
479
  <div className="border-t border-gray-100 bg-white">
lib/arxiv.ts ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import axios from 'axios';
2
+ import { XMLParser } from 'fast-xml-parser';
3
+
4
+ export interface ArxivPaper {
5
+ id: string;
6
+ title: string;
7
+ summary: string;
8
+ authors: string[];
9
+ published: string;
10
+ updated: string;
11
+ link: string;
12
+ pdfUrl: string;
13
+ }
14
+
15
+ const parser = new XMLParser();
16
+
17
+ /**
18
+ * Fetches paper metadata from Arxiv API.
19
+ * Supports both direct Arxiv IDs and search queries.
20
+ */
21
+ export async function fetchArxivPaper(query: string): Promise<ArxivPaper | null> {
22
+ try {
23
+ let id = '';
24
+
25
+ // Extract ID if it's a URL
26
+ if (query.includes('arxiv.org/abs/')) {
27
+ id = query.split('arxiv.org/abs/')[1].split('?')[0];
28
+ } else if (query.includes('arxiv.org/pdf/')) {
29
+ id = query.split('arxiv.org/pdf/')[1].replace('.pdf', '').split('?')[0];
30
+ } else if (/^\d{4}\.\d{4,5}$/.test(query)) {
31
+ id = query;
32
+ }
33
+
34
+ const apiUrl = id
35
+ ? `http://export.arxiv.org/api/query?id_list=${id}`
36
+ : `http://export.arxiv.org/api/query?search_query=all:${encodeURIComponent(query)}&max_results=1`;
37
+
38
+ console.log(`[arxiv] Fetching: ${apiUrl}`);
39
+ const response = await axios.get(apiUrl);
40
+ const jsonObj = parser.parse(response.data);
41
+
42
+ const entry = jsonObj.feed?.entry;
43
+ if (!entry) return null;
44
+
45
+ const paperEntry = Array.isArray(entry) ? entry[0] : entry;
46
+
47
+ return {
48
+ id: paperEntry.id,
49
+ title: paperEntry.title.trim().replace(/\n/g, ' '),
50
+ summary: paperEntry.summary.trim(),
51
+ authors: Array.isArray(paperEntry.author)
52
+ ? paperEntry.author.map((a: any) => a.name)
53
+ : [paperEntry.author.name],
54
+ published: paperEntry.published,
55
+ updated: paperEntry.updated,
56
+ link: paperEntry.id,
57
+ pdfUrl: paperEntry.id.replace('abs', 'pdf') + '.pdf'
58
+ };
59
+ } catch (error) {
60
+ console.error('[arxiv] Error fetching paper:', error);
61
+ return null;
62
+ }
63
+ }
lib/sandbox/providers/e2b-provider.ts CHANGED
@@ -228,6 +228,47 @@ export class E2BProvider extends SandboxProvider {
228
  };
229
  }
230
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
231
  async setupViteApp(): Promise<void> {
232
  if (!this.sandbox) {
233
  throw new Error('No active sandbox');
 
228
  };
229
  }
230
 
231
+ async setupPythonEnv(): Promise<void> {
232
+ if (!this.sandbox) {
233
+ throw new Error('No active sandbox');
234
+ }
235
+
236
+ console.log('[E2BProvider] Setting up Python environment...');
237
+
238
+ // Create directory structure
239
+ await this.sandbox.runCode(`
240
+ import os
241
+ os.makedirs('/home/user/app', exist_ok=True)
242
+ print('✓ /home/user/app created')
243
+ `);
244
+
245
+ // Create a requirements.txt with common scientific libraries
246
+ const requirements = `
247
+ numpy
248
+ pandas
249
+ matplotlib
250
+ scipy
251
+ scikit-learn
252
+ requests
253
+ `.trim();
254
+
255
+ await this.writeFile('requirements.txt', requirements);
256
+
257
+ // Create a main.py
258
+ const mainPy = `
259
+ def main():
260
+ print("Hello from Paper2Code Python Environment!")
261
+
262
+ if __name__ == "__main__":
263
+ main()
264
+ `.trim();
265
+
266
+ await this.writeFile('main.py', mainPy);
267
+
268
+ // Install requirements
269
+ await this.runCommand('pip install -r requirements.txt');
270
+ }
271
+
272
  async setupViteApp(): Promise<void> {
273
  if (!this.sandbox) {
274
  throw new Error('No active sandbox');
lib/sandbox/types.ts CHANGED
@@ -58,6 +58,11 @@ export abstract class SandboxProvider {
58
  throw new Error('setupViteApp not implemented for this provider');
59
  }
60
 
 
 
 
 
 
61
  async restartViteServer(): Promise<void> {
62
  // Default implementation for restarting Vite
63
  throw new Error('restartViteServer not implemented for this provider');
 
58
  throw new Error('setupViteApp not implemented for this provider');
59
  }
60
 
61
+ async setupPythonEnv(): Promise<void> {
62
+ // Default implementation for setting up a Python environment
63
+ throw new Error('setupPythonEnv not implemented for this provider');
64
+ }
65
+
66
  async restartViteServer(): Promise<void> {
67
  // Default implementation for restarting Vite
68
  throw new Error('restartViteServer not implemented for this provider');
package.json CHANGED
@@ -53,12 +53,14 @@
53
  "@vercel/sandbox": "^0.0.17",
54
  "ai": "^5.0.0",
55
  "autoprefixer": "^10.4.21",
 
56
  "class-variance-authority": "^0.7.1",
57
  "classnames": "^2.5.1",
58
  "clsx": "^2.1.1",
59
  "copy-to-clipboard": "^3.3.3",
60
  "cors": "^2.8.5",
61
  "dotenv": "^17.2.1",
 
62
  "framer-motion": "^12.23.12",
63
  "groq-sdk": "^0.29.0",
64
  "jotai": "^2.14.0",
 
53
  "@vercel/sandbox": "^0.0.17",
54
  "ai": "^5.0.0",
55
  "autoprefixer": "^10.4.21",
56
+ "axios": "^1.13.5",
57
  "class-variance-authority": "^0.7.1",
58
  "classnames": "^2.5.1",
59
  "clsx": "^2.1.1",
60
  "copy-to-clipboard": "^3.3.3",
61
  "cors": "^2.8.5",
62
  "dotenv": "^17.2.1",
63
+ "fast-xml-parser": "^5.3.6",
64
  "framer-motion": "^12.23.12",
65
  "groq-sdk": "^0.29.0",
66
  "jotai": "^2.14.0",
pnpm-lock.yaml CHANGED
@@ -128,6 +128,9 @@ importers:
128
  autoprefixer:
129
  specifier: ^10.4.21
130
  version: 10.4.21(postcss@8.5.6)
 
 
 
131
  class-variance-authority:
132
  specifier: ^0.7.1
133
  version: 0.7.1
@@ -146,6 +149,9 @@ importers:
146
  dotenv:
147
  specifier: ^17.2.1
148
  version: 17.2.1
 
 
 
149
  framer-motion:
150
  specifier: ^12.23.12
151
  version: 12.23.12(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
@@ -1763,8 +1769,8 @@ packages:
1763
  resolution: {integrity: sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg==}
1764
  engines: {node: '>=4'}
1765
 
1766
- axios@1.11.0:
1767
- resolution: {integrity: sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==}
1768
 
1769
  axobject-query@4.1.0:
1770
  resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==}
@@ -2195,6 +2201,10 @@ packages:
2195
  fast-levenshtein@2.0.6:
2196
  resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
2197
 
 
 
 
 
2198
  fastq@1.19.1:
2199
  resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==}
2200
 
@@ -2252,6 +2262,10 @@ packages:
2252
  resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==}
2253
  engines: {node: '>= 6'}
2254
 
 
 
 
 
2255
  format@0.2.2:
2256
  resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==}
2257
  engines: {node: '>=0.4.x'}
@@ -3278,6 +3292,9 @@ packages:
3278
  resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
3279
  engines: {node: '>=8'}
3280
 
 
 
 
3281
  styled-jsx@5.1.6:
3282
  resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==}
3283
  engines: {node: '>= 12.0.0'}
@@ -3829,7 +3846,7 @@ snapshots:
3829
 
3830
  '@mendable/firecrawl-js@4.3.4':
3831
  dependencies:
3832
- axios: 1.11.0
3833
  typescript-event-target: 1.1.1
3834
  zod: 3.25.76
3835
  zod-to-json-schema: 3.24.6(zod@3.25.76)
@@ -5116,10 +5133,10 @@ snapshots:
5116
 
5117
  axe-core@4.10.3: {}
5118
 
5119
- axios@1.11.0:
5120
  dependencies:
5121
  follow-redirects: 1.15.11
5122
- form-data: 4.0.4
5123
  proxy-from-env: 1.1.0
5124
  transitivePeerDependencies:
5125
  - debug
@@ -5692,6 +5709,10 @@ snapshots:
5692
 
5693
  fast-levenshtein@2.0.6: {}
5694
 
 
 
 
 
5695
  fastq@1.19.1:
5696
  dependencies:
5697
  reusify: 1.1.0
@@ -5745,6 +5766,14 @@ snapshots:
5745
  hasown: 2.0.2
5746
  mime-types: 2.1.35
5747
 
 
 
 
 
 
 
 
 
5748
  format@0.2.2: {}
5749
 
5750
  formdata-node@4.4.1:
@@ -6836,6 +6865,8 @@ snapshots:
6836
 
6837
  strip-json-comments@3.1.1: {}
6838
 
 
 
6839
  styled-jsx@5.1.6(react@19.1.0):
6840
  dependencies:
6841
  client-only: 0.0.1
 
128
  autoprefixer:
129
  specifier: ^10.4.21
130
  version: 10.4.21(postcss@8.5.6)
131
+ axios:
132
+ specifier: ^1.13.5
133
+ version: 1.13.5
134
  class-variance-authority:
135
  specifier: ^0.7.1
136
  version: 0.7.1
 
149
  dotenv:
150
  specifier: ^17.2.1
151
  version: 17.2.1
152
+ fast-xml-parser:
153
+ specifier: ^5.3.6
154
+ version: 5.3.6
155
  framer-motion:
156
  specifier: ^12.23.12
157
  version: 12.23.12(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
 
1769
  resolution: {integrity: sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg==}
1770
  engines: {node: '>=4'}
1771
 
1772
+ axios@1.13.5:
1773
+ resolution: {integrity: sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==}
1774
 
1775
  axobject-query@4.1.0:
1776
  resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==}
 
2201
  fast-levenshtein@2.0.6:
2202
  resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
2203
 
2204
+ fast-xml-parser@5.3.6:
2205
+ resolution: {integrity: sha512-QNI3sAvSvaOiaMl8FYU4trnEzCwiRr8XMWgAHzlrWpTSj+QaCSvOf1h82OEP1s4hiAXhnbXSyFWCf4ldZzZRVA==}
2206
+ hasBin: true
2207
+
2208
  fastq@1.19.1:
2209
  resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==}
2210
 
 
2262
  resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==}
2263
  engines: {node: '>= 6'}
2264
 
2265
+ form-data@4.0.5:
2266
+ resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==}
2267
+ engines: {node: '>= 6'}
2268
+
2269
  format@0.2.2:
2270
  resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==}
2271
  engines: {node: '>=0.4.x'}
 
3292
  resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
3293
  engines: {node: '>=8'}
3294
 
3295
+ strnum@2.1.2:
3296
+ resolution: {integrity: sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ==}
3297
+
3298
  styled-jsx@5.1.6:
3299
  resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==}
3300
  engines: {node: '>= 12.0.0'}
 
3846
 
3847
  '@mendable/firecrawl-js@4.3.4':
3848
  dependencies:
3849
+ axios: 1.13.5
3850
  typescript-event-target: 1.1.1
3851
  zod: 3.25.76
3852
  zod-to-json-schema: 3.24.6(zod@3.25.76)
 
5133
 
5134
  axe-core@4.10.3: {}
5135
 
5136
+ axios@1.13.5:
5137
  dependencies:
5138
  follow-redirects: 1.15.11
5139
+ form-data: 4.0.5
5140
  proxy-from-env: 1.1.0
5141
  transitivePeerDependencies:
5142
  - debug
 
5709
 
5710
  fast-levenshtein@2.0.6: {}
5711
 
5712
+ fast-xml-parser@5.3.6:
5713
+ dependencies:
5714
+ strnum: 2.1.2
5715
+
5716
  fastq@1.19.1:
5717
  dependencies:
5718
  reusify: 1.1.0
 
5766
  hasown: 2.0.2
5767
  mime-types: 2.1.35
5768
 
5769
+ form-data@4.0.5:
5770
+ dependencies:
5771
+ asynckit: 0.4.0
5772
+ combined-stream: 1.0.8
5773
+ es-set-tostringtag: 2.1.0
5774
+ hasown: 2.0.2
5775
+ mime-types: 2.1.35
5776
+
5777
  format@0.2.2: {}
5778
 
5779
  formdata-node@4.4.1:
 
6865
 
6866
  strip-json-comments@3.1.1: {}
6867
 
6868
+ strnum@2.1.2: {}
6869
+
6870
  styled-jsx@5.1.6(react@19.1.0):
6871
  dependencies:
6872
  client-only: 0.0.1