From 767a23f5be2b777e5f722216f937a51351c9001e Mon Sep 17 00:00:00 2001 From: Tessa Kriesel Date: Sun, 31 May 2026 19:36:21 -0500 Subject: [PATCH 1/3] =?UTF-8?q?fix(config):=20restore=20real=20Tabstack=20?= =?UTF-8?q?rivals=20config=20=E2=80=94=20open-source=20cleanup=20accidenta?= =?UTF-8?q?lly=20replaced=20with=20generic=20template?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reverts the template substitution from the open-source readiness cleanup. The demo at rivaldemo.com was seeded with Competitor A/B/C placeholder entries. Restores Tabstack as self and all real competitors (Firecrawl, Browserbase, Browser Use, Stagehand, Steel, Playwright, Apify, etc.). --- rivals.config.json | 297 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 263 insertions(+), 34 deletions(-) diff --git a/rivals.config.json b/rivals.config.json index a001ef7..b39ac7a 100644 --- a/rivals.config.json +++ b/rivals.config.json @@ -1,58 +1,287 @@ { "self": { - "name": "Your Company", - "slug": "your-company", - "url": "https://your-company.example.com", + "name": "Tabstack", + "slug": "tabstack", + "url": "https://tabstack.ai", "manual": { "matrix_overrides": { - "managed_service_score": 7, - "llm_included_score": 5 + "managed_service_score": 10, + "llm_included_score": 9 } }, "pages": [ - { "label": "Homepage", "url": "https://your-company.example.com", "type": "homepage" }, - { "label": "Pricing", "url": "https://your-company.example.com/pricing", "type": "pricing", "geo_target": "US" }, - { "label": "Docs", "url": "https://docs.your-company.example.com", "type": "docs" }, - { "label": "Blog", "url": "https://your-company.example.com/blog", "type": "blog" }, - { "label": "GitHub", "url": "https://github.com/your-org/your-repo", "type": "github" }, - { "label": "Twitter/X", "url": "https://x.com/yourcompany", "type": "social" }, - { "label": "Careers", "url": "https://your-company.example.com/careers", "type": "careers" } + { "label": "Homepage", "url": "https://tabstack.ai", "type": "homepage" }, + { "label": "Pricing", "url": "https://tabstack.ai/pricing", "type": "pricing", "geo_target": "US" }, + { "label": "Docs", "url": "https://docs.tabstack.ai", "type": "docs" }, + { "label": "Blog", "url": "https://tabstack.ai/blog", "type": "blog" }, + { "label": "GitHub", "url": "https://github.com/Mozilla-Ocho/tabstack-typescript", "type": "github" }, + { "label": "Twitter/X", "url": "https://x.com/tabstack", "type": "social" }, + { "label": "Careers", "url": "https://www.mozilla.org/en-US/careers/listings/", "type": "careers" } ] }, "competitors": [ { - "name": "Competitor A", - "slug": "competitor-a", - "url": "https://competitor-a.example.com", + "name": "Firecrawl", + "slug": "firecrawl", + "url": "https://firecrawl.dev", + "manual": { + "matrix_overrides": { + "managed_service_score": 7, + "llm_included_score": 7 + } + }, + "pages": [ + { "label": "Homepage", "url": "https://firecrawl.dev", "type": "homepage" }, + { "label": "Pricing", "url": "https://firecrawl.dev/pricing", "type": "pricing", "geo_target": "US" }, + { "label": "Changelog", "url": "https://firecrawl.dev/changelog", "type": "changelog" }, + { "label": "Careers", "url": "https://firecrawl.dev/careers", "type": "careers" }, + { "label": "GitHub", "url": "https://github.com/mendableai/firecrawl", "type": "github" }, + { "label": "Docs", "url": "https://docs.firecrawl.dev", "type": "docs" }, + { "label": "Blog", "url": "https://firecrawl.dev/blog", "type": "blog" }, + { "label": "About", "url": "https://www.firecrawl.dev/about", "type": "profile" }, + { "label": "Twitter/X", "url": "https://x.com/firecrawl_dev", "type": "social" }, + { + "label": "Reviews", + "url": "https://www.producthunt.com/products/extract-by-firecrawl/reviews", + "type": "reviews" + } + ] + }, + { + "name": "Browserbase", + "slug": "browserbase", + "url": "https://browserbase.com", + "pages": [ + { "label": "Homepage", "url": "https://browserbase.com", "type": "homepage" }, + { "label": "Pricing", "url": "https://browserbase.com/pricing", "type": "pricing", "geo_target": "US" }, + { "label": "Changelog", "url": "https://browserbase.com/changelog", "type": "changelog" }, + { "label": "Careers", "url": "https://browserbase.com/careers", "type": "careers" }, + { "label": "GitHub", "url": "https://github.com/browserbase/stagehand", "type": "github" }, + { "label": "Docs", "url": "https://docs.browserbase.com", "type": "docs" }, + { "label": "Blog", "url": "https://www.browserbase.com/blog", "type": "blog" }, + { "label": "Twitter/X", "url": "https://x.com/browserbase", "type": "social" }, + { "label": "Reviews", "url": "https://www.producthunt.com/products/browserbase/reviews", "type": "reviews" } + ], + "manual": { + "matrix_overrides": { + "managed_service_score": 9, + "llm_included_score": 1 + } + } + }, + { + "name": "Browser Use", + "slug": "browser-use", + "url": "https://browser-use.com", + "manual": { + "matrix_overrides": { + "managed_service_score": 3, + "llm_included_score": 2 + } + }, + "pages": [ + { "label": "Homepage", "url": "https://browser-use.com", "type": "homepage" }, + { "label": "Pricing", "url": "https://browser-use.com/pricing", "type": "pricing", "geo_target": "US" }, + { "label": "Careers", "url": "https://browser-use.com/careers", "type": "careers" }, + { "label": "Changelog", "url": "https://browser-use.com/changelog", "type": "changelog" }, + { "label": "GitHub", "url": "https://github.com/browser-use/browser-use", "type": "github" }, + { "label": "Docs", "url": "https://docs.browser-use.com", "type": "docs" }, + { "label": "Blog", "url": "https://browser-use.com/blog", "type": "blog" }, + { "label": "Twitter/X", "url": "https://x.com/browser_use", "type": "social" }, + { "label": "Reviews", "url": "https://www.producthunt.com/products/browser-use/reviews", "type": "reviews" } + ] + }, + { + "name": "Stagehand", + "slug": "stagehand", + "url": "https://stagehand.dev", + "manual": { + "matrix_overrides": { + "managed_service_score": 1, + "llm_included_score": 4 + } + }, + "pages": [ + { "label": "Homepage", "url": "https://stagehand.dev", "type": "homepage" }, + { "label": "GitHub", "url": "https://github.com/browserbase/stagehand", "type": "github" }, + { "label": "Docs", "url": "https://docs.stagehand.dev", "type": "docs" }, + { "label": "Changelog", "url": "https://stagehand.dev/changelog", "type": "changelog" }, + { "label": "About", "url": "https://www.browserbase.com/stagehand", "type": "profile" }, + { "label": "Twitter/X", "url": "https://x.com/stagehanddev", "type": "social" }, + { "label": "Reviews", "url": "https://www.producthunt.com/products/stagehand-2/reviews", "type": "reviews" } + ] + }, + { + "name": "Steel", + "slug": "steel", + "matrix": false, + "url": "https://steel.dev", + "pages": [ + { "label": "Homepage", "url": "https://steel.dev", "type": "homepage" }, + { "label": "Pricing", "url": "https://steel.dev/pricing", "type": "pricing", "geo_target": "US" }, + { "label": "GitHub", "url": "https://github.com/steel-dev/steel-browser", "type": "github" }, + { "label": "Docs", "url": "https://docs.steel.dev", "type": "docs" }, + { "label": "Changelog", "url": "https://steel.dev/changelog", "type": "changelog" }, + { "label": "Blog", "url": "https://steel.dev/blog", "type": "blog" }, + { "label": "Twitter/X", "url": "https://x.com/steeldotdev", "type": "social" } + ] + }, + { + "name": "Playwright", + "slug": "playwright", + "url": "https://playwright.dev", + "manual": { + "matrix_overrides": { + "managed_service_score": 1, + "llm_included_score": 1 + } + }, + "pages": [ + { "label": "Homepage", "url": "https://playwright.dev", "type": "homepage" }, + { "label": "GitHub", "url": "https://github.com/microsoft/playwright", "type": "github" }, + { "label": "Docs", "url": "https://playwright.dev/docs/intro", "type": "docs" }, + { "label": "Changelog", "url": "https://github.com/microsoft/playwright/releases", "type": "changelog" }, + { "label": "Blog", "url": "https://dev.to/playwright", "type": "blog" }, + { "label": "About", "url": "https://playwright.dev/community/welcome", "type": "profile" }, + { "label": "Twitter/X", "url": "https://x.com/playwrightweb", "type": "social" }, + { "label": "Reviews", "url": "https://www.g2.com/products/playwright/reviews", "type": "reviews" } + ] + }, + { + "name": "Apify", + "slug": "apify", + "url": "https://apify.com", "manual": { "matrix_overrides": { "managed_service_score": 8, - "llm_included_score": 6 + "llm_included_score": 4 } }, "pages": [ - { "label": "Homepage", "url": "https://competitor-a.example.com", "type": "homepage" }, - { "label": "Pricing", "url": "https://competitor-a.example.com/pricing", "type": "pricing", "geo_target": "US" }, - { "label": "Docs", "url": "https://docs.competitor-a.example.com", "type": "docs" }, - { "label": "Blog", "url": "https://competitor-a.example.com/blog", "type": "blog" }, - { "label": "Changelog", "url": "https://competitor-a.example.com/changelog", "type": "changelog" }, - { "label": "Careers", "url": "https://competitor-a.example.com/careers", "type": "careers" }, - { "label": "GitHub", "url": "https://github.com/your-org/competitor-a", "type": "github" }, - { "label": "Twitter/X", "url": "https://x.com/competitor_a", "type": "social" }, - { "label": "Reviews", "url": "https://www.producthunt.com/products/competitor-a/reviews", "type": "reviews" } + { "label": "Homepage", "url": "https://apify.com", "type": "homepage" }, + { "label": "Pricing", "url": "https://apify.com/pricing", "type": "pricing", "geo_target": "US" }, + { "label": "Changelog", "url": "https://apify.com/change-log", "type": "changelog" }, + { "label": "Careers", "url": "https://apify.com/jobs", "type": "careers" }, + { "label": "GitHub", "url": "https://github.com/apify/crawlee", "type": "github" }, + { "label": "Docs", "url": "https://docs.apify.com", "type": "docs" }, + { "label": "Blog", "url": "https://blog.apify.com", "type": "blog" }, + { "label": "About", "url": "https://apify.com/about", "type": "profile" }, + { "label": "Twitter/X", "url": "https://x.com/apify", "type": "social" }, + { "label": "Reviews", "url": "https://www.producthunt.com/products/apify/reviews", "type": "reviews" } + ] + }, + { + "name": "LangChain Browser Tools", + "slug": "langchain-browser", + "matrix": false, + "url": "https://langchain.com", + "pages": [ + { "label": "Homepage", "url": "https://langchain.com", "type": "homepage" }, + { "label": "Pricing", "url": "https://www.langchain.com/pricing", "type": "pricing", "geo_target": "US" }, + { "label": "Careers", "url": "https://www.langchain.com/careers", "type": "careers" }, + { "label": "Changelog", "url": "https://changelog.langchain.com/", "type": "changelog" }, + { "label": "GitHub", "url": "https://github.com/langchain-ai/langchainjs", "type": "github" }, + { "label": "Docs", "url": "https://js.langchain.com/docs/integrations/tools/", "type": "docs" }, + { "label": "Blog", "url": "https://blog.langchain.dev", "type": "blog" }, + { "label": "About", "url": "https://www.langchain.com/about", "type": "profile" }, + { "label": "Twitter/X", "url": "https://x.com/langchainai", "type": "social" }, + { "label": "Reviews", "url": "https://www.g2.com/products/langchain/reviews", "type": "reviews" } + ] + }, + { + "name": "Parallel AI", + "slug": "parallel-ai", + "matrix": false, + "url": "https://parallel.ai", + "pages": [ + { "label": "Homepage", "url": "https://parallel.ai", "type": "homepage" }, + { "label": "Pricing", "url": "https://parallel.ai/pricing", "type": "pricing", "geo_target": "US" }, + { "label": "Docs", "url": "https://docs.parallel.ai/home", "type": "docs" }, + { "label": "Blog", "url": "https://parallel.ai/blog", "type": "blog" }, + { "label": "About", "url": "https://parallel.ai/about", "type": "profile" }, + { "label": "Careers", "url": "https://jobs.ashbyhq.com/parallel", "type": "careers" }, + { "label": "Changelog", "url": "https://docs.parallel.ai/resources/changelog", "type": "changelog" }, + { "label": "GitHub", "url": "https://github.com/parallel-web", "type": "github" }, + { "label": "Twitter/X", "url": "https://x.com/p0", "type": "social" } + ] + }, + { + "name": "Exa", + "slug": "exa", + "matrix": false, + "url": "https://exa.ai", + "pages": [ + { "label": "Homepage", "url": "https://exa.ai", "type": "homepage" }, + { "label": "Pricing", "url": "https://exa.ai/pricing", "type": "pricing", "geo_target": "US" }, + { "label": "Docs", "url": "https://exa.ai/docs", "type": "docs" }, + { "label": "Blog", "url": "https://exa.ai/blog", "type": "blog" }, + { "label": "Careers", "url": "https://exa.ai/careers", "type": "careers" }, + { "label": "About", "url": "https://exa.ai/about", "type": "profile" }, + { "label": "Changelog", "url": "https://exa.ai/docs/changelog", "type": "changelog" }, + { "label": "GitHub", "url": "https://github.com/exa-labs", "type": "github" }, + { "label": "Twitter/X", "url": "https://x.com/ExaAILabs", "type": "social" }, + { "label": "Reviews", "url": "https://www.producthunt.com/products/exa-ai", "type": "reviews" } + ] + }, + { + "name": "Perplexity", + "slug": "perplexity", + "matrix": false, + "url": "https://www.perplexity.ai", + "pages": [ + { "label": "Homepage", "url": "https://www.perplexity.ai", "type": "homepage" }, + { "label": "Pricing", "url": "https://www.perplexity.ai/pro", "type": "pricing", "geo_target": "US" }, + { "label": "Docs", "url": "https://docs.perplexity.ai", "type": "docs" }, + { "label": "Blog", "url": "https://www.perplexity.ai/hub", "type": "blog" }, + { "label": "Careers", "url": "https://www.perplexity.ai/hub/careers", "type": "careers" }, + { "label": "Changelog", "url": "https://www.perplexity.ai/changelog", "type": "changelog" }, + { "label": "GitHub", "url": "https://github.com/perplexityai", "type": "github" }, + { "label": "Twitter/X", "url": "https://x.com/perplexity_ai", "type": "social" }, + { "label": "Reviews", "url": "https://www.producthunt.com/products/perplexity-ai", "type": "reviews" } + ] + }, + { + "name": "Bright Data", + "slug": "brightdata", + "matrix": false, + "url": "https://brightdata.com", + "pages": [ + { "label": "Homepage", "url": "https://brightdata.com", "type": "homepage" }, + { "label": "Pricing", "url": "https://brightdata.com/pricing", "type": "pricing", "geo_target": "US" }, + { "label": "Docs", "url": "https://docs.brightdata.com", "type": "docs" }, + { "label": "Blog", "url": "https://brightdata.com/blog", "type": "blog" }, + { "label": "Careers", "url": "https://brightdata.com/careers", "type": "careers" }, + { "label": "About", "url": "https://brightdata.com/about", "type": "profile" }, + { "label": "Changelog", "url": "https://docs.brightdata.com/release-notes", "type": "changelog" }, + { "label": "GitHub", "url": "https://github.com/brightdata", "type": "github" }, + { "label": "Twitter/X", "url": "https://x.com/brightdata", "type": "social" }, + { "label": "Reviews", "url": "https://www.g2.com/products/bright-data/reviews", "type": "reviews" } + ] + }, + { + "name": "Browser AI", + "slug": "browser-ai", + "matrix": false, + "url": "https://browser.ai", + "pages": [ + { "label": "Homepage", "url": "https://browser.ai", "type": "homepage" }, + { "label": "Pricing", "url": "https://browser.ai/pricing", "type": "pricing", "geo_target": "US" }, + { "label": "Docs", "url": "https://docs.browser.ai/general/start", "type": "docs" }, + { "label": "Blog", "url": "https://browser.ai/news", "type": "blog" }, + { "label": "GitHub", "url": "https://github.com/browserai", "type": "github" } ] }, { - "name": "Competitor B", - "slug": "competitor-b", - "url": "https://competitor-b.example.com", + "name": "fastCRW", + "slug": "fastcrw", + "matrix": false, + "url": "https://fastcrw.com", "pages": [ - { "label": "Homepage", "url": "https://competitor-b.example.com", "type": "homepage" }, - { "label": "Pricing", "url": "https://competitor-b.example.com/pricing", "type": "pricing", "geo_target": "US" }, - { "label": "Docs", "url": "https://docs.competitor-b.example.com", "type": "docs" }, - { "label": "Blog", "url": "https://competitor-b.example.com/blog", "type": "blog" }, - { "label": "GitHub", "url": "https://github.com/your-org/competitor-b", "type": "github" }, - { "label": "Twitter/X", "url": "https://x.com/competitor_b", "type": "social" } + { "label": "Homepage", "url": "https://fastcrw.com", "type": "homepage" }, + { "label": "Pricing", "url": "https://fastcrw.com/pricing", "type": "pricing", "geo_target": "US" }, + { "label": "Docs", "url": "https://docs.fastcrw.com", "type": "docs" }, + { "label": "Blog", "url": "https://fastcrw.com/blog", "type": "blog" }, + { "label": "GitHub", "url": "https://github.com/us/crw", "type": "github" } ] } ], From ee78ba5d90032823961fdde96584674b2ff44ea4 Mon Sep 17 00:00:00 2001 From: Tessa Kriesel Date: Sun, 31 May 2026 19:45:02 -0500 Subject: [PATCH 2/3] style: fix prettier formatting in page.tsx and research.test.ts --- app/[slug]/page.tsx | 11 +++++++++-- lib/tabstack/__tests__/research.test.ts | 8 +++++++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/app/[slug]/page.tsx b/app/[slug]/page.tsx index 8524a1a..1c070fb 100644 --- a/app/[slug]/page.tsx +++ b/app/[slug]/page.tsx @@ -140,7 +140,12 @@ export default async function CompetitorDetailPage({ params }: PageProps) { - {intelligenceBrief && (isSelf ? : )} + {intelligenceBrief && + (isSelf ? ( + + ) : ( + + ))} @@ -576,7 +581,9 @@ function SelfBriefSection({ brief }: { brief: Record }) { {icp && ( <> ICP SUMMARY -

+

{icp}

diff --git a/lib/tabstack/__tests__/research.test.ts b/lib/tabstack/__tests__/research.test.ts index 5cefeea..89c0cb3 100644 --- a/lib/tabstack/__tests__/research.test.ts +++ b/lib/tabstack/__tests__/research.test.ts @@ -303,7 +303,13 @@ describe("runResearch", () => { report: "Browser Use raised $17M [1].", metadata: { citedPages: [ - { id: "v1", url: "https://browser-use.com/posts/seed-round", title: "We Raised $17M", claims: [], sourceQueries: [] } + { + id: "v1", + url: "https://browser-use.com/posts/seed-round", + title: "We Raised $17M", + claims: [], + sourceQueries: [] + } ] } } From 6447e3696e02ecb2a74d36bc81043f75cd1e8570 Mon Sep 17 00:00:00 2001 From: Tessa Kriesel Date: Sun, 31 May 2026 20:43:46 -0500 Subject: [PATCH 3/3] fix(config): address all code review findings from PR #105 - rivals.config.json / rivals.config.tabstack.json: - Fix Browserbase GitHub URL (was pointing to stagehand repo, now sdk) - Add /reviews suffix to Perplexity and Exa ProductHunt URLs - Remove unverifiable Parallel AI Twitter handle (x.com/p0) - Remove fastCRW placeholder GitHub URL (github.com/us/crw) - app/matrix/page.tsx: - Replace static rivals.config.json import with runtime loadRivalConfig() so matrix:false exclusions stay in sync without requiring a redeploy - scripts/bootstrap-new-competitors.ts: - Add 25-minute wall-clock deadline to prevent Netlify build timeouts when bootstrapping many new competitors on a cold deploy --- app/matrix/page.tsx | 10 ++-------- rivals.config.json | 12 +++++------- rivals.config.tabstack.json | 12 +++++------- scripts/bootstrap-new-competitors.ts | 10 ++++++++++ 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/app/matrix/page.tsx b/app/matrix/page.tsx index b461dc7..b63c08e 100644 --- a/app/matrix/page.tsx +++ b/app/matrix/page.tsx @@ -4,15 +4,15 @@ import { getAxisScore } from "@/lib/matrix/overrides"; import { PositioningMatrix, type MatrixPoint } from "@/components/matrix/PositioningMatrix"; import { MatrixDownloadButton } from "@/components/matrix/MatrixDownloadButton"; import { RDSPageShell, RDSHeader, RDSFooter, RDSEmpty, RDSKicker } from "@/components/rds"; -import rivalConfigJson from "../../rivals.config.json"; - export const dynamic = "force-dynamic"; export default async function MatrixPage() { let matrixConfig: MatrixConfig; + let matrixExcluded = new Set(); try { const config = loadRivalConfig(); matrixConfig = config.matrix ?? DEFAULT_MATRIX_CONFIG; + matrixExcluded = new Set(config.competitors.filter((c) => c.matrix === false).map((c) => c.slug)); } catch (err) { if (err instanceof Error && (err as NodeJS.ErrnoException).code === "ENOENT") { matrixConfig = DEFAULT_MATRIX_CONFIG; @@ -21,12 +21,6 @@ export default async function MatrixPage() { } } - const matrixExcluded = new Set( - (rivalConfigJson.competitors as Array<{ slug: string; matrix?: boolean }>) - .filter((c) => c.matrix === false) - .map((c) => c.slug) - ); - const competitors = await prisma.competitor.findMany({ select: { id: true, name: true, slug: true, intelligenceBrief: true, manualData: true, isSelf: true }, where: matrixExcluded.size > 0 ? { slug: { notIn: [...matrixExcluded] } } : undefined, diff --git a/rivals.config.json b/rivals.config.json index b39ac7a..a1df71a 100644 --- a/rivals.config.json +++ b/rivals.config.json @@ -56,7 +56,7 @@ { "label": "Pricing", "url": "https://browserbase.com/pricing", "type": "pricing", "geo_target": "US" }, { "label": "Changelog", "url": "https://browserbase.com/changelog", "type": "changelog" }, { "label": "Careers", "url": "https://browserbase.com/careers", "type": "careers" }, - { "label": "GitHub", "url": "https://github.com/browserbase/stagehand", "type": "github" }, + { "label": "GitHub", "url": "https://github.com/browserbase/sdk", "type": "github" }, { "label": "Docs", "url": "https://docs.browserbase.com", "type": "docs" }, { "label": "Blog", "url": "https://www.browserbase.com/blog", "type": "blog" }, { "label": "Twitter/X", "url": "https://x.com/browserbase", "type": "social" }, @@ -201,8 +201,7 @@ { "label": "About", "url": "https://parallel.ai/about", "type": "profile" }, { "label": "Careers", "url": "https://jobs.ashbyhq.com/parallel", "type": "careers" }, { "label": "Changelog", "url": "https://docs.parallel.ai/resources/changelog", "type": "changelog" }, - { "label": "GitHub", "url": "https://github.com/parallel-web", "type": "github" }, - { "label": "Twitter/X", "url": "https://x.com/p0", "type": "social" } + { "label": "GitHub", "url": "https://github.com/parallel-web", "type": "github" } ] }, { @@ -220,7 +219,7 @@ { "label": "Changelog", "url": "https://exa.ai/docs/changelog", "type": "changelog" }, { "label": "GitHub", "url": "https://github.com/exa-labs", "type": "github" }, { "label": "Twitter/X", "url": "https://x.com/ExaAILabs", "type": "social" }, - { "label": "Reviews", "url": "https://www.producthunt.com/products/exa-ai", "type": "reviews" } + { "label": "Reviews", "url": "https://www.producthunt.com/products/exa-ai/reviews", "type": "reviews" } ] }, { @@ -237,7 +236,7 @@ { "label": "Changelog", "url": "https://www.perplexity.ai/changelog", "type": "changelog" }, { "label": "GitHub", "url": "https://github.com/perplexityai", "type": "github" }, { "label": "Twitter/X", "url": "https://x.com/perplexity_ai", "type": "social" }, - { "label": "Reviews", "url": "https://www.producthunt.com/products/perplexity-ai", "type": "reviews" } + { "label": "Reviews", "url": "https://www.producthunt.com/products/perplexity-ai/reviews", "type": "reviews" } ] }, { @@ -280,8 +279,7 @@ { "label": "Homepage", "url": "https://fastcrw.com", "type": "homepage" }, { "label": "Pricing", "url": "https://fastcrw.com/pricing", "type": "pricing", "geo_target": "US" }, { "label": "Docs", "url": "https://docs.fastcrw.com", "type": "docs" }, - { "label": "Blog", "url": "https://fastcrw.com/blog", "type": "blog" }, - { "label": "GitHub", "url": "https://github.com/us/crw", "type": "github" } + { "label": "Blog", "url": "https://fastcrw.com/blog", "type": "blog" } ] } ], diff --git a/rivals.config.tabstack.json b/rivals.config.tabstack.json index b39ac7a..a1df71a 100644 --- a/rivals.config.tabstack.json +++ b/rivals.config.tabstack.json @@ -56,7 +56,7 @@ { "label": "Pricing", "url": "https://browserbase.com/pricing", "type": "pricing", "geo_target": "US" }, { "label": "Changelog", "url": "https://browserbase.com/changelog", "type": "changelog" }, { "label": "Careers", "url": "https://browserbase.com/careers", "type": "careers" }, - { "label": "GitHub", "url": "https://github.com/browserbase/stagehand", "type": "github" }, + { "label": "GitHub", "url": "https://github.com/browserbase/sdk", "type": "github" }, { "label": "Docs", "url": "https://docs.browserbase.com", "type": "docs" }, { "label": "Blog", "url": "https://www.browserbase.com/blog", "type": "blog" }, { "label": "Twitter/X", "url": "https://x.com/browserbase", "type": "social" }, @@ -201,8 +201,7 @@ { "label": "About", "url": "https://parallel.ai/about", "type": "profile" }, { "label": "Careers", "url": "https://jobs.ashbyhq.com/parallel", "type": "careers" }, { "label": "Changelog", "url": "https://docs.parallel.ai/resources/changelog", "type": "changelog" }, - { "label": "GitHub", "url": "https://github.com/parallel-web", "type": "github" }, - { "label": "Twitter/X", "url": "https://x.com/p0", "type": "social" } + { "label": "GitHub", "url": "https://github.com/parallel-web", "type": "github" } ] }, { @@ -220,7 +219,7 @@ { "label": "Changelog", "url": "https://exa.ai/docs/changelog", "type": "changelog" }, { "label": "GitHub", "url": "https://github.com/exa-labs", "type": "github" }, { "label": "Twitter/X", "url": "https://x.com/ExaAILabs", "type": "social" }, - { "label": "Reviews", "url": "https://www.producthunt.com/products/exa-ai", "type": "reviews" } + { "label": "Reviews", "url": "https://www.producthunt.com/products/exa-ai/reviews", "type": "reviews" } ] }, { @@ -237,7 +236,7 @@ { "label": "Changelog", "url": "https://www.perplexity.ai/changelog", "type": "changelog" }, { "label": "GitHub", "url": "https://github.com/perplexityai", "type": "github" }, { "label": "Twitter/X", "url": "https://x.com/perplexity_ai", "type": "social" }, - { "label": "Reviews", "url": "https://www.producthunt.com/products/perplexity-ai", "type": "reviews" } + { "label": "Reviews", "url": "https://www.producthunt.com/products/perplexity-ai/reviews", "type": "reviews" } ] }, { @@ -280,8 +279,7 @@ { "label": "Homepage", "url": "https://fastcrw.com", "type": "homepage" }, { "label": "Pricing", "url": "https://fastcrw.com/pricing", "type": "pricing", "geo_target": "US" }, { "label": "Docs", "url": "https://docs.fastcrw.com", "type": "docs" }, - { "label": "Blog", "url": "https://fastcrw.com/blog", "type": "blog" }, - { "label": "GitHub", "url": "https://github.com/us/crw", "type": "github" } + { "label": "Blog", "url": "https://fastcrw.com/blog", "type": "blog" } ] } ], diff --git a/scripts/bootstrap-new-competitors.ts b/scripts/bootstrap-new-competitors.ts index cf5b443..a6e89d8 100644 --- a/scripts/bootstrap-new-competitors.ts +++ b/scripts/bootstrap-new-competitors.ts @@ -83,7 +83,10 @@ async function bootstrapCompetitor(competitor: { } } +const BOOTSTRAP_DEADLINE_MS = parseInt(process.env.BOOTSTRAP_TIMEOUT_MS ?? "1500000"); // 25 min + async function main() { + const startedAt = Date.now(); const competitors = await prisma.competitor.findMany({ include: { pages: true }, orderBy: { slug: "asc" } @@ -114,6 +117,13 @@ async function main() { ); for (const competitor of toBootstrap) { + if (Date.now() - startedAt > BOOTSTRAP_DEADLINE_MS) { + const remaining = toBootstrap.slice(toBootstrap.indexOf(competitor)).map((c) => c.slug); + console.warn( + `[bootstrap] Time limit reached (${BOOTSTRAP_DEADLINE_MS / 60000} min). Skipping: ${remaining.join(", ")}. These will be picked up on the next cron run.` + ); + break; + } // Re-check immediately before bootstrapping. The snapshot above can go // stale if a concurrent cron run or a parallel deploy writes the first // scan between now and this loop iteration — without this check, two