2026 03 04 lzdy#88
Conversation
Updated the product context fetching logic in the beauty assistant function to conditionally create the Supabase client based on the availability of environment variables. This change enhances flexibility and ensures that the default Supabase client is used when service role credentials are no t configured. Made-with: Cursor
…ling Enhanced the product context fetching logic in the beauty assistant function to improve error handling and ensure the Supabase client is conditionally created based on the availability of environment variables. This update aims to increase flexibility and reliability in product data retrieval. Made-with: Cursor
…ormatting Updated the brain connectivity check to allow for additional acceptable response statuses, improving error handling. Refined the product formatting function documentation for clarity and consistency. These changes aim to enhance the robustness of the beauty assistant's functionality. Made-with: Cursor
Refined the documentation for the product formatting function in the beauty assistant to enhance clarity and conciseness. This update aims to improve the readability of the code and ensure consistent documentation practices across the project. Made-with: Cursor
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request focuses on improving the operational robustness and developer experience by introducing dedicated scripts for health and connectivity checks, along with updated documentation. It also adds a crucial validation step for environment variables within a Supabase function to prevent runtime errors due to misconfiguration. Highlights
Changelog
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on Gemini (@gemini-code-assist) comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request introduces a set of useful health check scripts (health, brain, sync) for monitoring service connectivity and updates the documentation accordingly, while also improving the robustness of the beauty-assistant Supabase function by adding explicit checks for required environment variables. However, the beauty-assistant function contains several significant security vulnerabilities that were not addressed, including Prompt Injection, missing authentication on the webhook path, and an insecure CORS policy, with a specific PostgREST Filter Injection identified on line 373. General feedback also suggests avoiding hardcoding URLs in the new scripts, addressing a minor inconsistency in one script, and clarifying a point of confusion in the documentation.
| ); | ||
| } | ||
| const productContextClient = createClient(supabaseUrl, serviceRoleKey); | ||
| const { productContext, matchedProducts } = await fetchProductContext(productContextClient, lastText, detectedConcernSlug); |
There was a problem hiding this comment.
The fetchProductContext function is called with untrusted input lastText (derived from user messages) which is used to construct a PostgREST filter string without proper sanitization. Specifically, the extractKeywords function (called inside fetchProductContext) does not sanitize special characters like commas or parentheses. An attacker can craft a message containing these characters to inject additional filter conditions into the query. Since this query is executed using a service role client (productContextClient) that bypasses Row Level Security (RLS), an attacker could potentially access product data that should be restricted or cause the query to fail (Denial of Service).
| const { productContext, matchedProducts } = await fetchProductContext(productContextClient, lastText, detectedConcernSlug); | |
| const sanitizedLastText = lastText.replace(/[(),]/g, " "); | |
| const { productContext, matchedProducts } = await fetchProductContext(productContextClient, sanitizedLastText, detectedConcernSlug); |
| const BRAIN_URL = | ||
| "https://qqceibvalkoytafynwoc.supabase.co/functions/v1/beauty-assistant"; |
There was a problem hiding this comment.
The BRAIN_URL is hardcoded. It's a best practice to manage environment-specific configurations like URLs through environment variables. This allows the script to be run against different environments (development, staging, production) without code changes.
| const BRAIN_URL = | |
| "https://qqceibvalkoytafynwoc.supabase.co/functions/v1/beauty-assistant"; | |
| const BRAIN_URL = | |
| process.env.BRAIN_URL || "https://qqceibvalkoytafynwoc.supabase.co/functions/v1/beauty-assistant"; |
| const FRONTEND_HEALTH = "https://asperbeautyshop-com.lovable.app/health"; | ||
| const BRAIN_URL = | ||
| "https://qqceibvalkoytafynwoc.supabase.co/functions/v1/beauty-assistant"; |
There was a problem hiding this comment.
The FRONTEND_HEALTH and BRAIN_URL are hardcoded. It's recommended to use environment variables for these URLs to allow the script to be used across different environments (e.g., development, staging, production) without modifying the code.
| const FRONTEND_HEALTH = "https://asperbeautyshop-com.lovable.app/health"; | |
| const BRAIN_URL = | |
| "https://qqceibvalkoytafynwoc.supabase.co/functions/v1/beauty-assistant"; | |
| const FRONTEND_HEALTH = process.env.FRONTEND_HEALTH_URL || "https://asperbeautyshop-com.lovable.app/health"; | |
| const BRAIN_URL = | |
| process.env.BRAIN_URL || "https://qqceibvalkoytafynwoc.supabase.co/functions/v1/beauty-assistant"; |
| const FRONTEND_HEALTH = "https://asperbeautyshop-com.lovable.app/health"; | ||
| const BRAIN_URL = | ||
| "https://qqceibvalkoytafynwoc.supabase.co/functions/v1/beauty-assistant"; |
There was a problem hiding this comment.
The FRONTEND_HEALTH and BRAIN_URL are hardcoded. For better flexibility across different environments (development, staging, production), it's best to source these values from environment variables.
| const FRONTEND_HEALTH = "https://asperbeautyshop-com.lovable.app/health"; | |
| const BRAIN_URL = | |
| "https://qqceibvalkoytafynwoc.supabase.co/functions/v1/beauty-assistant"; | |
| const FRONTEND_HEALTH = process.env.FRONTEND_HEALTH_URL || "https://asperbeautyshop-com.lovable.app/health"; | |
| const BRAIN_URL = | |
| process.env.BRAIN_URL || "https://qqceibvalkoytafynwoc.supabase.co/functions/v1/beauty-assistant"; |
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughRebrands project to “Asper Beauty Shop”; adds CLI scripts (health, brain, sync) and three health/sync check scripts; refactors Supabase beauty-assistant for dual streaming backends (Lovable vs Gemini) with expanded prompts; adds many docs, Cursor settings and fixes, Antigravity tooling, new skills, and a skills-lock. Supabase file contains git conflict markers. Changes
Sequence Diagram(s)sequenceDiagram
rect rgba(200,200,255,0.5)
participant Frontend as Frontend
end
rect rgba(200,255,200,0.5)
participant Supabase as Supabase Function
end
rect rgba(255,200,200,0.5)
participant Lovable as Lovable Gateway
end
rect rgba(255,255,200,0.5)
participant Gemini as Gemini API
end
rect rgba(200,255,255,0.5)
participant DB as Supabase DB
end
Frontend->>Supabase: HTTP chat/webhook request
Supabase->>DB: fetch product/context (service_role → anon fallback)
DB-->>Supabase: product data or fallback
alt useLovable == true
Supabase->>Lovable: open streaming connection (SSE-like)
Lovable-->>Supabase: stream events (system, product, deltas)
Supabase-->>Frontend: relay SSE stream
else
Supabase->>Gemini: initiate streaming (GGL SSE-like)
Gemini-->>Supabase: streaming deltas
Supabase-->>Frontend: relay streaming deltas
end
Supabase-->>Frontend: final status / completion
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
Note 🎁 Summarized by CodeRabbit FreeYour organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Pro by visiting https://app.coderabbit.ai/login. Comment |
…oved the use of an array for acceptable statuses and replaced it with direct checks for a 200 status code across brain-check, health-check, and sync-check scripts.
… checks Added a comment to the brain-check script to specify that only 2xx status codes are considered successful, while 401 (auth) and 405 (method) should trigger failures. This update aims to improve code clarity and ensure proper error handling visibility.
Added a comprehensive list of available npm scripts to the README file, detailing their functions to enhance developer onboarding and streamline project workflows. This update aims to improve documentation clarity and accessibility for team members.
There was a problem hiding this comment.
Pull request overview
Adds operational “health/sync” command-line checks and improves Supabase Edge Function env validation, with accompanying README guidance.
Changes:
- Added Node scripts to check frontend
/health, the Supabase “beauty-assistant” function health, or both. - Wired new scripts into
package.json(npm run health|brain|sync). - Added fail-fast env var validation for service-role product context reads in the
beauty-assistantEdge Function.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| supabase/functions/beauty-assistant/index.ts | Validates required env vars before creating a service-role Supabase client for product context. |
| scripts/sync-check.js | New script to verify frontend + “brain” connectivity in parallel. |
| scripts/health-check.js | New “health” script (currently checks both endpoints). |
| scripts/brain-check.js | New script to verify the Edge Function health endpoint and print the response. |
| package.json | Adds health, brain, and sync npm scripts. |
| README.md | Documents the new commands and an editor setting snippet. |
| const FRONTEND_HEALTH = "https://asperbeautyshop-com.lovable.app/health"; | ||
| const BRAIN_URL = | ||
| "https://qqceibvalkoytafynwoc.supabase.co/functions/v1/beauty-assistant"; | ||
|
|
||
| async function check(name, url) { | ||
| try { | ||
| const res = await fetch(url, { method: "GET" }); | ||
| return { name, ok: res.ok, status: res.status, url }; | ||
| } catch (err) { | ||
| return { name, ok: false, status: null, error: err.message, url }; | ||
| } | ||
| } | ||
|
|
||
| async function main() { | ||
| console.log("Asper Beauty Shop — Health Check\n"); | ||
|
|
||
| const frontend = await check("Frontend /health", FRONTEND_HEALTH); | ||
| const brain = await check("Beauty Assistant (brain)", BRAIN_URL); | ||
|
|
There was a problem hiding this comment.
health-check.js currently performs the same two checks as sync-check.js (frontend + brain), which conflicts with the README’s description of npm run health as "frontend only" and makes health vs sync ambiguous. Consider either limiting health to just the frontend endpoint or consolidating the scripts so each command has a distinct purpose.
Clarified the description of the sync check (SNC) in the README to enhance understanding of its purpose. Additionally, updated the instructions for adjusting shared settings across profiles, including the new `update.channel` key. This update aims to improve documentation clarity and assist developers in configuring their environments effectively.
| const FRONTEND_HEALTH = "https://asperbeautyshop-com.lovable.app/health"; | ||
| const BRAIN_URL = |
There was a problem hiding this comment.
These scripts hardcode production URLs. In practice this makes local/staging checks difficult and can cause accidental reliance on prod endpoints. Consider allowing overrides via environment variables (similar to scripts/sync-shopify-catalog.ts using process.env with sensible defaults) and/or CLI args for FRONTEND_HEALTH / BRAIN_URL.
| const FRONTEND_HEALTH = "https://asperbeautyshop-com.lovable.app/health"; | |
| const BRAIN_URL = | |
| const FRONTEND_HEALTH = | |
| process.env.FRONTEND_HEALTH || "https://asperbeautyshop-com.lovable.app/health"; | |
| const BRAIN_URL = | |
| process.env.BRAIN_URL || |
| // Fetch product context with service role when configured (bypasses RLS). Otherwise empty context, consistent with webhook path. | ||
| const supabaseUrl = Deno.env.get("SUPABASE_URL"); | ||
| const serviceRoleKey = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY"); | ||
| let productContext = ""; | ||
| let matchedProducts: any[] = []; | ||
| if (supabaseUrl && serviceRoleKey) { | ||
| const productContextClient = createClient(supabaseUrl, serviceRoleKey); | ||
| const result = await fetchProductContext(productContextClient, lastText, detectedConcernSlug); | ||
| productContext = result.productContext; | ||
| matchedProducts = result.matchedProducts; | ||
| } |
There was a problem hiding this comment.
SUPABASE_SERVICE_ROLE_KEY is treated as optional here, but this handler still uses Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")! for telemetry inserts when campaignSource is present (earlier in this same try-block). If the service role key is intentionally optional, guard the telemetry insert the same way (or fall back to SUPABASE_ANON_KEY), otherwise fail fast with a clear error when it’s missing so the runtime behavior is consistent.
| // Fetch product context with service role when configured (bypasses RLS). Otherwise empty context, consistent with webhook path. | ||
| const supabaseUrl = Deno.env.get("SUPABASE_URL"); | ||
| const serviceRoleKey = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY"); | ||
| let productContext = ""; | ||
| let matchedProducts: any[] = []; | ||
| if (supabaseUrl && serviceRoleKey) { | ||
| const productContextClient = createClient(supabaseUrl, serviceRoleKey); | ||
| const result = await fetchProductContext(productContextClient, lastText, detectedConcernSlug); | ||
| productContext = result.productContext; | ||
| matchedProducts = result.matchedProducts; | ||
| } |
There was a problem hiding this comment.
Website chat now skips fetchProductContext entirely unless the service role key is set, which means productContext stays as an empty string even though SUPABASE_ANON_KEY is already required for auth in this path. This makes website chat behavior diverge from the webhook path (which falls back to anon) and results in an empty “Inventory” section instead of the function’s default “(No matching products found…)” context. Consider using the same fallback logic as the webhook path (service role key if present, otherwise anon key) and always calling fetchProductContext when SUPABASE_URL is configured.
Eliminated unnecessary blank lines in the README to streamline the document and improve readability. This minor update aims to enhance the overall presentation of the documentation.
…-assistant): product context + Deno declare Made-with: Cursor
| const res = await fetch(url, { method: "GET" }); | ||
| return { name, ok: res.ok, status: res.status, url }; | ||
| } catch (err) { | ||
| return { name, ok: false, status: null, error: err.message, url }; |
There was a problem hiding this comment.
In the catch block, the code assumes the thrown value has a .message property. In JS, catch (err) can be a non-Error value, which would make err.message undefined and hide the real failure reason. Prefer err instanceof Error ? err.message : String(err).
| return { name, ok: false, status: null, error: err.message, url }; | |
| return { | |
| name, | |
| ok: false, | |
| status: null, | |
| error: err instanceof Error ? err.message : String(err), | |
| url, | |
| }; |
|
|
||
| async function fetchStatus(name, url) { | ||
| try { | ||
| const res = await fetch(url, { method: "GET" }); | ||
| return { name, status: res.status, ok: res.ok }; | ||
| } catch (err) { | ||
| return { name, status: null, ok: false, error: err.message }; |
There was a problem hiding this comment.
This script uses fetch() without any timeout/abort handling, so a stalled network connection can hang the command for a long time. Consider adding an AbortController with a short timeout (e.g., 5–10s) and surfacing a clear timeout error.
| async function fetchStatus(name, url) { | |
| try { | |
| const res = await fetch(url, { method: "GET" }); | |
| return { name, status: res.status, ok: res.ok }; | |
| } catch (err) { | |
| return { name, status: null, ok: false, error: err.message }; | |
| const FETCH_TIMEOUT_MS = 10_000; | |
| async function fetchStatus(name, url) { | |
| const controller = new AbortController(); | |
| const timeoutId = setTimeout(() => { | |
| controller.abort(); | |
| }, FETCH_TIMEOUT_MS); | |
| try { | |
| const res = await fetch(url, { method: "GET", signal: controller.signal }); | |
| return { name, status: res.status, ok: res.ok }; | |
| } catch (err) { | |
| const isAbortError = | |
| err && (err.name === "AbortError" || err.message?.includes("aborted")); | |
| const errorMessage = isAbortError | |
| ? `Request timed out after ${FETCH_TIMEOUT_MS}ms` | |
| : err.message; | |
| return { name, status: null, ok: false, error: errorMessage }; | |
| } finally { | |
| clearTimeout(timeoutId); |
| const res = await fetch(url, { method: "GET" }); | ||
| return { name, status: res.status, ok: res.ok }; | ||
| } catch (err) { | ||
| return { name, status: null, ok: false, error: err.message }; |
There was a problem hiding this comment.
In the catch block, the code assumes the thrown value has a .message property. In JS, catch (err) can be a non-Error value, which would make err.message undefined and hide the real failure reason. Prefer err instanceof Error ? err.message : String(err).
| return { name, status: null, ok: false, error: err.message }; | |
| return { | |
| name, | |
| status: null, | |
| ok: false, | |
| error: err instanceof Error ? err.message : String(err), | |
| }; |
| const res = await fetch(BRAIN_URL, { method: "GET" }); | ||
| const text = await res.text(); |
There was a problem hiding this comment.
This script uses fetch() without any timeout/abort handling, so a stalled network connection can hang the command for a long time. Consider adding an AbortController with a short timeout (e.g., 5–10s) and surfacing a clear timeout error.
| process.exit(1); | ||
| } | ||
| } catch (err) { | ||
| console.log(" ✗ Error:", err.message); |
There was a problem hiding this comment.
In both catch blocks, the code assumes the thrown value has a .message property. In JS, catch (err) can be a non-Error value, which would make err.message undefined and hide the real failure reason. Prefer err instanceof Error ? err.message : String(err).
| console.log(" ✗ Error:", err.message); | |
| console.log(" ✗ Error:", err instanceof Error ? err.message : String(err)); |
|
|
||
| async function check(name, url) { | ||
| try { | ||
| const res = await fetch(url, { method: "GET" }); | ||
| return { name, ok: res.ok, status: res.status, url }; | ||
| } catch (err) { | ||
| return { name, ok: false, status: null, error: err.message, url }; |
There was a problem hiding this comment.
This script uses fetch() without any timeout/abort handling, so a stalled network connection can hang the command for a long time (especially in CI). Consider adding an AbortController with a short timeout (e.g., 5–10s) and reporting a clear timeout error.
| async function check(name, url) { | |
| try { | |
| const res = await fetch(url, { method: "GET" }); | |
| return { name, ok: res.ok, status: res.status, url }; | |
| } catch (err) { | |
| return { name, ok: false, status: null, error: err.message, url }; | |
| const FETCH_TIMEOUT_MS = 10000; // 10 seconds | |
| async function check(name, url) { | |
| const controller = new AbortController(); | |
| const timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS); | |
| try { | |
| const res = await fetch(url, { method: "GET", signal: controller.signal }); | |
| return { name, ok: res.ok, status: res.status, url }; | |
| } catch (err) { | |
| const errorMessage = | |
| err && err.name === "AbortError" | |
| ? `Request timed out after ${FETCH_TIMEOUT_MS / 1000}s` | |
| : err.message; | |
| return { name, ok: false, status: null, error: errorMessage, url }; | |
| } finally { | |
| clearTimeout(timeoutId); |
Included a new `cursor-user-settings-FIXED.json` file with the corrected JSON structure for Cursor user settings. Updated the `CURSOR-SETTINGS-FIX.md` to guide users on copying the fixed JSON into their `settings.json`. This change enhances user experience by providing clear instructions for resolving configuration issues.
….md reference Enhanced the README by adding a link to the new APPLY_AND_RUN.md for deployment commands and social integrations. Updated ROLE_AND_MANDATE to reflect the new document's usage in deployment prep and checklist verification. These changes improve clarity and streamline the deployment process for developers.
Enhanced the APPLY_TO_MAIN_SITE.md by providing a comprehensive checklist for production deployment, ensuring all necessary steps are clearly outlined. This update improves the deployment process for developers and aligns with the project's operational standards.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Asper Beauty Shop <252395498+asperpharma@users.noreply.github.com>
| declare const Deno: { env: { get(key: string): string | undefined } }; | ||
| // @ts-expect-error — Deno URL imports; resolved at runtime by Supabase Edge | ||
| // @ts-expect-error — Deno URL imports; resolved at runtime by Supabase Edge | ||
| // @ts-expect-error — Deno URL imports; resolved at runtime by Supabase Edge | ||
| // @ts-expect-error — Deno URL imports; resolved at runtime by Supabase Edge | ||
| import { serve } from "https://deno.land/std@0.168.0/http/server.ts"; | ||
| // @ts-expect-error — Deno URL imports; resolved at runtime by Supabase Edge | ||
| // @ts-expect-error — Deno URL imports; resolved at runtime by Supabase Edge |
There was a problem hiding this comment.
The file header introduces duplicate declarations/lines (the Dr. Bot description line is repeated, and declare const Deno is declared twice). This will raise TypeScript duplicate identifier errors and should be deduplicated to a single header comment and a single Deno declaration.
| declare const Deno: { env: { get(key: string): string | undefined } }; | |
| // @ts-expect-error — Deno URL imports; resolved at runtime by Supabase Edge | |
| // @ts-expect-error — Deno URL imports; resolved at runtime by Supabase Edge | |
| // @ts-expect-error — Deno URL imports; resolved at runtime by Supabase Edge | |
| // @ts-expect-error — Deno URL imports; resolved at runtime by Supabase Edge | |
| import { serve } from "https://deno.land/std@0.168.0/http/server.ts"; | |
| // @ts-expect-error — Deno URL imports; resolved at runtime by Supabase Edge | |
| // @ts-expect-error — Deno URL imports; resolved at runtime by Supabase Edge | |
| // @ts-expect-error — Deno URL imports; resolved at runtime by Supabase Edge | |
| import { serve } from "https://deno.land/std@0.168.0/http/server.ts"; | |
| // @ts-expect-error — Deno URL imports; resolved at runtime by Supabase Edge |
| declare const Deno: { env: { get(key: string): string | undefined } }; | ||
| // @ts-expect-error — Deno URL imports; resolved at runtime by Supabase Edge | ||
| // @ts-expect-error — Deno URL imports; resolved at runtime by Supabase Edge | ||
| // @ts-expect-error — Deno URL imports; resolved at runtime by Supabase Edge | ||
| // @ts-expect-error — Deno URL imports; resolved at runtime by Supabase Edge | ||
| import { serve } from "https://deno.land/std@0.168.0/http/server.ts"; | ||
| // @ts-expect-error — Deno URL imports; resolved at runtime by Supabase Edge | ||
| // @ts-expect-error — Deno URL imports; resolved at runtime by Supabase Edge |
There was a problem hiding this comment.
There are multiple consecutive // @ts-expect-error directives before the imports. @ts-expect-error applies only to the next line and will itself error if the next line has no TS error, so the extra directives will fail typechecking as "Unused 'ts-expect-error' directive". Keep only a single directive directly above the specific import that needs it (or switch to @ts-ignore if you truly want to silence without enforcement).
| declare const Deno: { env: { get(key: string): string | undefined } }; | |
| // @ts-expect-error — Deno URL imports; resolved at runtime by Supabase Edge | |
| // @ts-expect-error — Deno URL imports; resolved at runtime by Supabase Edge | |
| // @ts-expect-error — Deno URL imports; resolved at runtime by Supabase Edge | |
| // @ts-expect-error — Deno URL imports; resolved at runtime by Supabase Edge | |
| import { serve } from "https://deno.land/std@0.168.0/http/server.ts"; | |
| // @ts-expect-error — Deno URL imports; resolved at runtime by Supabase Edge | |
| // @ts-expect-error — Deno URL imports; resolved at runtime by Supabase Edge | |
| // @ts-expect-error — Deno URL imports; resolved at runtime by Supabase Edge | |
| import { serve } from "https://deno.land/std@0.168.0/http/server.ts"; | |
| // @ts-expect-error — Deno URL imports; resolved at runtime by Supabase Edge |
| // Fetch product context with service role (or anon fallback), consistent with webhook path. | ||
| const supabaseKey = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY") ?? Deno.env.get("SUPABASE_ANON_KEY"); | ||
| const supabaseKey = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY") ?? Deno.env.get("SUPABASE_ANON_KEY"); | ||
| let matchedProducts: any[] = []; | ||
| if (supabaseUrl && supabaseKey) { | ||
| const productContextClient = createClient(supabaseUrl, supabaseKey); | ||
| const result = await fetchProductContext(productContextClient, lastText, detectedConcernSlug); | ||
| productContext = result.productContext; | ||
| matchedProducts = result.matchedProducts; | ||
| } else { | ||
| // Keep system prompt behavior consistent when product lookup is skipped | ||
| productContext = "(No matching products found in catalog.)"; | ||
| } | ||
|
|
There was a problem hiding this comment.
In the website chat path, supabaseUrl and productContext are used but never declared in this scope, and supabaseKey is declared twice. This will fail at runtime/compile time. Define const supabaseUrl = Deno.env.get("SUPABASE_URL") and let productContext = "" (or similar) in this block, and remove the duplicate supabaseKey declaration/comment.
| <<<<<<< HEAD | ||
| ======= | ||
| } | ||
| // Flush any remaining decoded data and process the final buffer | ||
| buffer += decoder.decode(); | ||
| if (buffer) { | ||
| const remainingLines = buffer.split("\n"); | ||
| for (const line of remainingLines) { | ||
| const trimmed = line.trimEnd(); | ||
| if (trimmed.startsWith("data:")) { | ||
| const dataPayload = trimmed.slice("data:".length).trimStart(); | ||
| if (dataPayload === "[DONE]") { | ||
| continue; | ||
| } | ||
| try { | ||
| const json = JSON.parse(dataPayload); | ||
| const text = json?.candidates?.[0]?.content?.parts?.[0]?.text; | ||
| if (text) { | ||
| const chunk = `data: ${JSON.stringify({ choices: [{ delta: { content: text } }] })}\n\n`; | ||
| controller.enqueue(encoder.encode(chunk)); | ||
| } | ||
| } catch { | ||
| // skip malformed chunk | ||
| } | ||
| } | ||
| } | ||
| >>>>>>> 1164a387ba6726c241c737a856e7eb7ad0b17e05 | ||
| } |
There was a problem hiding this comment.
Unresolved git merge conflict markers (<<<<<<<, =======, >>>>>>>) are present inside the Gemini streaming loop, and the brace/loop structure is currently invalid. This will break the Edge Function. Resolve the conflict by choosing the intended implementation and removing the markers, then ensure the loop/body braces compile.
| <<<<<<< HEAD | |
| ======= | |
| } | |
| // Flush any remaining decoded data and process the final buffer | |
| buffer += decoder.decode(); | |
| if (buffer) { | |
| const remainingLines = buffer.split("\n"); | |
| for (const line of remainingLines) { | |
| const trimmed = line.trimEnd(); | |
| if (trimmed.startsWith("data:")) { | |
| const dataPayload = trimmed.slice("data:".length).trimStart(); | |
| if (dataPayload === "[DONE]") { | |
| continue; | |
| } | |
| try { | |
| const json = JSON.parse(dataPayload); | |
| const text = json?.candidates?.[0]?.content?.parts?.[0]?.text; | |
| if (text) { | |
| const chunk = `data: ${JSON.stringify({ choices: [{ delta: { content: text } }] })}\n\n`; | |
| controller.enqueue(encoder.encode(chunk)); | |
| } | |
| } catch { | |
| // skip malformed chunk | |
| } | |
| } | |
| } | |
| >>>>>>> 1164a387ba6726c241c737a856e7eb7ad0b17e05 | |
| } | |
| } |
| VITE_SUPABASE_PUBLISHABLE_KEY="sb_publishable_XYH3MdZyiulOKUcAD5f6_w_MQIhHLru" | ||
|
|
||
| # The Commerce Engine (Shopify) | ||
| VITE_SHOPIFY_STORE_DOMAIN="lovable-project-milns.myshopify.com" | ||
| VITE_SHOPIFY_STOREFRONT_TOKEN="79d7870bb2e8b940752bdee2af19edbb" |
There was a problem hiding this comment.
This doc embeds concrete values for VITE_SUPABASE_PUBLISHABLE_KEY and VITE_SHOPIFY_STOREFRONT_TOKEN. Elsewhere the repo template explicitly says "Do NOT commit real keys" (see env.main-site.example). Even if these are "publishable", committing them makes rotation harder and can leak access if they’re not intended to be public. Replace these with placeholders (empty or <YOUR_...>), and point readers to where to retrieve/set them (Lovable env / Supabase / Shopify).
| VITE_SUPABASE_PUBLISHABLE_KEY="sb_publishable_XYH3MdZyiulOKUcAD5f6_w_MQIhHLru" | |
| # The Commerce Engine (Shopify) | |
| VITE_SHOPIFY_STORE_DOMAIN="lovable-project-milns.myshopify.com" | |
| VITE_SHOPIFY_STOREFRONT_TOKEN="79d7870bb2e8b940752bdee2af19edbb" | |
| # Get this from Supabase Dashboard → Project Settings → API → anon/public key | |
| VITE_SUPABASE_PUBLISHABLE_KEY="<YOUR_SUPABASE_PUBLISHABLE_KEY>" | |
| # The Commerce Engine (Shopify) | |
| VITE_SHOPIFY_STORE_DOMAIN="lovable-project-milns.myshopify.com" | |
| # Get this from Shopify Admin → Apps → Develop apps → Storefront API access token | |
| VITE_SHOPIFY_STOREFRONT_TOKEN="<YOUR_SHOPIFY_STOREFRONT_TOKEN>" |
| "GitHub.vscode-pull-request-github", | ||
| "", | ||
| "GitHub.copilot" |
There was a problem hiding this comment.
This "fixed" settings JSON includes an empty-string entry in remote.defaultExtensionsIfInstalledLocally. Cursor/VS Code expects extension IDs here; an empty entry can cause validation issues and makes the copy-paste file less reliable. Remove the empty string (and also consider deduplicating GitHub.copilot which appears twice).
| "GitHub.vscode-pull-request-github", | |
| "", | |
| "GitHub.copilot" | |
| "GitHub.vscode-pull-request-github" |
| **File:** `C:\Users\C-R\AppData\Roaming\Cursor\User\settings.json` | ||
|
|
||
| **Copy-paste option:** A full corrected JSON is in [cursor-user-settings-FIXED.json](cursor-user-settings-FIXED.json). Copy its entire contents and paste into your User `settings.json`, then save and reload Cursor. |
There was a problem hiding this comment.
This doc hard-codes a single Windows user-specific path (C:\Users\C-R\...). For a repo doc intended for others, it should use a portable path (e.g. %APPDATA%\Cursor\User\settings.json) and ideally include the macOS/Linux equivalents. That avoids confusion and makes the instructions applicable for all developers.
Introduces a new asynchronous function, `runAntigravityDiagnostic`, to programmatically execute a PowerShell diagnostic script for the Antigravity feature. This function provides a safe and workflow-compliant method to trigger diagnostics from the UI, returning standardized status and details. It includes checks for the execution environment, handles various outcomes (success, no process detected, error, or escalation), and ensures robust error handling.
| * Project scripts (SNC, health, brain), applyToAllProfiles, and commitDirectlyWarning: see README. | ||
| */ | ||
| declare const Deno: { env: { get(key: string): string | undefined } }; | ||
| declare const Deno: { env: { get(key: string): string | undefined } }; |
There was a problem hiding this comment.
Deno is declared twice, which will cause a TypeScript redeclaration error during typechecking/linting. Remove the duplicate declare const Deno ... so it’s defined only once.
| declare const Deno: { env: { get(key: string): string | undefined } }; |
| // Fetch product context with service role (or anon fallback), consistent with webhook path. | ||
| // Fetch product context with service role (or anon fallback), consistent with webhook path. | ||
| const supabaseKey = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY") ?? Deno.env.get("SUPABASE_ANON_KEY"); | ||
| const supabaseKey = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY") ?? Deno.env.get("SUPABASE_ANON_KEY"); | ||
| let matchedProducts: any[] = []; | ||
| if (supabaseUrl && supabaseKey) { | ||
| const productContextClient = createClient(supabaseUrl, supabaseKey); | ||
| const result = await fetchProductContext(productContextClient, lastText, detectedConcernSlug); | ||
| productContext = result.productContext; | ||
| matchedProducts = result.matchedProducts; |
There was a problem hiding this comment.
In the website chat path, supabaseKey is declared twice and supabaseUrl/productContext are used without being defined in this scope, which will fail compilation/runtime. Define supabaseUrl and productContext here (and keep only one supabaseKey declaration) before calling fetchProductContext().
| <<<<<<< HEAD | ||
| ======= | ||
| } | ||
| // Flush any remaining decoded data and process the final buffer | ||
| buffer += decoder.decode(); | ||
| if (buffer) { | ||
| const remainingLines = buffer.split("\n"); | ||
| for (const line of remainingLines) { | ||
| const trimmed = line.trimEnd(); | ||
| if (trimmed.startsWith("data:")) { | ||
| const dataPayload = trimmed.slice("data:".length).trimStart(); | ||
| if (dataPayload === "[DONE]") { | ||
| continue; | ||
| } | ||
| try { | ||
| const json = JSON.parse(dataPayload); | ||
| const text = json?.candidates?.[0]?.content?.parts?.[0]?.text; | ||
| if (text) { | ||
| const chunk = `data: ${JSON.stringify({ choices: [{ delta: { content: text } }] })}\n\n`; | ||
| controller.enqueue(encoder.encode(chunk)); | ||
| } | ||
| } catch { | ||
| // skip malformed chunk | ||
| } | ||
| } | ||
| } | ||
| >>>>>>> 1164a387ba6726c241c737a856e7eb7ad0b17e05 | ||
| } |
There was a problem hiding this comment.
This file still contains unresolved git merge conflict markers (<<<<<<<, =======, >>>>>>>) inside the Gemini SSE streaming loop. These will break parsing/compilation and must be fully resolved (choose one side and delete the markers).
| <<<<<<< HEAD | |
| ======= | |
| } | |
| // Flush any remaining decoded data and process the final buffer | |
| buffer += decoder.decode(); | |
| if (buffer) { | |
| const remainingLines = buffer.split("\n"); | |
| for (const line of remainingLines) { | |
| const trimmed = line.trimEnd(); | |
| if (trimmed.startsWith("data:")) { | |
| const dataPayload = trimmed.slice("data:".length).trimStart(); | |
| if (dataPayload === "[DONE]") { | |
| continue; | |
| } | |
| try { | |
| const json = JSON.parse(dataPayload); | |
| const text = json?.candidates?.[0]?.content?.parts?.[0]?.text; | |
| if (text) { | |
| const chunk = `data: ${JSON.stringify({ choices: [{ delta: { content: text } }] })}\n\n`; | |
| controller.enqueue(encoder.encode(chunk)); | |
| } | |
| } catch { | |
| // skip malformed chunk | |
| } | |
| } | |
| } | |
| >>>>>>> 1164a387ba6726c241c737a856e7eb7ad0b17e05 | |
| } | |
| } |
| Get-Process | Where-Object { $_.ProcessName -match 'language|antigravity' } | Select-Object Id, ProcessName, Path | Format-Table -AutoSize | ||
| if (-not $?) { Write-Host "No matching processes (language_server_windows_x64.exe / Antigravity not running)." } |
There was a problem hiding this comment.
This script won’t print the “No matching processes…” message when there are simply no matches, because $? will still be $true for a successful pipeline that returns zero rows. Instead, capture the filtered process list into a variable and check whether it’s empty before printing the fallback message.
| Get-Process | Where-Object { $_.ProcessName -match 'language|antigravity' } | Select-Object Id, ProcessName, Path | Format-Table -AutoSize | |
| if (-not $?) { Write-Host "No matching processes (language_server_windows_x64.exe / Antigravity not running)." } | |
| $matchingProcesses = Get-Process | Where-Object { $_.ProcessName -match 'language|antigravity' } | Select-Object Id, ProcessName, Path | |
| if ($matchingProcesses) { | |
| $matchingProcesses | Format-Table -AutoSize | |
| } else { | |
| Write-Host "No matching processes (language_server_windows_x64.exe / Antigravity not running)." | |
| } |
| "c:\\Users\\C-R\\Desktop\\Asper-Beauty-Shop\\Asper Shop Images\\.vscode\\extensions.json" | ||
| ], | ||
| "vs-kubernetes.kubeconfig": "c:\\Users\\C-R\\Desktop\\Asper-Beauty-Shop\\Asper Shop Images\\.vscode\\extensions.json" |
There was a problem hiding this comment.
This settings template includes absolute paths that appear specific to one machine/user (e.g. c:\\Users\\C-R\\Desktop\\...). If this file is meant to be shared, replace those paths with placeholders or remove them so users don’t copy broken, machine-specific settings.
| "c:\\Users\\C-R\\Desktop\\Asper-Beauty-Shop\\Asper Shop Images\\.vscode\\extensions.json" | |
| ], | |
| "vs-kubernetes.kubeconfig": "c:\\Users\\C-R\\Desktop\\Asper-Beauty-Shop\\Asper Shop Images\\.vscode\\extensions.json" | |
| "<path-to-kubeconfig>" | |
| ], | |
| "vs-kubernetes.kubeconfig": "<path-to-kubeconfig>" |
Co-authored-by: asperpharma <252395498+asperpharma@users.noreply.github.com>
…main Implement PR #88: rebrand docs, fix duplicate package.json scripts, add operational runbooks
Summary by CodeRabbit
Documentation
New Features
Behavior