diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..f148b4d11261d2145993363f9dfc4867d91c1613
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,30 @@
+# Credentials and Secrets
+.env
+
+# Python Compiled Files
+__pycache__/
+*.py[cod]
+
+# Virtual Environments
+venv/
+.venv/
+
+# System Files
+.DS_Store
+Thumbs.db
+
+# Logs
+*.log
+
+# Editor Directories
+.vscode/
+.idea/
+
+*.jpg
+
+stitch_merchflow_ai_dashboard.zip
+screen.jpg
+test_image.jpg
+*.zip
+*.jpeg
+*.png
\ No newline at end of file
diff --git a/.vite/deps/_metadata.json b/.vite/deps/_metadata.json
new file mode 100644
index 0000000000000000000000000000000000000000..53b40cc8acdeb1529f877875a6c89b7630f5e272
--- /dev/null
+++ b/.vite/deps/_metadata.json
@@ -0,0 +1,8 @@
+{
+ "hash": "7678463b",
+ "configHash": "aaeefbd1",
+ "lockfileHash": "e3b0c442",
+ "browserHash": "7b5c57bd",
+ "optimized": {},
+ "chunks": {}
+}
\ No newline at end of file
diff --git a/.vite/deps/package.json b/.vite/deps/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..3dbc1ca591c0557e35b6004aeba250e6a70b56e3
--- /dev/null
+++ b/.vite/deps/package.json
@@ -0,0 +1,3 @@
+{
+ "type": "module"
+}
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..66449542f576b755431fd35856be66037e9d18e7
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,10 @@
+FROM python:3.9
+WORKDIR /code
+COPY ./requirements.txt /code/requirements.txt
+RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
+COPY . /code
+# Fix permissions for libraries that write to home
+RUN mkdir -p /tmp/home
+ENV HOME=/tmp/home
+# Start the FastAPI server on port 7860 (required by Hugging Face)
+CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860"]
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..3b4f6366b388e590824d07f175d8e66b15b30c80
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,6 @@
+Copyright Ā© 2026 Bhishaj Technologies.
+This software and its documentation are the confidential and proprietary
+information of Bhishaj Technologies (URN: UDYAM-UP-02-0108589).
+Unauthorized copying, distribution, or use of this source code
+is strictly prohibited. Use is governed by the signed Master Service Agreement.
+All Rights Reserved.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..76bad046c954d55de22134eb2571b6105cb88546
--- /dev/null
+++ b/README.md
@@ -0,0 +1,75 @@
+---
+title: MerchFlow AI
+emoji: š
+colorFrom: blue
+colorTo: purple
+sdk: docker
+pinned: false
+---
+
+# MerchFlow AI
+
+
+
+
+
+MerchFlow AI is a high-performance, multi-agent orchestration system designed to automate the generation of premium e-commerce product listings. By synergizing Computer Vision, Retrieval Augmented Generation (RAG), and Large Language Models (LLMs), it transforms raw product images into SEO-optimized market-ready content.
+
+## šļø Architecture Flow
+
+The system employs a sophisticated event-driven architecture orchestrated by **FastAPI**:
+
+1. **šļø Visual Agent (Gemini 1.5)**
+ * **Function**: Zero-shot image analysis.
+ * **Process**: Extracts high-fidelity visual attributes including dominant color palettes, stylistic classifications, and granular item types.
+ * **Engine**: Google Gemini 1.5 Flash (Vision).
+
+2. **š§ Memory Agent (Pinecone)**
+ * **Function**: Semantic Search & RAG.
+ * **Process**: Vectorizes visual tags to query a high-dimensional index, retrieving historically high-performing SEO keywords and market trends relevant to the product.
+ * **Engine**: Pinecone Vector Database.
+
+3. **āļø Writer Agent (Llama 3)**
+ * **Function**: Creative Synthesis.
+ * **Process**: Fuses visual data with retrieved market intelligence to generate persuasive, conversion-focused title, description, and feature bullets.
+ * **Engine**: Meta Llama 3 (via Groq Cloud).
+
+4. **āļø Orchestrator (FastAPI)**
+ * **Function**: Async Pipeline Management.
+ * **Process**: Handles non-blocking agent execution, error propagation, and API lifecycle management.
+
+5. **š Post-Processing (n8n)**
+ * **Function**: Automation Webhook.
+ * **Process**: Triggers downstream workflows (database storage, Shopify API integration) via secure webhooks upon successful generation.
+
+## š Complete Setup
+
+To run this system locally, ensure you have the following environment variables configured in your `.env` file:
+
+```env
+GEMINI_API_KEY=your_gemini_key
+GROQ_API_KEY=your_groq_key
+PINECONE_API_KEY=your_pinecone_key
+N8N_WEBHOOK_URL=your_n8n_webhook_url
+```
+
+## ā” Quick Start
+
+### 1. Installation
+Install the required dependencies using pip:
+
+```bash
+pip install -r requirements.txt
+```
+
+### 2. Execution
+Launch the FastAPI server:
+
+```bash
+python main.py
+```
+
+The API will be available at `http://localhost:7860`.
+
+---
+**š”ļø Maintained by Bhishaj Technologies (UDYAM-UP-02-0108589). All Rights Reserved.**
diff --git a/add_license.py b/add_license.py
new file mode 100644
index 0000000000000000000000000000000000000000..97c3441097983a99d20aa6fadc2434438ccfa68f
--- /dev/null
+++ b/add_license.py
@@ -0,0 +1,64 @@
+import os
+import subprocess
+
+def run_command(command):
+ try:
+ # shell=True is often required on Windows for some commands/environments
+ print(f"Running: {command}")
+ result = subprocess.run(command, check=True, shell=True, capture_output=True, text=True)
+ print(result.stdout)
+ except subprocess.CalledProcessError as e:
+ print(f"Error running: {command}")
+ print(e.stderr)
+ # We don't exit here to allow attempting subsequent commands or user debugging if one fails,
+ # though for git flow it usually makes sense to stop.
+ # Given the instruction is a sequence, we should probably stop if add/commit fails.
+ exit(1)
+
+def main():
+ license_text = """MIT License
+
+Copyright (c) 2025 Bhishaj
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+"""
+
+ file_path = "LICENSE"
+
+ print(f"Creating {file_path}...")
+ with open(file_path, "w", encoding="utf-8") as f:
+ f.write(license_text)
+ print(f"{file_path} created successfully.")
+
+ print("Running git commands...")
+
+ # 1. git add LICENSE
+ run_command("git add LICENSE")
+
+ # 2. git commit -m 'Add MIT License'
+ run_command("git commit -m \"Add MIT License\"")
+
+ # 3. git push space clean_deploy:main
+ print("Pushing to Hugging Face Space (this might take a few seconds)...")
+ run_command("git push space clean_deploy:main")
+
+ print("Done! License added and pushed to Hugging Face Space.")
+
+if __name__ == "__main__":
+ main()
diff --git a/agents/__init__.py b/agents/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/agents/manager.py b/agents/manager.py
new file mode 100644
index 0000000000000000000000000000000000000000..a246213c4fc762489896742ea0a63a09225c006c
--- /dev/null
+++ b/agents/manager.py
@@ -0,0 +1,46 @@
+import csv
+import time
+import os
+import datetime
+from agents.trend_spotter import TrendSpotter
+from agents.visionary import Visionary
+
+class MerchManager:
+ def __init__(self):
+ self.trend_spotter = TrendSpotter()
+ self.visionary = Visionary()
+ self.results_dir = "results"
+ if not os.path.exists(self.results_dir):
+ os.makedirs(self.results_dir)
+
+ def generate_batch(self, niche: str) -> str:
+ # Step 1: Get slogans
+ print(f"š Analyzing trends for niche: {niche}...")
+ slogans = self.trend_spotter.get_trends(niche)
+
+ results = []
+
+ # Step 2: Generate art prompts
+ print(f"šØ Generating designs for {len(slogans)} slogans...")
+ for i, slogan in enumerate(slogans):
+ print(f"Generating design {i+1}/{len(slogans)}...")
+ prompt = self.visionary.generate_art_prompt(slogan, niche)
+ results.append({
+ "Niche": niche,
+ "Slogan": slogan,
+ "Art Prompt": prompt
+ })
+ time.sleep(10)
+
+ # Step 3 & 4: Save to CSV
+ timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
+ filename = f"merch_batch_{niche}_{timestamp}.csv"
+ filepath = os.path.join(self.results_dir, filename)
+
+ with open(filepath, mode='w', newline='', encoding='utf-8') as file:
+ writer = csv.DictWriter(file, fieldnames=["Niche", "Slogan", "Art Prompt"])
+ writer.writeheader()
+ writer.writerows(results)
+
+ print(f"ā
Batch complete! Saved to {filepath}")
+ return filename
diff --git a/agents/memory_agent.py b/agents/memory_agent.py
new file mode 100644
index 0000000000000000000000000000000000000000..c3f6dc256d86133b997babe5283aa099b04b7918
--- /dev/null
+++ b/agents/memory_agent.py
@@ -0,0 +1,104 @@
+import os
+import time
+from dotenv import load_dotenv
+from pinecone import Pinecone, ServerlessSpec
+import google.generativeai as genai
+
+load_dotenv()
+
+class MemoryAgent:
+ def __init__(self):
+ # Configure Gemini
+ self.gemini_api_key = os.getenv("GEMINI_API_KEY")
+ if not self.gemini_api_key:
+ raise ValueError("GEMINI_API_KEY not found")
+ genai.configure(api_key=self.gemini_api_key)
+
+ # Configure Pinecone
+ self.pinecone_api_key = os.getenv("PINECONE_API_KEY")
+ if not self.pinecone_api_key:
+ raise ValueError("PINECONE_API_KEY not found")
+
+ self.pc = Pinecone(api_key=self.pinecone_api_key)
+ self.index_name = "merchflow-index"
+
+ # Check and create index
+ existing_indexes = [i.name for i in self.pc.list_indexes()]
+ if self.index_name not in existing_indexes:
+ print(f"Creating index {self.index_name}...")
+ self.pc.create_index(
+ name=self.index_name,
+ dimension=768, # models/text-embedding-004 dimension
+ metric='cosine',
+ spec=ServerlessSpec(
+ cloud='aws',
+ region='us-east-1'
+ )
+ )
+ # Wait for index to be ready
+ while not self.pc.describe_index(self.index_name).status['ready']:
+ time.sleep(1)
+ print("Index created.")
+
+ self.index = self.pc.Index(self.index_name)
+
+ def _get_embedding(self, text):
+ # Using models/text-embedding-004
+ result = genai.embed_content(
+ model="models/text-embedding-004",
+ content=text,
+ task_type="retrieval_document"
+ )
+ return result['embedding']
+
+ def seed_database(self):
+ # Check if empty
+ stats = self.index.describe_index_stats()
+ if stats.total_vector_count > 0:
+ print("Database already seeded.")
+ return
+
+ print("Seeding database...")
+ items = [
+ {
+ "id": "item1",
+ "text": "Running Shoe",
+ "keywords": "breathable, shock absorption, marathon training, lightweight"
+ },
+ {
+ "id": "item2",
+ "text": "Graphic T-Shirt",
+ "keywords": "100% cotton, vintage wash, pre-shrunk, soft feel"
+ },
+ {
+ "id": "item3",
+ "text": "Leather Wallet",
+ "keywords": "genuine leather, RFID blocking, minimalist, bifold"
+ }
+ ]
+
+ vectors = []
+ for item in items:
+ embedding = self._get_embedding(item['text'])
+ vectors.append({
+ "id": item['id'],
+ "values": embedding,
+ "metadata": {"keywords": item['keywords'], "text": item['text']}
+ })
+
+ self.index.upsert(vectors=vectors)
+ print(f"Seeded {len(vectors)} items.")
+
+ def retrieve_keywords(self, query_text: str):
+ try:
+ query_embedding = self._get_embedding(query_text)
+
+ results = self.index.query(
+ vector=query_embedding,
+ top_k=5,
+ include_metadata=True
+ )
+ return [m.metadata['keywords'] for m in results.matches if m.metadata and 'keywords' in m.metadata]
+ except Exception as e:
+ print(f"ā Keyword Retrieval Failed: {e}")
+ return []
diff --git a/agents/visual_analyst.py b/agents/visual_analyst.py
new file mode 100644
index 0000000000000000000000000000000000000000..4c692d8a129a936b0a0adf911af93d31cc76f817
--- /dev/null
+++ b/agents/visual_analyst.py
@@ -0,0 +1,60 @@
+import os
+import json
+import re
+import google.generativeai as genai
+from dotenv import load_dotenv
+
+load_dotenv()
+
+class VisualAnalyst:
+ def __init__(self):
+ self.api_key = os.getenv("GEMINI_API_KEY")
+ if not self.api_key:
+ raise ValueError("GEMINI_API_KEY not found")
+
+ genai.configure(api_key=self.api_key)
+ self.model_name = "models/gemini-flash-latest"
+ self.model = genai.GenerativeModel(self.model_name)
+ print(f"ā
VisualAnalyst stored Gemini model: {self.model_name}")
+
+ async def analyze_image(self, image_path: str):
+ try:
+ # Upload the file to Gemini
+ # Note: For efficiency in production, files should be managed (uploads/deletes)
+ # but for this agentic flow, we'll upload per request or assume local path usage helper if needed.
+ # However, the standard `model.generate_content` can take PIL images or file objects directly for some sdk versions,
+ # but using the File API is cleaner for 1.5 Flash multi-modal.
+ # Let's use the simpler PIL integration if available, or just path if the SDK supports it.
+ # actually, standard genai usage for images usually involves PIL or uploading.
+ # Let's try the PIL approach first as it's often more direct for local scripts.
+ import PIL.Image
+ img = PIL.Image.open(image_path)
+
+ user_prompt = (
+ "Analyze this product image. "
+ "Return ONLY valid JSON with keys: main_color, product_type, design_style, visual_features."
+ )
+
+ # Gemini 1.5 Flash supports JSON response schema, but simple prompting often works well too.
+ # We'll stick to prompt engineering for now to match the "Return ONLY valid JSON" instruction.
+ response = self.model.generate_content([user_prompt, img], request_options={'timeout': 15.0})
+
+ response_text = response.text
+
+ # Use regex to find the JSON block robustly
+ match = re.search(r'\{.*\}', response_text, re.DOTALL)
+ if match:
+ cleaned_content = match.group(0)
+ else:
+ cleaned_content = response_text
+
+ return json.loads(cleaned_content.strip())
+
+ except Exception as e:
+ print(f"ā Analysis Failed: {e}")
+ return {
+ "main_color": "Unknown",
+ "product_type": "Unknown",
+ "design_style": "Unknown",
+ "visual_features": [f"Error: {str(e)}"]
+ }
diff --git a/agents/writer_agent.py b/agents/writer_agent.py
new file mode 100644
index 0000000000000000000000000000000000000000..826bf305076b9d9167062be77a0d20f1c508b8c2
--- /dev/null
+++ b/agents/writer_agent.py
@@ -0,0 +1,48 @@
+import os
+import json
+from groq import Groq
+from dotenv import load_dotenv
+
+load_dotenv()
+
+class WriterAgent:
+ def __init__(self):
+ self.api_key = os.getenv("GROQ_API_KEY")
+ if not self.api_key:
+ raise ValueError("GROQ_API_KEY not found in environment variables")
+ self.client = Groq(api_key=self.api_key)
+ self.model = "llama-3.3-70b-versatile"
+
+ def write_listing(self, visual_data: dict, seo_keywords: list) -> dict:
+ system_prompt = (
+ "You are an expert e-commerce copywriter. "
+ "Write a persuasive product listing based on these visual attributes and SEO keywords. "
+ "Return JSON with keys: title, description, bullet_points."
+ )
+
+ user_content = f"""
+ Visual Attributes: {json.dumps(visual_data, indent=2)}
+
+ SEO Keywords: {', '.join(seo_keywords)}
+
+ Please generate the listing in JSON format.
+ """
+
+ try:
+ completion = self.client.chat.completions.create(
+ model=self.model,
+ messages=[
+ {"role": "system", "content": system_prompt},
+ {"role": "user", "content": user_content}
+ ],
+ temperature=0.7,
+ response_format={"type": "json_object"},
+ timeout=15.0
+ )
+
+ response_text = completion.choices[0].message.content
+ return json.loads(response_text)
+
+ except Exception as e:
+ print(f"Error generating listing: {e}")
+ return {"error": str(e)}
diff --git a/apply_proprietary_license.py b/apply_proprietary_license.py
new file mode 100644
index 0000000000000000000000000000000000000000..35aea5a702c689c810dfa74d8b384d0fae2f7276
--- /dev/null
+++ b/apply_proprietary_license.py
@@ -0,0 +1,57 @@
+#!/usr/bin/env python3
+"""Apply proprietary licensing updates and deploy git changes."""
+
+from __future__ import annotations
+
+import subprocess
+from pathlib import Path
+
+LICENSE_TEXT = """Copyright Ā© 2026 Bhishaj Technologies.
+This software and its documentation are the confidential and proprietary
+information of Bhishaj Technologies (URN: UDYAM-UP-02-0108589).
+Unauthorized copying, distribution, or use of this source code
+is strictly prohibited. Use is governed by the signed Master Service Agreement.
+All Rights Reserved.
+"""
+
+MIT_BADGE_LINE = ""
+FOOTER = "---\n**š”ļø Maintained by Bhishaj Technologies (UDYAM-UP-02-0108589). All Rights Reserved.**\n"
+
+
+def overwrite_license_file() -> None:
+ Path("LICENSE").write_text(LICENSE_TEXT, encoding="utf-8")
+
+
+def sanitize_readme() -> None:
+ readme_path = Path("README.md")
+ readme = readme_path.read_text(encoding="utf-8")
+
+ filtered_lines = [line for line in readme.splitlines() if line.strip() != MIT_BADGE_LINE]
+ sanitized = "\n".join(filtered_lines).rstrip("\n")
+
+ if FOOTER.strip() not in sanitized:
+ sanitized = f"{sanitized}\n\n{FOOTER}"
+ else:
+ sanitized = f"{sanitized}\n"
+
+ readme_path.write_text(sanitized, encoding="utf-8")
+
+
+def run_git_commands() -> None:
+ commands = [
+ ["git", "add", "LICENSE", "README.md"],
+ ["git", "commit", "-m", "Switch to Proprietary License (Bhishaj Technologies)"],
+ ["git", "push", "space", "clean_deploy:main"],
+ ]
+ for command in commands:
+ subprocess.run(command, check=True)
+
+
+def main() -> None:
+ overwrite_license_file()
+ sanitize_readme()
+ run_git_commands()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/apply_qa_fixes.py b/apply_qa_fixes.py
new file mode 100644
index 0000000000000000000000000000000000000000..7b21cfd70eb26dc6a4a596510f242b56a1ea3f44
--- /dev/null
+++ b/apply_qa_fixes.py
@@ -0,0 +1,111 @@
+import os
+import re
+
+def patch_main():
+ with open("main.py", "r", encoding="utf-8") as f:
+ content = f.read()
+
+ old_pattern = r'return JSONResponse\(\s*content=\{\s*"error": str\(e\),\s*"type": type\(e\)\.__name__,\s*"details": error_details\s*\},\s*status_code=500\s*\)'
+ new_text = '''return JSONResponse(
+ content={
+ "error": "An internal server error occurred.",
+ "type": type(e).__name__
+ },
+ status_code=500
+ )'''
+ content = re.sub(old_pattern, new_text, content[1:] if content.startswith('\ufeff') else content)
+
+ with open("main.py", "w", encoding="utf-8") as f:
+ f.write(content)
+ print("Patched main.py")
+
+
+def patch_memory_agent():
+ path = "agents/memory_agent.py"
+ with open(path, "r", encoding="utf-8") as f:
+ content = f.read()
+
+ old_func = r'def retrieve_keywords\(.*?: str\):.*?return \[m\.metadata\[\'keywords\'\] for m in results\.matches if m\.metadata and \'keywords\' in m\.metadata\]'
+ new_func = '''def retrieve_keywords(self, query_text: str):
+ try:
+ query_embedding = self._get_embedding(query_text)
+
+ results = self.index.query(
+ vector=query_embedding,
+ top_k=5,
+ include_metadata=True
+ )
+ return [m.metadata['keywords'] for m in results.matches if m.metadata and 'keywords' in m.metadata]
+ except Exception as e:
+ print(f"ā Keyword Retrieval Failed: {e}")
+ return []'''
+
+ content = re.sub(old_func, new_func, content, flags=re.DOTALL)
+
+ with open(path, "w", encoding="utf-8") as f:
+ f.write(content)
+ print("Patched agents/memory_agent.py")
+
+
+def patch_visual_analyst():
+ path = "agents/visual_analyst.py"
+ with open(path, "r", encoding="utf-8") as f:
+ content = f.read()
+
+ # Add timeout
+ content = content.replace(
+ "response = self.model.generate_content([user_prompt, img])",
+ "response = self.model.generate_content([user_prompt, img], request_options={'timeout': 15.0})"
+ )
+
+ if "import re" not in content:
+ content = content.replace("import json", "import json\nimport re")
+
+ # Rewrite JSON parsing
+ old_parsing = r'# Clean up potential markdown code fences.*?return json\.loads\(cleaned_content\.strip\(\)\)'
+ new_parsing = '''# Use regex to find the JSON block robustly
+ match = re.search(r'\\{.*\\}', response_text, re.DOTALL)
+ if match:
+ cleaned_content = match.group(0)
+ else:
+ cleaned_content = response_text
+
+ return json.loads(cleaned_content.strip())'''
+
+ content = re.sub(old_parsing, new_parsing, content, flags=re.DOTALL)
+
+ with open(path, "w", encoding="utf-8") as f:
+ f.write(content)
+ print("Patched agents/visual_analyst.py")
+
+
+def patch_writer_agent():
+ path = "agents/writer_agent.py"
+ with open(path, "r", encoding="utf-8") as f:
+ content = f.read()
+
+ old_completion = r'completion = self\.client\.chat\.completions\.create\(\s*model=self\.model,\s*messages=\[\s*\{"role": "system", "content": system_prompt\},\s*\{"role": "user", "content": user_content\}\s*\],\s*temperature=0\.7,\s*response_format=\{"type": "json_object"\}\s*\)'
+
+ new_completion = '''completion = self.client.chat.completions.create(
+ model=self.model,
+ messages=[
+ {"role": "system", "content": system_prompt},
+ {"role": "user", "content": user_content}
+ ],
+ temperature=0.7,
+ response_format={"type": "json_object"},
+ timeout=15.0
+ )'''
+
+ content = re.sub(old_completion, new_completion, content)
+
+ with open(path, "w", encoding="utf-8") as f:
+ f.write(content)
+ print("Patched agents/writer_agent.py")
+
+if __name__ == "__main__":
+ patch_main()
+ patch_memory_agent()
+ patch_visual_analyst()
+ patch_writer_agent()
+ print("All file patching completed successfully.")
diff --git a/args.json b/args.json
new file mode 100644
index 0000000000000000000000000000000000000000..932aab106c59eb00118aaa29363078216419750f
--- /dev/null
+++ b/args.json
@@ -0,0 +1,4 @@
+{
+ "projectId": "13453765122851154258",
+ "screenId": "cf3af7f8a9e74daf85096241ed88c75c"
+}
diff --git a/check_basic.py b/check_basic.py
new file mode 100644
index 0000000000000000000000000000000000000000..62ed83a48e0a428b6347cdd23e262080cb85ed5d
--- /dev/null
+++ b/check_basic.py
@@ -0,0 +1,20 @@
+import os
+from huggingface_hub import InferenceClient
+from dotenv import load_dotenv
+
+load_dotenv()
+
+api_key = os.getenv("HF_TOKEN")
+client = InferenceClient(api_key=api_key)
+
+print(f"Testing token with microsoft/resnet-50")
+
+try:
+ # Pass the URL directly as the input (InferenceClient handles URLs for image tasks)
+ result = client.image_classification(
+ model="microsoft/resnet-50",
+ image="https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/tasks/car.jpg?download=true"
+ )
+ print("Success:", result)
+except Exception as e:
+ print("Failed:", e)
diff --git a/check_gemini.py b/check_gemini.py
new file mode 100644
index 0000000000000000000000000000000000000000..cfca9358a8a65cf29acb003b394c594983f845f0
--- /dev/null
+++ b/check_gemini.py
@@ -0,0 +1,26 @@
+import os
+import google.generativeai as genai
+from dotenv import load_dotenv
+
+load_dotenv()
+
+api_key = os.getenv("GEMINI_API_KEY") or os.getenv("GOOGLE_API_KEY")
+genai.configure(api_key=api_key)
+
+print("Listing available Gemini models...")
+try:
+ for m in genai.list_models():
+ if 'generateContent' in m.supported_generation_methods:
+ print(m.name)
+except Exception as e:
+ print(f"List models failed: {e}")
+
+model_name = "gemini-1.5-flash"
+print(f"\nTesting model: {model_name}")
+
+try:
+ model = genai.GenerativeModel(model_name)
+ response = model.generate_content("Hello, can you see this?")
+ print("Response:", response.text)
+except Exception as e:
+ print(f"Test failed: {e}")
diff --git a/check_gemini_clean.py b/check_gemini_clean.py
new file mode 100644
index 0000000000000000000000000000000000000000..751fe58712a396513b8f40bd9b9773b8407f90ad
--- /dev/null
+++ b/check_gemini_clean.py
@@ -0,0 +1,24 @@
+import os
+import google.generativeai as genai
+from dotenv import load_dotenv
+
+load_dotenv()
+
+api_key = os.getenv("GEMINI_API_KEY") or os.getenv("GOOGLE_API_KEY")
+genai.configure(api_key=api_key)
+
+candidates = [
+ "gemini-2.0-flash",
+ "gemini-2.0-flash-exp",
+ "models/gemini-2.0-flash"
+]
+
+for model_name in candidates:
+ print(f"\nTesting model: {model_name}")
+ try:
+ model = genai.GenerativeModel(model_name)
+ response = model.generate_content("Hello")
+ print(f"ā
Success with {model_name}: {response.text}")
+ break
+ except Exception as e:
+ print(f"ā Failed with {model_name}: {e}")
diff --git a/check_groq.py b/check_groq.py
new file mode 100644
index 0000000000000000000000000000000000000000..1f1cfc8daf0a05db639ec0c8a57894f8937e5d93
--- /dev/null
+++ b/check_groq.py
@@ -0,0 +1,11 @@
+import os
+from groq import Groq
+from dotenv import load_dotenv
+
+load_dotenv()
+client = Groq(api_key=os.getenv("GROQ_API_KEY"))
+
+print("Listing Groq models...")
+models = client.models.list()
+for m in models.data:
+ print(m.id)
diff --git a/check_groq_models.py b/check_groq_models.py
new file mode 100644
index 0000000000000000000000000000000000000000..95ef4f8e708e6ef50024201903a48ad7c058663c
--- /dev/null
+++ b/check_groq_models.py
@@ -0,0 +1,14 @@
+import os
+from groq import Groq
+from dotenv import load_dotenv
+
+load_dotenv()
+
+try:
+ client = Groq(api_key=os.getenv("GROQ_API_KEY"))
+ models = client.models.list()
+ print("Available Models:")
+ for model in models.data:
+ print(f"- {model.id}")
+except Exception as e:
+ print(f"Error listing models: {e}")
diff --git a/check_groq_vision.py b/check_groq_vision.py
new file mode 100644
index 0000000000000000000000000000000000000000..2fef204aa21a954072c0646b9fd04ea68d80a0d2
--- /dev/null
+++ b/check_groq_vision.py
@@ -0,0 +1,36 @@
+import os
+from groq import Groq
+from dotenv import load_dotenv
+import base64
+
+load_dotenv()
+
+client = Groq(api_key=os.getenv("GROQ_API_KEY"))
+model = "llama-3.2-11b-vision-preview"
+
+print(f"Testing Groq Vision model: {model}")
+
+# Test 1: Image URL
+print("\n--- Test 1: Image URL ---")
+try:
+ image_url = "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/tasks/car.jpg?download=true"
+ completion = client.chat.completions.create(
+ model=model,
+ messages=[
+ {
+ "role": "user",
+ "content": [
+ {"type": "text", "text": "What's in this image?"},
+ {"type": "image_url", "image_url": {"url": image_url}},
+ ],
+ }
+ ],
+ temperature=1,
+ max_tokens=1024,
+ top_p=1,
+ stream=False,
+ stop=None,
+ )
+ print("Response:", completion.choices[0].message.content)
+except Exception as e:
+ print("Groq Vision failed:", e)
diff --git a/check_idefics.py b/check_idefics.py
new file mode 100644
index 0000000000000000000000000000000000000000..9a147a19999a3e66c6c125232153a5e26f9112f8
--- /dev/null
+++ b/check_idefics.py
@@ -0,0 +1,33 @@
+import os
+from huggingface_hub import InferenceClient
+from dotenv import load_dotenv
+
+load_dotenv()
+
+api_key = os.getenv("HF_TOKEN")
+client = InferenceClient(api_key=api_key)
+model = "HuggingFaceM4/idefics2-8b"
+
+print(f"Testing model: {model}")
+
+# Test 1: Image URL
+print("\n--- Test 1: Image URL ---")
+try:
+ image_url = "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/tasks/car.jpg?download=true"
+ messages = [
+ {
+ "role": "user",
+ "content": [
+ {"type": "image_url", "image_url": {"url": image_url}},
+ {"type": "text", "text": "What is in this image?"}
+ ]
+ }
+ ]
+ completion = client.chat.completions.create(
+ model=model,
+ messages=messages,
+ max_tokens=100
+ )
+ print("Response:", completion.choices[0].message.content)
+except Exception as e:
+ print("Image URL failed:", e)
diff --git a/check_idefics_raw.py b/check_idefics_raw.py
new file mode 100644
index 0000000000000000000000000000000000000000..63e5737d1812d52a2843276bbd54f98858b8b93c
--- /dev/null
+++ b/check_idefics_raw.py
@@ -0,0 +1,29 @@
+import os
+import requests
+import json
+from dotenv import load_dotenv
+
+load_dotenv()
+
+api_key = os.getenv("HF_TOKEN")
+model = "HuggingFaceM4/idefics2-8b"
+url = f"https://router.huggingface.co/models/{model}"
+
+headers = {"Authorization": f"Bearer {api_key}"}
+
+print(f"Testing URL: {url}")
+
+# Test A: Simple text inputs
+print("\n--- Test A: Simple Text ---")
+response = requests.post(url, headers=headers, json={"inputs": "Hello"})
+print(f"Status: {response.status_code}")
+print("Response:", response.text)
+
+# Test B: Formatted inputs (Standard for some VLM APIs)
+# Often they accept { "inputs": "User: ...", "parameters": ... }
+print("\n--- Test B: Formatted Prompt ---")
+image_url = "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/tasks/car.jpg?download=true"
+prompt = f"User:  Describe this image. Drop Product Image Here Supports JPG, PNG, WEBP Drop Product Image Here Supports JPG, PNG, WEBPMerchFlow AI
+Enterprise Edition
+Generated Output
+Error: dashboard.html not found
"
+@app.post("/generate-catalog")
+async def generate_catalog(file: UploadFile = File(...)):
+ try:
+ # 1. Save Temp File
+ os.makedirs("uploads", exist_ok=True)
+ file_path = f"uploads/{file.filename}"
+ with open(file_path, "wb") as f:
+ f.write(await file.read())
+ # 2. Run AI Pipeline
+ visual_data = await visual_agent.analyze_image(file_path)
+
+ query = f"{visual_data.get('main_color', '')} {visual_data.get('product_type', 'product')}"
+ seo_keywords = memory_agent.retrieve_keywords(query)
+
+ listing = writer_agent.write_listing(visual_data, seo_keywords)
+
+ # 3. Construct Final Payload
+ final_data = {
+ "visual_data": visual_data,
+ "seo_keywords": seo_keywords,
+ "listing": listing
+ }
+ # 4. ā” N8N AUTOMATION TRIGGER ā”
+ n8n_url = os.getenv("N8N_WEBHOOK_URL")
+ if n8n_url:
+ print(f"š Sending data to N8N: {n8n_url}")
+ # Fire and forget (don't make the user wait for n8n)
+ asyncio.create_task(send_to_n8n(n8n_url, final_data))
+
+ # Cleanup
+ if os.path.exists(file_path):
+ os.remove(file_path)
+
+ return JSONResponse(content=final_data)
+ except Exception as e:
+ return JSONResponse(content={"error": str(e)}, status_code=500)
+# Async Helper to send data without blocking
+async def send_to_n8n(url, data):
+ try:
+ async with httpx.AsyncClient() as client:
+ await client.post(url, json=data, timeout=5.0)
+ print("ā
N8N Webhook Sent Successfully")
+ except Exception as e:
+ print(f"ā N8N Webhook Failed: {e}")
+if __name__ == "__main__":
+ import uvicorn
+ uvicorn.run(app, host="0.0.0.0", port=7860)
+'''
+ with open("main.py", "w", encoding="utf-8") as f:
+ f.write(main_content)
+ print("Updated main.py with N8N integration logic.")
+
+def deploy():
+ try:
+ subprocess.run(["git", "add", "."], check=True)
+ # Check if there are changes to commit
+ status = subprocess.run(["git", "status", "--porcelain"], capture_output=True, text=True)
+ if status.stdout.strip():
+ subprocess.run(["git", "commit", "-m", "Add N8N Integration"], check=True)
+ print("Git commit successful.")
+ else:
+ print("No changes to commit.")
+
+ print("Pushing to space...")
+ subprocess.run(["git", "push", "space", "clean_deploy:main"], check=True)
+ print("ā
Successfully deployed to Hugging Face Space.")
+
+ except subprocess.CalledProcessError as e:
+ print(f"ā Deployment failed: {e}")
+
+if __name__ == "__main__":
+ print("Starting N8N Integration Setup...")
+ update_requirements()
+ update_main()
+ deploy()
+ print("ā
connect_n8n.py completed.")
diff --git a/create_dockerfile.py b/create_dockerfile.py
new file mode 100644
index 0000000000000000000000000000000000000000..03ef0f80ff9e027e30a57aef41a8c93db4f1a38d
--- /dev/null
+++ b/create_dockerfile.py
@@ -0,0 +1,50 @@
+import subprocess
+import sys
+
+def run_command(command):
+ print(f"Running: {command}")
+ try:
+ # shell=True allows us to run the command string exactly as provided
+ subprocess.run(command, shell=True, check=True)
+ except subprocess.CalledProcessError as e:
+ print(f"Error executing command '{command}': {e}")
+ sys.exit(1)
+
+def main():
+ # 1. Create Dockerfile
+ dockerfile_content = """FROM python:3.9
+WORKDIR /code
+COPY ./requirements.txt /code/requirements.txt
+RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
+COPY . /code
+# Fix permissions for libraries that write to home
+RUN mkdir -p /tmp/home
+ENV HOME=/tmp/home
+# Start the FastAPI server on port 7860 (required by Hugging Face)
+CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860"]
+"""
+
+ print("Creating Dockerfile...")
+ try:
+ with open("Dockerfile", "w", newline='\n') as f:
+ f.write(dockerfile_content)
+ print("Dockerfile created successfully.")
+ except Exception as e:
+ print(f"Failed to create Dockerfile: {e}")
+ sys.exit(1)
+
+ # 2. Push to Space
+ print("Executing Git commands...")
+ commands = [
+ 'git add Dockerfile',
+ 'git commit -m "Add Dockerfile for Hugging Face deployment"',
+ 'git push -f space clean_deploy:main'
+ ]
+
+ for cmd in commands:
+ run_command(cmd)
+
+ print("\ncreate_dockerfile.py execution completed.")
+
+if __name__ == "__main__":
+ main()
diff --git a/dashboard.html b/dashboard.html
new file mode 100644
index 0000000000000000000000000000000000000000..700dc4963cdb914a1ddc7662a761f0908f286f8e
--- /dev/null
+++ b/dashboard.html
@@ -0,0 +1,455 @@
+
+
+
+
+MerchFlow AI
+Input Data
+Step 1 of 2
+Generated Output
+Error: dashboard.html not found. Please ensure the file exists.
"
+# 2. SERVE DASHBOARD AT ROOT (Home Page)
+@app.get("/", response_class=HTMLResponse)
+async def read_root():
+ return dashboard_html
+# 3. KEEP /dashboard ROUTE AS BACKUP
+@app.get("/dashboard", response_class=HTMLResponse)
+async def read_dashboard():
+ return dashboard_html
+@app.post("/analyze")
+async def analyze_merch(file: UploadFile = File(...)):
+ try:
+ os.makedirs("uploads", exist_ok=True)
+ file_path = f"uploads/{file.filename}"
+ with open(file_path, "wb") as f:
+ f.write(await file.read())
+ result = await visual_agent.analyze_image(file_path)
+
+ if os.path.exists(file_path):
+ os.remove(file_path)
+
+ return JSONResponse(content=result)
+ except Exception as e:
+ return JSONResponse(content={"error": str(e)}, status_code=500)
+if __name__ == "__main__":
+ import uvicorn
+ uvicorn.run(app, host="0.0.0.0", port=7860)
+"""
+
+ # Overwrite main.py
+ print("Overwriting main.py...")
+ try:
+ with open("main.py", "w", encoding="utf-8") as f:
+ f.write(main_py_content)
+ print("Successfully updated main.py")
+ except Exception as e:
+ print(f"Error writing main.py: {e}")
+ return
+
+ # Define git commands
+ git_commands = [
+ ["git", "add", "main.py"],
+ ["git", "commit", "-m", "Fix dashboard 404 by serving HTML at root"],
+ ["git", "push", "space", "clean_deploy:main"]
+ ]
+
+ # Run git commands
+ print("\nRunning git commands...")
+ for cmd in git_commands:
+ print(f"Executing: {' '.join(cmd)}")
+ try:
+ subprocess.run(cmd, check=True)
+ except subprocess.CalledProcessError as e:
+ print(f"Command failed: {e}")
+ # If commit fails (e.g. nothing to commit), we might want to continue or stop.
+ # But push should definitely happen if commit works.
+ # If commit fails because "nothing to commit, working tree clean", push might still be relevant if previous commit wasn't pushed?
+ # But the user logic implies we just made a change to main.py, so commit should succeed unless main.py was ALREADY this content.
+ # We will continue to try push even if commit fails, just in case.
+ # But wait, if commit fails, push might proceed.
+ pass
+
+ print("\nfix_dashboard_routing.py completed.")
+
+if __name__ == "__main__":
+ main()
diff --git a/fix_google_key.py b/fix_google_key.py
new file mode 100644
index 0000000000000000000000000000000000000000..cdabb024b499d627225436f7f41e66ecc88d51e3
--- /dev/null
+++ b/fix_google_key.py
@@ -0,0 +1,48 @@
+import os
+import sys
+
+# Force UTF-8 output for Windows terminals
+sys.stdout.reconfigure(encoding='utf-8')
+
+# 1. Update .env
+env_path = ".env"
+key = "GOOGLE_API_KEY"
+value = "AIzaSyDgIkagGBciWNZDTn07OlfY9tVPvo6KJ1on"
+
+print(f"Updating {key} in .env...")
+
+lines = []
+if os.path.exists(env_path):
+ with open(env_path, "r", encoding="utf-8") as f:
+ lines = f.readlines()
+
+found = False
+new_lines = []
+for line in lines:
+ if line.startswith(f"{key}="):
+ new_lines.append(f"{key}={value}\n")
+ found = True
+ else:
+ new_lines.append(line)
+
+if not found:
+ if new_lines and not new_lines[-1].endswith('\n'):
+ new_lines.append('\n')
+ new_lines.append(f"{key}={value}\n")
+
+with open(env_path, "w", encoding="utf-8") as f:
+ f.writelines(new_lines)
+
+print(f"ā
Updated {key} in .env")
+
+# 2. Upload to Cloud
+print("Syncing secrets to Hugging Face Space...")
+try:
+ # Build path to ensure we can import upload_secrets
+ sys.path.append(os.getcwd())
+ from upload_secrets import upload_secrets
+
+ upload_secrets()
+ print("ā
Google Key saved locally and uploaded to Hugging Face!")
+except Exception as e:
+ print(f"ā Failed to sync: {e}")
diff --git a/fix_readme.py b/fix_readme.py
new file mode 100644
index 0000000000000000000000000000000000000000..cf13f7160c11abcd35e60e2f718f561b95e9b59a
--- /dev/null
+++ b/fix_readme.py
@@ -0,0 +1,42 @@
+import os
+import sys
+import subprocess
+
+# Force UTF-8 output for Windows terminals
+sys.stdout.reconfigure(encoding='utf-8')
+
+readme_content = """---
+title: MerchFlow AI
+emoji: š
+colorFrom: blue
+colorTo: indigo
+sdk: docker
+pinned: false
+---
+# MerchFlow AI
+An AI-powered merchandising agent.
+"""
+
+def run_command(command):
+ print(f"Running: {command}")
+ try:
+ subprocess.run(command, check=True, shell=True)
+ print("ā
Success")
+ except subprocess.CalledProcessError as e:
+ print(f"ā Error: {e}")
+ # Don't exit, try to continue or let user see error
+
+def fix_readme():
+ print("Writing README.md...")
+ with open("README.md", "w", encoding="utf-8") as f:
+ f.write(readme_content)
+ print("ā
Created README.md")
+
+ print("Deploying changes...")
+ run_command("git add README.md")
+ run_command('git commit -m "Add Hugging Face configuration"')
+ run_command("git push space clean_deploy:main")
+ print("ā
Configuration fixed and pushed!")
+
+if __name__ == "__main__":
+ fix_readme()
diff --git a/fix_vision_core.py b/fix_vision_core.py
new file mode 100644
index 0000000000000000000000000000000000000000..5ab5455ebe5f3da50e3432d33ad0ce0366af50e7
--- /dev/null
+++ b/fix_vision_core.py
@@ -0,0 +1,90 @@
+import os
+import subprocess
+
+def fix_vision_core():
+ # Content for agents/visual_analyst.py
+ content = """import os
+import json
+import asyncio
+import google.generativeai as genai
+from PIL import Image
+from dotenv import load_dotenv
+
+load_dotenv()
+
+class VisualAnalyst:
+ def __init__(self):
+ api_key = os.getenv("GEMINI_API_KEY")
+ if not api_key:
+ print("ā ļø GEMINI_API_KEY missing")
+
+ genai.configure(api_key=api_key)
+ # Use the modern, faster Flash model
+ self.model = genai.GenerativeModel('gemini-1.5-flash')
+
+ async def analyze_image(self, image_path: str):
+ print(f"šļø Analyzing image: {image_path}")
+
+ try:
+ # 1. Load image properly with Pillow (Fixes format issues)
+ img = Image.open(image_path)
+
+ # 2. Define the prompt
+ prompt = \"\"\"
+ Analyze this product image for an e-commerce listing.
+ Return ONLY a raw JSON object (no markdown formatting) with this structure:
+ {
+ "main_color": "string",
+ "product_type": "string",
+ "design_style": "string (minimalist, streetwear, vintage, etc)",
+ "visual_features": ["list", "of", "visible", "features"],
+ "suggested_title": "creative product title",
+ "condition_guess": "new/used"
+ }
+ \"\"\"
+
+ # 3. Run in a thread to prevent blocking (Sync to Async wrapper)
+ response = await asyncio.to_thread(
+ self.model.generate_content,
+ [prompt, img]
+ )
+
+ # 4. Clean and Parse JSON
+ text_response = response.text.replace('```json', '').replace('```', '').strip()
+ return json.loads(text_response)
+ except Exception as e:
+ print(f"ā Vision Error: {e}")
+ # Return a Safe Fallback (Simulation)
+ return {
+ "main_color": "Unknown",
+ "product_type": "Unidentified Item",
+ "design_style": "Standard",
+ "visual_features": ["Error analyzing image"],
+ "suggested_title": "Manual Review Needed",
+ "condition_guess": "New"
+ }
+"""
+ # Write the file
+ os.makedirs("agents", exist_ok=True)
+ with open("agents/visual_analyst.py", "w", encoding="utf-8") as f:
+ f.write(content)
+ print("ā
agents/visual_analyst.py updated.")
+
+ # Git operations
+ print("š Pushing to HuggingFace...")
+ commands = [
+ ["git", "add", "agents/visual_analyst.py"],
+ ["git", "commit", "-m", "Fix vision core and error handling"],
+ ["git", "push", "space", "clean_deploy:main"]
+ ]
+
+ for cmd in commands:
+ try:
+ print(f"Running: {' '.join(cmd)}")
+ subprocess.run(cmd, check=True)
+ except subprocess.CalledProcessError as e:
+ print(f"ā ļø Command failed: {e}")
+ # Continue even if commit fails (e.g. prompt already applied)
+
+if __name__ == "__main__":
+ fix_vision_core()
diff --git a/force_deploy.py b/force_deploy.py
new file mode 100644
index 0000000000000000000000000000000000000000..1de4fee4d978815996d1dbb76aaa5f1792cca8ca
--- /dev/null
+++ b/force_deploy.py
@@ -0,0 +1,62 @@
+"""
+force_deploy.py
+Force-deploys the current codebase to the Hugging Face Space.
+Handles HF binary file restrictions by untracking offending files first.
+"""
+import subprocess
+import sys
+
+def run(cmd, allow_fail=False):
+ print(f"\n>>> {cmd}")
+ result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
+ if result.stdout:
+ print(result.stdout.strip())
+ if result.stderr:
+ print(result.stderr.strip())
+ if result.returncode != 0 and not allow_fail:
+ print(f"Command failed with exit code {result.returncode}")
+ sys.exit(1)
+ return result
+
+if __name__ == "__main__":
+ print("=" * 60)
+ print(" MerchFlow AI ā Force Deploy to Hugging Face Space")
+ print("=" * 60)
+
+ # 0. Remove binary files from Git tracking (keep local copies)
+ binaries = [
+ "stitch_merchflow_ai_dashboard.zip",
+ "screen.jpg",
+ "test_image.jpg",
+ ]
+ for b in binaries:
+ run(f"git rm --cached {b}", allow_fail=True)
+
+ # Ensure .gitignore blocks them from being re-added
+ ignore_entries = binaries + ["*.zip", "*.jpg", "*.jpeg", "*.png"]
+ try:
+ existing = open(".gitignore", "r").read()
+ except FileNotFoundError:
+ existing = ""
+ with open(".gitignore", "a") as f:
+ for entry in ignore_entries:
+ if entry not in existing:
+ f.write(f"\n{entry}")
+ print("\nUpdated .gitignore with binary exclusions.")
+
+ # 1. Stage everything
+ run("git add .")
+
+ # 2. Commit (allow fail in case nothing changed)
+ run('git commit -m "Critical Deployment: Force update of License, Security Fixes, and Branding"', allow_fail=True)
+
+ # 3. Force push HEAD to space remote's main branch
+ run("git push --force space HEAD:main")
+
+ # 4. Print the latest commit for verification
+ print("\n" + "=" * 60)
+ print(" Deployed Commit:")
+ print("=" * 60)
+ run("git log -1")
+
+ print("\nā
Force deploy complete.")
diff --git a/force_push.py b/force_push.py
new file mode 100644
index 0000000000000000000000000000000000000000..2bd720807e49693d7bf0c9a1a56595b32b234274
--- /dev/null
+++ b/force_push.py
@@ -0,0 +1,14 @@
+import subprocess
+
+def main():
+ print("Executing force push...")
+ command = "git push --force space HEAD:main"
+ result = subprocess.run(command, shell=True, capture_output=True, text=True)
+
+ print("STDOUT:")
+ print(result.stdout)
+ print("STDERR:")
+ print(result.stderr)
+
+if __name__ == "__main__":
+ main()
diff --git a/glassui.html b/glassui.html
new file mode 100644
index 0000000000000000000000000000000000000000..700dc4963cdb914a1ddc7662a761f0908f286f8e
--- /dev/null
+++ b/glassui.html
@@ -0,0 +1,455 @@
+
+
+
+
+MerchFlow AI
+Input Data
+Step 1 of 2
+Generated Output
+Error: dashboard.html not found
"
+
+@app.post("/generate-catalog")
+async def generate_catalog(file: UploadFile = File(...)):
+ file_path = None
+ try:
+ # 1. Save Temp File
+ os.makedirs("uploads", exist_ok=True)
+ file_path = f"uploads/{file.filename}"
+ with open(file_path, "wb") as f:
+ f.write(await file.read())
+
+ # 2. Run AI Pipeline (Sequential)
+ print("ā¶ļø Starting Visual Analysis...")
+ visual_data = await visual_agent.analyze_image(file_path)
+
+ print("ā¶ļø Retrieving Keywords...")
+ query = f"{visual_data.get('main_color', '')} {visual_data.get('product_type', 'product')}"
+ seo_keywords = memory_agent.retrieve_keywords(query)
+
+ print("ā¶ļø Writing Listing...")
+ listing = writer_agent.write_listing(visual_data, seo_keywords)
+
+ # 3. Construct Final Payload
+ final_data = {
+ "visual_data": visual_data,
+ "seo_keywords": seo_keywords,
+ "listing": listing
+ }
+
+ # 4. Async N8n Trigger (Before Return)
+ # Constraint: "Must happen after agents finish but before returning"
+ n8n_url = os.getenv("N8N_WEBHOOK_URL")
+ if n8n_url:
+ print(f"š Triggering N8N Webhook: {n8n_url}")
+ await trigger_n8n_webhook(n8n_url, final_data)
+ else:
+ print("ā¹ļø N8N_WEBHOOK_URL not set, skipping webhook.")
+
+ return JSONResponse(content=final_data)
+
+ except Exception as e:
+ error_details = traceback.format_exc()
+ print(f"ā Error in generate-catalog: {e}")
+ print(error_details)
+ return JSONResponse(
+ content={
+ "error": "An internal server error occurred.",
+ "type": type(e).__name__
+ },
+ status_code=500
+ )
+
+ finally:
+ # Cleanup
+ if file_path and os.path.exists(file_path):
+ try:
+ os.remove(file_path)
+ except Exception as cleanup_error:
+ print(f"ā ļø Cleanup Warning: {cleanup_error}")
+
+async def trigger_n8n_webhook(url: str, data: dict):
+ """
+ Sends data to n8n webhook asynchronously using httpx.
+ """
+ async with httpx.AsyncClient() as client:
+ try:
+ # We await the post to ensure it's sent before returning,
+ # fulfilling the user constraint.
+ response = await client.post(url, json=data, timeout=10.0)
+ response.raise_for_status()
+ print(f"ā
N8N Webhook Success: {response.status_code}")
+ except httpx.HTTPStatusError as e:
+ print(f"ā N8N Webhook HTTP Error: {e.response.status_code} - {e.response.text}")
+ except Exception as e:
+ print(f"ā N8N Webhook Connection Failed: {e}")
+
+if __name__ == "__main__":
+ import uvicorn
+ uvicorn.run(app, host="0.0.0.0", port=7860)
diff --git a/projects.json b/projects.json
new file mode 100644
index 0000000000000000000000000000000000000000..d53acced75f5520fb15fcbab4c0eafa5ed94ae72
--- /dev/null
+++ b/projects.json
@@ -0,0 +1,757 @@
+{
+ "projects": [
+ {
+ "createTime": "2026-02-25T18:25:28.653824Z",
+ "designTheme": {
+ "colorMode": "DARK",
+ "customColor": "#7f0df2",
+ "font": "INTER",
+ "roundness": "ROUND_EIGHT",
+ "saturation": 2
+ },
+ "deviceType": "DESKTOP",
+ "metadata": {
+ "userRole": "OWNER"
+ },
+ "name": "projects/13453765122851154258",
+ "origin": "STITCH",
+ "projectType": "TEXT_TO_UI_PRO",
+ "screenInstances": [
+ {
+ "height": 600,
+ "id": "3a95b353-57ca-4c29-a0e0-ad8398fe3db3",
+ "sourceScreen": "projects/13453765122851154258/screens/15822672384834121564",
+ "type": "SCREEN_INSTANCE",
+ "width": 600
+ },
+ {
+ "height": 1024,
+ "hidden": true,
+ "id": "8bc0a17301cb44cf9a3905542e09db2b",
+ "sourceScreen": "projects/13453765122851154258/screens/8bc0a17301cb44cf9a3905542e09db2b",
+ "width": 1280,
+ "x": 753,
+ "y": -163
+ },
+ {
+ "height": 1024,
+ "hidden": true,
+ "id": "90bae99f55b046049c55926a7da7d787",
+ "sourceScreen": "projects/13453765122851154258/screens/90bae99f55b046049c55926a7da7d787",
+ "width": 1280,
+ "x": 804,
+ "y": -332
+ },
+ {
+ "height": 1024,
+ "hidden": true,
+ "id": "bf36d828ed3846cbb6b81f2b44d7e42d",
+ "sourceScreen": "projects/13453765122851154258/screens/bf36d828ed3846cbb6b81f2b44d7e42d",
+ "width": 1280,
+ "x": 664
+ },
+ {
+ "height": 1024,
+ "id": "cf3af7f8a9e74daf85096241ed88c75c",
+ "isFavourite": true,
+ "sourceScreen": "projects/13453765122851154258/screens/cf3af7f8a9e74daf85096241ed88c75c",
+ "width": 1280,
+ "x": 970,
+ "y": -179
+ }
+ ],
+ "thumbnailScreenshot": {
+ "downloadUrl": "https://lh3.googleusercontent.com/aida/AOfcidVwihSF-Kd8W6zf1x913xCGZ7Wc7ZizpmwOg0SARD0a-Q43pMiPyRmB1fnygdmusXnFA8x_YHU_Y9zfRsj8Lp-Unj12g94PnahnjQufHEuMvdhS3JlJZc8dka2Qi3aty3PLeRGvzTgCL9BlN6Nmip5XZrLP6leo9ZKq-hg5iKfKLr7TaKmnN2WSum1VR9MOrqvuWkI2qMMakkvS2NZHKivzUTXVpmCkGNOMZ-2inEisr2GusRBbBrtyChqO",
+ "name": "projects/13453765122851154258/files/a19c43fc67b0469fbc1174a000338555"
+ },
+ "title": "Merchflow AI",
+ "updateTime": "2026-02-25T18:44:08.130497Z",
+ "visibility": "PRIVATE"
+ },
+ {
+ "createTime": "2026-02-12T18:28:57.583433Z",
+ "designTheme": {
+ "colorMode": "DARK",
+ "customColor": "#c8f906",
+ "font": "INTER",
+ "roundness": "ROUND_FULL",
+ "saturation": 2
+ },
+ "deviceType": "MOBILE",
+ "metadata": {
+ "userRole": "OWNER"
+ },
+ "name": "projects/14416597482131741463",
+ "origin": "STITCH",
+ "projectType": "TEXT_TO_UI_PRO",
+ "screenInstances": [
+ {
+ "height": 884,
+ "id": "91dd95e95696415196414912a882c7e7",
+ "sourceScreen": "projects/14416597482131741463/screens/91dd95e95696415196414912a882c7e7",
+ "width": 390,
+ "x": 1344
+ },
+ {
+ "height": 884,
+ "id": "b1053b8349ee4e4992f6cfbc92dab3fc",
+ "sourceScreen": "projects/14416597482131741463/screens/b1053b8349ee4e4992f6cfbc92dab3fc",
+ "width": 390
+ },
+ {
+ "height": 1324,
+ "id": "c7aff944733e486d983a63392b7aa65f",
+ "sourceScreen": "projects/14416597482131741463/screens/c7aff944733e486d983a63392b7aa65f",
+ "width": 390,
+ "x": 2252
+ },
+ {
+ "height": 2060,
+ "id": "d2e32096f621489fb0d0a4195928f9f4",
+ "sourceScreen": "projects/14416597482131741463/screens/d2e32096f621489fb0d0a4195928f9f4",
+ "width": 390,
+ "x": 1798
+ },
+ {
+ "height": 2035,
+ "hidden": true,
+ "id": "ec37f6f67858486198bf9eae665c2825",
+ "sourceScreen": "projects/14416597482131741463/screens/ec37f6f67858486198bf9eae665c2825",
+ "width": 390,
+ "y": 1942
+ }
+ ],
+ "thumbnailScreenshot": {
+ "downloadUrl": "https://lh3.googleusercontent.com/aida/AOfcidVZhkC2L9hEo0egk8Y_EN0jF1M1U_xsJU_XBzPtnGnWIxWpCq0qqZvyYEh8cnRo8KB3IQpPkjX2vqxsBWcBm4TOzSaPJgGjK5LFat9ngLGaoMcZLSiUOc3TdC9Rbovm408BEpUWbIl3RASEla-WXi40EJv-ELimBTkuluXvPUwhL99y4Rub1UkfFpxudinWZedCzJ44QJD8VfUKzC-OW_zruNv7L4wxLhzfysunADtKIvfKkJ7-5uEceBY",
+ "name": "projects/14416597482131741463/files/1dcb6251d53346438730e8fe145924d2"
+ },
+ "title": "House of Nutrition Landing Page",
+ "updateTime": "2026-02-12T18:40:45.529842Z",
+ "visibility": "PRIVATE"
+ },
+ {
+ "createTime": "2026-02-09T17:36:41.806831Z",
+ "designTheme": {
+ "colorMode": "DARK",
+ "customColor": "#2b2bee",
+ "font": "BE_VIETNAM_PRO",
+ "roundness": "ROUND_EIGHT",
+ "saturation": 3
+ },
+ "deviceType": "MOBILE",
+ "metadata": {
+ "userRole": "OWNER"
+ },
+ "name": "projects/14876616641695088904",
+ "origin": "STITCH",
+ "projectType": "TEXT_TO_UI_PRO",
+ "screenInstances": [
+ {
+ "height": 884,
+ "id": "0403e315651948b28aafcfa4b3b0032c",
+ "sourceScreen": "projects/14876616641695088904/screens/0403e315651948b28aafcfa4b3b0032c",
+ "width": 390,
+ "y": 13089
+ },
+ {
+ "height": 884,
+ "id": "04cd0b2f4ce442d5affc2f5b75b26de3",
+ "sourceScreen": "projects/14876616641695088904/screens/04cd0b2f4ce442d5affc2f5b75b26de3",
+ "width": 390,
+ "x": 454,
+ "y": 13089
+ },
+ {
+ "height": 884,
+ "id": "0e70c9d66ddf489d8365c61004cca425",
+ "sourceScreen": "projects/14876616641695088904/screens/0e70c9d66ddf489d8365c61004cca425",
+ "width": 390,
+ "y": 19717
+ },
+ {
+ "height": 1099,
+ "id": "1336736c68fd4de09836d79018745afd",
+ "sourceScreen": "projects/14876616641695088904/screens/1336736c68fd4de09836d79018745afd",
+ "width": 390,
+ "y": 15417
+ },
+ {
+ "height": 1103,
+ "id": "1f8518adcc3542efa40b9d7d78681cc4",
+ "sourceScreen": "projects/14876616641695088904/screens/1f8518adcc3542efa40b9d7d78681cc4",
+ "width": 390,
+ "y": 9443
+ },
+ {
+ "height": 884,
+ "id": "24b25983e21f4042a6f42c0eab391114",
+ "sourceScreen": "projects/14876616641695088904/screens/24b25983e21f4042a6f42c0eab391114",
+ "width": 390,
+ "y": 24500
+ },
+ {
+ "height": 884,
+ "id": "2c61cd9c59ed44ceb646fc27e1d6348d",
+ "sourceScreen": "projects/14876616641695088904/screens/2c61cd9c59ed44ceb646fc27e1d6348d",
+ "width": 390,
+ "y": 5189
+ },
+ {
+ "height": 1048,
+ "id": "2d60fa1bc18a4ce7abb89d22a5afc3e6",
+ "sourceScreen": "projects/14876616641695088904/screens/2d60fa1bc18a4ce7abb89d22a5afc3e6",
+ "width": 390,
+ "y": 8139
+ },
+ {
+ "height": 1554,
+ "id": "30d3b1682ceb48aab8c017a887aa33d2",
+ "sourceScreen": "projects/14876616641695088904/screens/30d3b1682ceb48aab8c017a887aa33d2",
+ "width": 390,
+ "y": 6329
+ },
+ {
+ "height": 884,
+ "id": "5524c3f2f11b4e19974c68de88ff0501",
+ "sourceScreen": "projects/14876616641695088904/screens/5524c3f2f11b4e19974c68de88ff0501",
+ "width": 390,
+ "y": 4049
+ },
+ {
+ "height": 803,
+ "id": "5fff4884df894eb599d07ee74e8413e7",
+ "sourceScreen": "projects/14876616641695088904/screens/5fff4884df894eb599d07ee74e8413e7",
+ "width": 390,
+ "x": 908,
+ "y": 14229
+ },
+ {
+ "height": 1266,
+ "id": "6ae63c719a77450b83d1bd87b642ecfb",
+ "sourceScreen": "projects/14876616641695088904/screens/6ae63c719a77450b83d1bd87b642ecfb",
+ "width": 390,
+ "y": 16772
+ },
+ {
+ "height": 799,
+ "id": "866e1584496f45dc905ea6a347de4da4",
+ "sourceScreen": "projects/14876616641695088904/screens/866e1584496f45dc905ea6a347de4da4",
+ "width": 390,
+ "x": 454,
+ "y": 28270
+ },
+ {
+ "height": 891,
+ "id": "a664c737e8f546e182c07489ea60edf3",
+ "sourceScreen": "projects/14876616641695088904/screens/a664c737e8f546e182c07489ea60edf3",
+ "width": 390,
+ "y": 11942
+ },
+ {
+ "height": 1144,
+ "id": "aa56e4e88beb4f63b9d9a6634788ede6",
+ "sourceScreen": "projects/14876616641695088904/screens/aa56e4e88beb4f63b9d9a6634788ede6",
+ "width": 390,
+ "y": 2649
+ },
+ {
+ "height": 723,
+ "id": "ae015ad1ae8445da9293c6ca546ed92d",
+ "sourceScreen": "projects/14876616641695088904/screens/ae015ad1ae8445da9293c6ca546ed92d",
+ "width": 390,
+ "x": 454,
+ "y": 24500
+ },
+ {
+ "height": 1133,
+ "id": "b0132faa7cfb474193b3a035e4143d0d",
+ "sourceScreen": "projects/14876616641695088904/screens/b0132faa7cfb474193b3a035e4143d0d",
+ "width": 390,
+ "y": 23111
+ },
+ {
+ "height": 884,
+ "id": "b358d7f040d04d6180133f56b6914adc",
+ "sourceScreen": "projects/14876616641695088904/screens/b358d7f040d04d6180133f56b6914adc",
+ "width": 390,
+ "y": 25640
+ },
+ {
+ "height": 1998,
+ "id": "b66bbd8e89c043bd9d2c107c3e2c4ce8",
+ "sourceScreen": "projects/14876616641695088904/screens/b66bbd8e89c043bd9d2c107c3e2c4ce8",
+ "width": 390,
+ "y": 20857
+ },
+ {
+ "height": 1167,
+ "id": "bb10a997745c466585024c58ca800275",
+ "sourceScreen": "projects/14876616641695088904/screens/bb10a997745c466585024c58ca800275",
+ "width": 390,
+ "y": 18294
+ },
+ {
+ "height": 1234,
+ "id": "c14aacd0bd5e4d1c86ddf6af3c12a96e",
+ "sourceScreen": "projects/14876616641695088904/screens/c14aacd0bd5e4d1c86ddf6af3c12a96e",
+ "width": 390,
+ "y": 26780
+ },
+ {
+ "height": 1129,
+ "id": "c519daadb8054fed99550cf3e0fde652",
+ "sourceScreen": "projects/14876616641695088904/screens/c519daadb8054fed99550cf3e0fde652",
+ "width": 390,
+ "x": 454,
+ "y": 16772
+ },
+ {
+ "height": 884,
+ "id": "c6f4b4494ffa49b68d34a27b9e864eaf",
+ "sourceScreen": "projects/14876616641695088904/screens/c6f4b4494ffa49b68d34a27b9e864eaf",
+ "width": 390,
+ "y": 10802
+ },
+ {
+ "height": 932,
+ "id": "cb586b03a673475e9b0dfceef53f8ca6",
+ "sourceScreen": "projects/14876616641695088904/screens/cb586b03a673475e9b0dfceef53f8ca6",
+ "width": 390,
+ "x": 454,
+ "y": 14229
+ },
+ {
+ "height": 1253,
+ "id": "cfa8fc268bb54eee9f3daa823a2a335d",
+ "sourceScreen": "projects/14876616641695088904/screens/cfa8fc268bb54eee9f3daa823a2a335d",
+ "width": 390
+ },
+ {
+ "height": 884,
+ "id": "eee54a4b5b1a4380af6efcd7b12e0bd3",
+ "sourceScreen": "projects/14876616641695088904/screens/eee54a4b5b1a4380af6efcd7b12e0bd3",
+ "width": 390,
+ "y": 28270
+ },
+ {
+ "height": 884,
+ "id": "fa2c9f9ac77d4822b3b601c47e9de473",
+ "sourceScreen": "projects/14876616641695088904/screens/fa2c9f9ac77d4822b3b601c47e9de473",
+ "width": 390,
+ "y": 1509
+ },
+ {
+ "height": 884,
+ "id": "fbf09e340a01431bbdcaca34b039e56d",
+ "sourceScreen": "projects/14876616641695088904/screens/fbf09e340a01431bbdcaca34b039e56d",
+ "width": 390,
+ "y": 14229
+ },
+ {
+ "height": 884,
+ "id": "ffe3420c4dd748c8a130dca9d59979b3",
+ "sourceScreen": "projects/14876616641695088904/screens/ffe3420c4dd748c8a130dca9d59979b3",
+ "width": 390,
+ "x": 454,
+ "y": 23111
+ }
+ ],
+ "thumbnailScreenshot": {
+ "downloadUrl": "https://lh3.googleusercontent.com/aida/AOfcidXd4EgEAE0cVviIP02RhFf-S0B_XyA1Hopm5w2-JY9vGBd17mifrmBlHT1oXWwOflFjPXn8DV8KZBqxMaFTTHniHs8RFBdb2ueceRnYGgeVVO3-0G6K40pelUeStYIBxEEf73oukJjvmPHaciY9-Dg40ysYFwgl7O9eQa5PrCWoub7X1hOGeLQ5yIJe2AhjWIeUwcDmW5CCFVA_QfCwpHwk78eOBPhfllfYXo18cOacHpBlj0BsB7pStfQ4",
+ "name": "projects/14876616641695088904/files/b1c6390f8db3459e8bd1a5ceaa570f86"
+ },
+ "title": "Aura TV",
+ "updateTime": "2026-02-09T18:54:15.490610Z",
+ "visibility": "PRIVATE"
+ },
+ {
+ "createTime": "2026-02-09T15:29:23.414547Z",
+ "designTheme": {},
+ "metadata": {
+ "userRole": "OWNER"
+ },
+ "name": "projects/10352760979295925874",
+ "origin": "STITCH",
+ "projectType": "PROJECT_DESIGN",
+ "title": "Draft README.md",
+ "updateTime": "2026-02-09T15:29:23.414547Z",
+ "visibility": "PRIVATE"
+ },
+ {
+ "createTime": "2026-02-07T20:10:39.013135Z",
+ "designTheme": {},
+ "metadata": {
+ "userRole": "OWNER"
+ },
+ "name": "projects/11474351006747027536",
+ "origin": "STITCH",
+ "projectType": "PROJECT_DESIGN",
+ "title": "Continuity Redis Queue Verification",
+ "updateTime": "2026-02-07T20:10:39.013135Z",
+ "visibility": "PRIVATE"
+ },
+ {
+ "createTime": "2026-01-02T21:37:47.732401Z",
+ "designTheme": {
+ "colorMode": "DARK",
+ "customColor": "#3713ec",
+ "font": "PLUS_JAKARTA_SANS",
+ "roundness": "ROUND_FULL",
+ "saturation": 2
+ },
+ "deviceType": "DESKTOP",
+ "metadata": {
+ "userRole": "OWNER"
+ },
+ "name": "projects/2433657010932190317",
+ "origin": "STITCH",
+ "projectType": "TEXT_TO_UI_PRO",
+ "screenInstances": [
+ {
+ "height": 714,
+ "id": "511f5d39a22641d49e65bf0c79823f82",
+ "sourceScreen": "projects/2433657010932190317/screens/511f5d39a22641d49e65bf0c79823f82",
+ "width": 1280,
+ "x": 7744
+ },
+ {
+ "height": 1024,
+ "id": "546a9408cf774fddb9e46be1b46465e5",
+ "sourceScreen": "projects/2433657010932190317/screens/546a9408cf774fddb9e46be1b46465e5",
+ "width": 1280,
+ "x": 5056
+ },
+ {
+ "height": 731,
+ "id": "a6d97010b7564a6f8051f63cf898316f",
+ "sourceScreen": "projects/2433657010932190317/screens/a6d97010b7564a6f8051f63cf898316f",
+ "width": 1280,
+ "x": 9088
+ },
+ {
+ "height": 714,
+ "id": "b644533eb2084dea937356b40582bd92",
+ "sourceScreen": "projects/2433657010932190317/screens/b644533eb2084dea937356b40582bd92",
+ "width": 1280,
+ "x": 6400
+ },
+ {
+ "height": 714,
+ "id": "c24b6584df4b4fb18457c47b74010a67",
+ "sourceScreen": "projects/2433657010932190317/screens/c24b6584df4b4fb18457c47b74010a67",
+ "width": 1280,
+ "x": 2368
+ },
+ {
+ "height": 714,
+ "id": "c50847373ed345fbbc29795455754fc5",
+ "sourceScreen": "projects/2433657010932190317/screens/c50847373ed345fbbc29795455754fc5",
+ "width": 1280,
+ "x": 3712
+ },
+ {
+ "height": 510,
+ "id": "d368a2a8-b872-478e-9027-950e819788da",
+ "sourceScreen": "projects/2433657010932190317/screens/5596238648239871334",
+ "width": 960
+ },
+ {
+ "height": 714,
+ "id": "fb95def010c2414bb055363bbe15e284",
+ "sourceScreen": "projects/2433657010932190317/screens/fb95def010c2414bb055363bbe15e284",
+ "width": 1280,
+ "x": 1024
+ }
+ ],
+ "thumbnailScreenshot": {
+ "downloadUrl": "https://lh3.googleusercontent.com/aida/AOfcidVylXDx3fy-lDIZCKxu93nj5ccLLXs96fgT-kzQ6e3UaIim6EqtjIbaUB4XZKM3PoNA6qUrYpWMJxvQIwjKVBM-qz3UR3FQJwC9Tl76t2iA4vnyhe9ISV3pCAeNhgjrTaC69PpanG_R6aLFBrpujothPlrBRlv2Hg4yeVcJKJXOBMnTdAtLoADQs_XN6iWmf6nL-aK-ZmvGygXBVAWvZcmwRthfEWidd2-AAtX7VcbOo8neHlGbqJJGcb8",
+ "name": "projects/2433657010932190317/files/f647e84464644998a58c66bd6e5ebcb8"
+ },
+ "title": "Spotify VisionOS Landing Page",
+ "updateTime": "2026-01-02T21:50:58.053355Z",
+ "visibility": "PRIVATE"
+ },
+ {
+ "createTime": "2025-12-26T13:23:42.313267Z",
+ "designTheme": {
+ "colorMode": "DARK",
+ "customColor": "#7f0df2",
+ "font": "SPACE_GROTESK",
+ "roundness": "ROUND_EIGHT",
+ "saturation": 3
+ },
+ "deviceType": "DESKTOP",
+ "metadata": {
+ "userRole": "OWNER"
+ },
+ "name": "projects/1557963788365082774",
+ "origin": "STITCH",
+ "projectType": "TEXT_TO_UI_PRO",
+ "screenInstances": [
+ {
+ "height": 1024,
+ "id": "8091fb58aba6426d9e2b71ba9a55c2b5",
+ "sourceScreen": "projects/1557963788365082774/screens/8091fb58aba6426d9e2b71ba9a55c2b5",
+ "width": 1280
+ }
+ ],
+ "thumbnailScreenshot": {
+ "downloadUrl": "https://lh3.googleusercontent.com/aida/AOfcidU6UBSHU6hzJW87gG7gN4j1xOrxZCn7CE6IAT5kIlkT-vEirE-4Tz8ZQRxshEMFYZhuOdDKWTO9Mh5ahuEv-hZbeHdr_QIN-R4TN3uUJu-hdEYUoaazVhGzlE61gSKcX0N2cYh6vMh3N5ZRqhbD3XV3Y9M7aVwYXN9mBxF69C6knKlmG7_c4qWlpMTwYAHPTblffkHs9CEGWJz3sW6VrkIxiSiq5Y_4X8ip8ZPkaa_DgwexB5vnd4Vv2g",
+ "name": "projects/1557963788365082774/files/6296130a1fe1424286a708b8fc4f8836"
+ },
+ "title": "Continuity Dashboard",
+ "updateTime": "2025-12-26T13:25:55.556755Z",
+ "visibility": "PRIVATE"
+ },
+ {
+ "createTime": "2025-12-13T21:24:48.432473Z",
+ "designTheme": {
+ "colorMode": "DARK",
+ "customColor": "#2bee79",
+ "font": "SPLINE_SANS",
+ "roundness": "ROUND_FULL",
+ "saturation": 3
+ },
+ "deviceType": "DESKTOP",
+ "metadata": {
+ "userRole": "OWNER"
+ },
+ "name": "projects/15558297327625973075",
+ "origin": "STITCH",
+ "projectType": "TEXT_TO_UI_PRO",
+ "screenInstances": [
+ {
+ "height": 1024,
+ "id": "bca4b7407c584037a859d6c46fe65e5f",
+ "sourceScreen": "projects/15558297327625973075/screens/bca4b7407c584037a859d6c46fe65e5f",
+ "width": 1280,
+ "y": 1280
+ },
+ {
+ "height": 1024,
+ "id": "dbca4b45d36c4ef2a026817001ad8956",
+ "sourceScreen": "projects/15558297327625973075/screens/dbca4b45d36c4ef2a026817001ad8956",
+ "width": 1280
+ }
+ ],
+ "thumbnailScreenshot": {
+ "downloadUrl": "https://lh3.googleusercontent.com/aida/AOfcidW8yfO5Sx9it1kpgHKj53Z7KlwcijvHU8ZB6yBXvp0GZthaVZG_0kRrvpVtXeFbQDH5vuw6eXaSEDYMrAIETHa2buKF6qXwSGIF6-6fantnZFQqsHrqge07zrapQmalOMukZNtyyoiJfihsr0NzZc0XUkDo5Ez_UyheSzPkJA5_G78cfvDD_telmYCe0iLmm-sSD_eklDtSGnag23a1_k6Eoc7l8F-10W6G5aEV8aojJUQiwg0NDkV0g2Fc",
+ "name": "projects/15558297327625973075/files/d90f04b5d3ba49ff80dce85cbc68fe13"
+ },
+ "title": "MerchFlow AI Dashboard",
+ "updateTime": "2025-12-13T21:30:52.096705Z",
+ "visibility": "PRIVATE"
+ },
+ {
+ "createTime": "2025-12-11T21:10:34.814568Z",
+ "designTheme": {
+ "colorMode": "LIGHT",
+ "customColor": "#f9f506",
+ "font": "SPLINE_SANS",
+ "roundness": "ROUND_FULL",
+ "saturation": 2
+ },
+ "deviceType": "DESKTOP",
+ "metadata": {
+ "userRole": "OWNER"
+ },
+ "name": "projects/9539484857893441782",
+ "origin": "STITCH",
+ "projectType": "TEXT_TO_UI_PRO",
+ "screenInstances": [
+ {
+ "height": 2887,
+ "id": "0107bc28f352430483e118170bb1fdbb",
+ "sourceScreen": "projects/9539484857893441782/screens/0107bc28f352430483e118170bb1fdbb",
+ "width": 1280,
+ "y": 3331
+ },
+ {
+ "height": 2887,
+ "id": "039d814e16994b74b269ec56cd4e571f",
+ "sourceScreen": "projects/9539484857893441782/screens/039d814e16994b74b269ec56cd4e571f",
+ "width": 1280,
+ "y": 6474
+ },
+ {
+ "height": 3075,
+ "id": "1b5d204b0c6d408fb59acbfa745782dd",
+ "sourceScreen": "projects/9539484857893441782/screens/1b5d204b0c6d408fb59acbfa745782dd",
+ "width": 1280,
+ "x": 2688
+ },
+ {
+ "height": 2434,
+ "id": "30bfd58fb84749c7aa2e643b3b32013c",
+ "sourceScreen": "projects/9539484857893441782/screens/30bfd58fb84749c7aa2e643b3b32013c",
+ "width": 1280,
+ "y": 9617
+ },
+ {
+ "height": 3034,
+ "id": "32031fbc83ec4f13a144b780ccfd4374",
+ "sourceScreen": "projects/9539484857893441782/screens/32031fbc83ec4f13a144b780ccfd4374",
+ "width": 1280,
+ "x": 1344
+ },
+ {
+ "height": 2772,
+ "id": "888eeb71e56745959b7f7d95e411f175",
+ "sourceScreen": "projects/9539484857893441782/screens/888eeb71e56745959b7f7d95e411f175",
+ "width": 1280,
+ "x": 4353,
+ "y": 151
+ },
+ {
+ "height": 1024,
+ "id": "f1b415128c2e451cabfa94246817abb3",
+ "sourceScreen": "projects/9539484857893441782/screens/f1b415128c2e451cabfa94246817abb3",
+ "width": 1280
+ },
+ {
+ "height": 1292,
+ "id": "f8e7509c68df4006b3350d7a1d0b3899",
+ "sourceScreen": "projects/9539484857893441782/screens/f8e7509c68df4006b3350d7a1d0b3899",
+ "width": 1280,
+ "x": 1344,
+ "y": 9617
+ }
+ ],
+ "thumbnailScreenshot": {
+ "downloadUrl": "https://lh3.googleusercontent.com/aida/AOfcidV5UaPSUaP8unPgPTcp447dfmaH3IDeirvDPCWBgzsEVODUniUTEwJu6xRDRRK1dN_FM_6LzQamfaM3nnkrHv4zMnOVMZgRZjKYX_u1IfQNSX0NuAor5dMK-SYTYDI9eDV5PJYqcAg5Qav4mVGtubHGCistED_aWALdKDJxzWyrSn3pfRU2T6l-fbOllVyZ2wVkhm-4dgB9rku44eUo0JsPO5jsbDhTS8_hOScMHHv0gHa4SVvS-pmZ05A",
+ "name": "projects/9539484857893441782/files/8f860557e03749bc978f10c7a4f9259d"
+ },
+ "title": "Gaurav Vashistha Portfolio",
+ "updateTime": "2025-12-13T21:23:57.879282Z",
+ "visibility": "PRIVATE"
+ },
+ {
+ "createTime": "2025-12-11T21:02:53.053473Z",
+ "designTheme": {
+ "colorMode": "LIGHT",
+ "customColor": "#ea2a33",
+ "font": "SPLINE_SANS",
+ "roundness": "ROUND_EIGHT",
+ "saturation": 3
+ },
+ "deviceType": "DESKTOP",
+ "metadata": {
+ "userRole": "OWNER"
+ },
+ "name": "projects/11685221869984408512",
+ "origin": "STITCH",
+ "projectType": "TEXT_TO_UI_PRO",
+ "screenInstances": [
+ {
+ "height": 1632,
+ "id": "bbeb9fa35d4e48d8aa7b3f375fbbeac1",
+ "sourceScreen": "projects/11685221869984408512/screens/bbeb9fa35d4e48d8aa7b3f375fbbeac1",
+ "width": 1280
+ }
+ ],
+ "thumbnailScreenshot": {
+ "downloadUrl": "https://lh3.googleusercontent.com/aida/AOfcidWh5K15vHTueYgyOGbjNg3hLdJA180DqU9UwOGfsbj7HU7Pe_8dVHf_-_5bK_e7IzTp0bOi-d8H6X6Cq4vDbcRWhszLAK5WbvX5KeFBo4oTGAXxx3em2soPB2DehFsComBwxxsi75qtPvLSsROcWW8f_MUe2sxmd57JQnuDCeEHmsZbTtg0o79TdZIXwh_Xb2aVj2a4rGq4dfJj7fRVWESIZz_8w5Js5rEjGrHo88UmcVD1PLfmWCzGiAMQ",
+ "name": "projects/11685221869984408512/files/2720c714dc0e4a589495246211537a13"
+ },
+ "title": "Developer Portfolio Homepage",
+ "updateTime": "2025-12-11T21:10:30.140593Z",
+ "visibility": "PRIVATE"
+ },
+ {
+ "createTime": "2025-12-11T21:02:07.188345Z",
+ "designTheme": {},
+ "metadata": {
+ "userRole": "OWNER"
+ },
+ "name": "projects/17495328282521709620",
+ "origin": "STITCH",
+ "projectType": "TEXT_TO_UI_PRO",
+ "updateTime": "2025-12-11T21:02:45.517477Z",
+ "visibility": "PRIVATE"
+ },
+ {
+ "createTime": "2025-12-11T14:29:03.451868Z",
+ "designTheme": {
+ "colorMode": "DARK",
+ "customColor": "#13a4ec",
+ "font": "INTER",
+ "roundness": "ROUND_EIGHT",
+ "saturation": 3
+ },
+ "deviceType": "MOBILE",
+ "metadata": {
+ "userRole": "OWNER"
+ },
+ "name": "projects/9585049832339031288",
+ "origin": "STITCH",
+ "projectType": "TEXT_TO_UI_PRO",
+ "screenInstances": [
+ {
+ "height": 698,
+ "id": "06e18302346d42df87a21e05ea98c96f",
+ "sourceScreen": "projects/9585049832339031288/screens/06e18302346d42df87a21e05ea98c96f",
+ "width": 390,
+ "x": 924,
+ "y": 961
+ },
+ {
+ "height": 698,
+ "id": "230d6b07a86d4f56bed3de950577a359",
+ "sourceScreen": "projects/9585049832339031288/screens/230d6b07a86d4f56bed3de950577a359",
+ "width": 390,
+ "x": -271,
+ "y": 947
+ },
+ {
+ "height": 698,
+ "id": "51a0a19b55774b96be22a3c8ecb22a40",
+ "sourceScreen": "projects/9585049832339031288/screens/51a0a19b55774b96be22a3c8ecb22a40",
+ "width": 390,
+ "x": 355,
+ "y": 947
+ },
+ {
+ "height": 698,
+ "id": "bdb86d1013724984bbcdd822b74d3943",
+ "sourceScreen": "projects/9585049832339031288/screens/bdb86d1013724984bbcdd822b74d3943",
+ "width": 390,
+ "x": -844,
+ "y": 947
+ },
+ {
+ "height": 698,
+ "id": "bfefda73ab734db889fb3cfe9d455630",
+ "sourceScreen": "projects/9585049832339031288/screens/bfefda73ab734db889fb3cfe9d455630",
+ "width": 390,
+ "y": 1915
+ },
+ {
+ "height": 698,
+ "id": "f239458ea0d64d6d80b331000f1bac19",
+ "sourceScreen": "projects/9585049832339031288/screens/f239458ea0d64d6d80b331000f1bac19",
+ "width": 390,
+ "y": 2869
+ }
+ ],
+ "thumbnailScreenshot": {
+ "downloadUrl": "https://lh3.googleusercontent.com/aida/AOfcidUbDNfVurrrfK5SFAKM_HpbQ5N5xodP1S6ZRCUTsgGcspBgrCa6u1R1dDLtInnQdGoVEbM7VYcNwlzmgUHrgNIfl_6kjp3WDJeUmDEJuXmeHyOiIPKJhZQT5Tag-cSlBRAxff8eEGB-nspuG-tGBksYNhV1B_rEDYHWwa9xGAxFS1ITEf3K7bqBoLanqV4h85Len4LRl_k9vWFynfE59-ieQ39nC2uZ5yhsz9Zktxjtvp234ZDjP7NB9H0",
+ "name": "projects/9585049832339031288/files/c4d1e82e07624ccea55021a120af9cd4"
+ },
+ "title": "Send Files Screen",
+ "updateTime": "2025-12-11T14:39:48.965873Z",
+ "visibility": "PRIVATE"
+ },
+ {
+ "createTime": "2025-12-11T14:28:27.026859Z",
+ "designTheme": {},
+ "metadata": {
+ "userRole": "OWNER"
+ },
+ "name": "projects/15763750653304082652",
+ "origin": "STITCH",
+ "projectType": "TEXT_TO_UI_PRO",
+ "updateTime": "2025-12-11T14:28:55.770282Z",
+ "visibility": "PRIVATE"
+ }
+ ]
+}
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000000000000000000000000000000000000..27fcd50255e0cfe9cea9e3b80c076420e4317cc6
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,14 @@
+fastapi
+uvicorn
+python-multipart
+langchain
+langchain-community
+langchain-google-genai
+langchain-groq
+pinecone>=3.0.0
+pydantic
+python-dotenv
+google-generativeai>=0.8.3
+groq
+Pillow
+huggingface_hub
diff --git a/restore_full_brain.py b/restore_full_brain.py
new file mode 100644
index 0000000000000000000000000000000000000000..d6d1b2b729d26ad5152deef536fca2cdd66c525c
--- /dev/null
+++ b/restore_full_brain.py
@@ -0,0 +1,122 @@
+import os
+import subprocess
+
+def restore_main():
+ content = """import os
+from fastapi import FastAPI, UploadFile, File
+from fastapi.responses import HTMLResponse, JSONResponse
+from dotenv import load_dotenv
+
+# Import Agents
+from agents.visual_analyst import VisualAnalyst
+from agents.memory_agent import MemoryAgent
+from agents.writer_agent import WriterAgent
+
+load_dotenv()
+app = FastAPI()
+
+# Initialize All Agents
+try:
+ visual_agent = VisualAnalyst()
+ memory_agent = MemoryAgent()
+ writer_agent = WriterAgent()
+
+ # Seed memory on startup
+ memory_agent.seed_database()
+ print("ā
All Agents Online")
+except Exception as e:
+ print(f"ā ļø Warning: Some agents failed to load: {e}")
+
+# 1. SERVE DASHBOARD AT ROOT
+@app.get("/", response_class=HTMLResponse)
+async def read_root():
+ try:
+ with open("dashboard.html", "r") as f:
+ return f.read()
+ except FileNotFoundError:
+ return "Error: dashboard.html not found"
+
+# 2. THE MAIN ORCHESTRATOR ENDPOINT
+@app.post("/generate-catalog")
+async def generate_catalog(file: UploadFile = File(...)):
+ try:
+ # A. Save Temp File
+ os.makedirs("uploads", exist_ok=True)
+ file_path = f"uploads/{file.filename}"
+ with open(file_path, "wb") as f:
+ f.write(await file.read())
+
+ # B. Visual Analysis (The Eyes)
+ visual_data = await visual_agent.analyze_image(file_path)
+
+ # C. Memory Search (The Context)
+ # Create a search query from visual data
+ query = f"{visual_data.get('main_color', '')} {visual_data.get('product_type', 'product')}"
+ seo_keywords = memory_agent.retrieve_keywords(query)
+
+ # D. Write Copy (The Brain)
+ listing = writer_agent.write_listing(visual_data, seo_keywords)
+
+ # Cleanup
+ if os.path.exists(file_path):
+ os.remove(file_path)
+
+ # Return Full Data Structure
+ return JSONResponse(content={
+ "visual_data": visual_data,
+ "seo_keywords": seo_keywords,
+ "listing": listing
+ })
+ except Exception as e:
+ print(f"Error: {e}")
+ return JSONResponse(content={"error": str(e)}, status_code=500)
+
+if __name__ == "__main__":
+ import uvicorn
+ uvicorn.run(app, host="0.0.0.0", port=7860)
+"""
+ with open("main.py", "w", encoding="utf-8") as f:
+ f.write(content)
+ print("ā
main.py restored with full agent logic.")
+
+def update_dashboard():
+ try:
+ with open("dashboard.html", "r", encoding="utf-8") as f:
+ content = f.read()
+
+ # Replace localhost URL with relative path
+ new_content = content.replace("http://localhost:8000/generate-catalog", "/generate-catalog")
+
+ with open("dashboard.html", "w", encoding="utf-8") as f:
+ f.write(new_content)
+ print("ā
dashboard.html updated for cloud deployment.")
+ except Exception as e:
+ print(f"ā Error updating dashboard.html: {e}")
+
+def deploy():
+ print("š Starting Deployment...")
+ commands = [
+ ["git", "add", "main.py", "dashboard.html"],
+ ["git", "commit", "-m", "Restore full brain logic and fix dashboard URL"],
+ ["git", "push", "space", "clean_deploy:main"]
+ ]
+
+ for cmd in commands:
+ try:
+ print(f"Running: {' '.join(cmd)}")
+ result = subprocess.run(cmd, check=True, capture_output=True, text=True)
+ print(result.stdout)
+ except subprocess.CalledProcessError as e:
+ print(f"ā Error running command: {' '.join(cmd)}")
+ print(e.stderr)
+ # Don't break on commit error as it might be empty
+ if "nothing to commit" in e.stderr:
+ continue
+ # For other errors we might want to continue or stop, but let's try to proceed
+ print("ā
Deployment script finished.")
+
+if __name__ == "__main__":
+ print("š§ Restoring Full Brain...")
+ restore_main()
+ update_dashboard()
+ deploy()
diff --git a/results/merch_batch_Coffee_20251214_052033.csv b/results/merch_batch_Coffee_20251214_052033.csv
new file mode 100644
index 0000000000000000000000000000000000000000..c1f0a80e0566c43a7d8cb00773880893eb903857
--- /dev/null
+++ b/results/merch_batch_Coffee_20251214_052033.csv
@@ -0,0 +1,6 @@
+Niche,Slogan,Art Prompt
+Coffee,Espresso Patronum.,"Neo-traditional digital illustration: Coffee portafilter emitting a glowing blue steam Patronus. Rich brown, teal, and gold colors. Centered, detailed T-shirt graphic. (23 words)"
+Coffee,This Is My Resting Coffee Face.,"**Retro 1970s cartoon T-shirt design, centered.** Grumpy, steaming mug face integrated with bold text. Colors: Espresso, Cream, Burnt Orange. **Vector.**"
+Coffee,My Blood Type Is Dark Roast.,"Retro screen print vector. Dripping anatomical heart as a coffee bean. Deep espresso, charcoal, and cream colors. Bold, centered T-shirt design."
+Coffee,Humaning Is Hard.,Error generating prompt
+Coffee,Powered By Anxiety & Arabica.,Error generating prompt
diff --git a/results/merch_batch_Coffee_20251214_052441.csv b/results/merch_batch_Coffee_20251214_052441.csv
new file mode 100644
index 0000000000000000000000000000000000000000..7f8381c28fd2d2bda077ce79ead91b3ca8e5f631
--- /dev/null
+++ b/results/merch_batch_Coffee_20251214_052441.csv
@@ -0,0 +1,6 @@
+Niche,Slogan,Art Prompt
+Coffee,Thou Shalt Not Decaf.,"High-contrast woodcut engraving of sacred coffee tablets. Espresso, cream, gold palette. Centered, dramatic lighting. Vector-ready."
+Coffee,Pre-Coffee: Danger Zone.,"Mid-Century modern warning sign, centered. Bold text on distressed yellow. Black/crimson palette. Vector, high contrast, coffee bomb symbol."
+Coffee,My Blood Type Is Coffee.,"Vintage distressed screen print. IV blood bag filled with coffee, centered. Espresso, cream, and black palette. Typography reads: ""My Blood Type Is Coffee."""
+Coffee,"I'm Not Addicted, I'm Committed.","Retro-vector graphic: Coffee cup with stylized steam waves. Burnt orange, espresso, and cream palette. Centered, bold T-shirt design."
+Coffee,Warning: May Talk About Coffee Too Much.,Error generating prompt
diff --git a/results/merch_batch_Coffee_20251214_052609.csv b/results/merch_batch_Coffee_20251214_052609.csv
new file mode 100644
index 0000000000000000000000000000000000000000..8fcc4073e7a26390e633aa14dadabb47b3061f78
--- /dev/null
+++ b/results/merch_batch_Coffee_20251214_052609.csv
@@ -0,0 +1,6 @@
+Niche,Slogan,Art Prompt
+Coffee,Must Caffeinate to Human.,"Vintage tattoo flash style. Skull morphing into steaming coffee cup, centered. Warm brown, cream, black palette. T-shirt ready graphic."
+Coffee,My Blood Type Is Arabica.,Error generating prompt
+Coffee,Decaf Is A Conspiracy.,Error generating prompt
+Coffee,Powered by Espresso and Anxiety.,Error generating prompt
+Coffee,I'll Sleep When The Coffee Runs Out.,Error generating prompt
diff --git a/results/merch_batch_Coffee_20251214_052808.csv b/results/merch_batch_Coffee_20251214_052808.csv
new file mode 100644
index 0000000000000000000000000000000000000000..4d2451d6353381da86a9c628f569dd0c13971e1b
--- /dev/null
+++ b/results/merch_batch_Coffee_20251214_052808.csv
@@ -0,0 +1,4 @@
+Niche,Slogan,Art Prompt
+Coffee,Retro Cat Mom,Error generating prompt
+Coffee,Pixel Art Kitty,Error generating prompt
+Coffee,Cattitude,Error generating prompt
diff --git a/scan_vision_models.py b/scan_vision_models.py
new file mode 100644
index 0000000000000000000000000000000000000000..790809b19de08f78ff5c11be4e162a36aa33c9d9
--- /dev/null
+++ b/scan_vision_models.py
@@ -0,0 +1,44 @@
+import os
+import requests
+import json
+from dotenv import load_dotenv
+
+load_dotenv()
+
+api_key = os.getenv("HF_TOKEN")
+headers = {"Authorization": f"Bearer {api_key}"}
+
+candidates = [
+ "HuggingFaceM4/idefics2-8b",
+ "HuggingFaceM4/idefics2-8b-chatty",
+ "llava-hf/llava-1.5-7b-hf",
+ "llava-hf/llava-v1.6-mistral-7b-hf",
+ "microsoft/Phi-3-vision-128k-instruct",
+ "NousResearch/Nous-Hermes-2-Vision-Alpha",
+ "OpenGVLab/InternVL-Chat-V1-5",
+ "Qwen/Qwen2.5-VL-7B-Instruct",
+ "google/paligemma-3b-mix-224"
+]
+
+print("Scanning for working Serverless Vision Models...\n")
+
+for model in candidates:
+ url = f"https://router.huggingface.co/models/{model}"
+ print(f"Testing: {model}")
+ try:
+ # Simple probe payload
+ response = requests.post(url, headers=headers, json={"inputs": "Hello"})
+ if response.status_code == 200:
+ print(f"ā
WORKS! {model} (Status: 200)")
+ print(f"Response: {response.text[:100]}...")
+ elif response.status_code == 400:
+ # 400 might mean it Exists but input format is wrong (which is good!)
+ print(f"ā ļø EXISTS but 400 (Bad Request): {model}")
+ print(f"Response: {response.text[:100]}...")
+ elif response.status_code == 404:
+ print(f"ā 404 Not Found: {model}")
+ else:
+ print(f"ā Error {response.status_code}: {model}")
+ except Exception as e:
+ print(f"ā Exception: {e}")
+ print("-" * 30)
diff --git a/screen_code.json b/screen_code.json
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/setup_dashboard.py b/setup_dashboard.py
new file mode 100644
index 0000000000000000000000000000000000000000..ed1ad7cee2c9fa213197c85aa6569713be718fff
--- /dev/null
+++ b/setup_dashboard.py
@@ -0,0 +1,140 @@
+import re
+import os
+
+def automate_dashboard_setup():
+ input_filename = "code.html"
+ output_filename = "dashboard.html"
+
+ # 1. Check if the source file exists
+ if not os.path.exists(input_filename):
+ print(f"ā Error: '{input_filename}' not found. Please save your Stitch UI code as '{input_filename}' first.")
+ return
+
+ print(f"Reading {input_filename}...")
+ with open(input_filename, "r", encoding="utf-8") as f:
+ html = f.read()
+
+ # --- Step 1: Inject IDs into the HTML elements ---
+
+ print("Injecting IDs for interactivity...")
+
+ # Inject ID for the Drop Zone & Hidden Input
+ # We look for the dashed border div that acts as the drop zone
+ if 'border-dashed' in html:
+ html = re.sub(
+ r'(
+""" + # Replace the closing body tag with our script + html = html.replace('', js_logic) + + # --- Step 3: Save the Result --- + with open(output_filename, "w", encoding="utf-8") as f: + f.write(html) + + print(f"ā Success! Generated '{output_filename}'.") + print("š You can now open 'dashboard.html' in your browser.") + +if __name__ == "__main__": + automate_dashboard_setup() \ No newline at end of file diff --git a/setup_env.py b/setup_env.py new file mode 100644 index 0000000000000000000000000000000000000000..5a5dc44dabd28dabb8010f803b7ec9d2f7f5e99f --- /dev/null +++ b/setup_env.py @@ -0,0 +1,47 @@ +import os +import sys +import subprocess +import platform + +def main(): + print(f"OS Detected: {platform.system()}") + + # Define venv directory + venv_dir = "venv" + + # Create venv + print(f"Creating virtual environment in '{venv_dir}'...") + try: + subprocess.check_call([sys.executable, "-m", "venv", venv_dir]) + except subprocess.CalledProcessError as e: + print(f"Error creating venv: {e}") + sys.exit(1) + + # Determine pip path based on OS + if platform.system() == "Windows": + pip_exe = os.path.join(venv_dir, "Scripts", "pip") + else: + pip_exe = os.path.join(venv_dir, "bin", "pip") + + # Upgrade pip + print("Upgrading pip...") + try: + subprocess.check_call([pip_exe, "install", "--upgrade", "pip"]) + except subprocess.CalledProcessError as e: + print(f"Error upgrading pip: {e}") + + # Install dependencies + if os.path.exists("requirements.txt"): + print("Installing dependencies from requirements.txt...") + try: + subprocess.check_call([pip_exe, "install", "-r", "requirements.txt"]) + except subprocess.CalledProcessError as e: + print(f"Error installing dependencies: {e}") + sys.exit(1) + else: + print("requirements.txt not found.") + + print("Setup Complete! Run the server now.") + +if __name__ == "__main__": + main() diff --git a/setup_github.py b/setup_github.py new file mode 100644 index 0000000000000000000000000000000000000000..34fffa583386ec00262b3c9a84d561fdbe0e8d6c --- /dev/null +++ b/setup_github.py @@ -0,0 +1,53 @@ +import subprocess +import shutil +import sys +import os + +def run_command(command, step_name): + """Executes a shell command with error handling.""" + print(f"š {step_name}...") + try: + subprocess.run(command, shell=True, check=True) + print(f"ā {step_name} complete.") + except subprocess.CalledProcessError as e: + print(f"ā Error during {step_name}.") + print(f"Command failed: {command}") + # We don't exit immediately for all errors (e.g. repo already exists), + # but for this script's flow, most steps are critical. + if "commit" in command: + print("(Proceeding, maybe nothing to commit)") + elif "repo create" in command: + print("(Proceeding, repository might already exist)") + else: + sys.exit(1) + +def main(): + # 1. Check for GitHub CLI + if not shutil.which("gh"): + print("ā GitHub CLI not found. Please install it or create the repo manually at github.com/new") + sys.exit(1) + + # 2. Initialize Git + # Only init if not already a git repo + if not os.path.exists(".git"): + run_command("git init", "Initialize Git") + else: + print("ā¹ļø Git already initialized.") + + # 3. Add all files + run_command("git add .", "Stage all files") + + # 4. Commit + run_command('git commit -m "Initial launch: MerchFlow AI Enterprise Edition"', "Commit files") + + # 5. Create GitHub Repo + # We ignore error here if it fails assuming it might exist, or let the user see the gh output + run_command("gh repo create MerchFlow-AI --public --source=. --remote=origin", "Create GitHub Repo") + + # 6. Push to origin + run_command("git push -u origin main", "Push to GitHub") + + print("\nš Project successfully pushed to GitHub!") + +if __name__ == "__main__": + main() diff --git a/setup_legal_assets.py b/setup_legal_assets.py new file mode 100644 index 0000000000000000000000000000000000000000..9a2828ba32e2d0809065cddc6cd1b978520bf774 --- /dev/null +++ b/setup_legal_assets.py @@ -0,0 +1,45 @@ +import os +import subprocess +from pathlib import Path + +def create_legal_assets(): + # Create legal directory + os.makedirs("legal", exist_ok=True) + + # Define MSA content + msa_content = """# Master Service Agreement + +**Parties:** "Bhishaj Technologies (MSME URN: UDYAM-UP-02-0108589)" (The Consultant) AND "[Client Name]" (The Client). + +## Clause 1: Payment & MSME Protection +Strict adherence to the Micro, Small and Medium Enterprises Development (MSMED) Act, 2006. Pursuant to Section 15, all payments must be settled within forty-five (45) days of invoice date. Delayed payments shall attract compound interest at three times (3x) the bank rate notified by the Reserve Bank of India, as mandated by Section 16 of the Act. + +## Clause 2: Intellectual Property Rights +Background IP (including pre-existing code, libraries, and agents) remains the sole property of the Consultant. Custom deliverables become Client property only upon full and final settlement of all dues. + +## Clause 3: Future Transfer (Successor Entity) +Assignment of Rights: Bhishaj Technologies (The Consultant) reserves the right to assign or transfer all rights, duties, and intellectual property created under this agreement to a successor corporate entity or affiliate (e.g., Bhishaj Labs Pvt Ltd) upon formal incorporation, without requiring further consent from the Client. + +## Clause 4: Confidentiality +Both parties agree to hold in strict confidence any proprietary or confidential information, including trade secrets, disclosed during the course of this agreement. Such information shall not be used for any purpose other than the performance of obligations under this agreement, nor disclosed to any third party without prior written consent. +""" + + # Write MSA_TEMPLATE.md + Path("legal/MSA_TEMPLATE.md").write_text(msa_content, encoding="utf-8") + +def deploy_assets(): + commands = [ + ["git", "add", "legal/"], + ["git", "commit", "-m", "Add Master Service Agreement template with MSME & Successor clauses"], + ["git", "push", "space", "clean_deploy:main"] + ] + + for cmd in commands: + try: + subprocess.run(cmd, check=True) + except subprocess.CalledProcessError as e: + print(f"Command failed: {e}") + +if __name__ == "__main__": + create_legal_assets() + deploy_assets() diff --git a/site_output/astro.config.mjs b/site_output/astro.config.mjs new file mode 100644 index 0000000000000000000000000000000000000000..caf24310f4ea86bc567c48ba86aca176e0015f2f --- /dev/null +++ b/site_output/astro.config.mjs @@ -0,0 +1,2 @@ +import { defineConfig } from 'astro/config'; +export default defineConfig({}); \ No newline at end of file diff --git a/site_output/package.json b/site_output/package.json new file mode 100644 index 0000000000000000000000000000000000000000..7c2c8e6e38fc440df1e0ac12e4be3f573b42fb30 --- /dev/null +++ b/site_output/package.json @@ -0,0 +1,15 @@ +{ + "name": "stitch-site", + "type": "module", + "version": "0.0.1", + "scripts": { + "dev": "astro dev", + "start": "astro dev", + "build": "astro build", + "preview": "astro preview", + "astro": "astro" + }, + "dependencies": { + "astro": "^5.0.0" + } +} diff --git a/site_output/src/layouts/Layout.astro b/site_output/src/layouts/Layout.astro new file mode 100644 index 0000000000000000000000000000000000000000..8ccef1b9e373ebf7a2a82ee0eef8a93a5deceb45 --- /dev/null +++ b/site_output/src/layouts/Layout.astro @@ -0,0 +1,22 @@ +--- +interface Props { + title: string; +} + +const { title } = Astro.props; +--- + + + +
+ + + + + +
+ +
+