mirror of
https://github.com/different-ai/openwork
synced 2026-04-25 17:15:34 +02:00
feat(landing): SEO metadata, JSON-LD, H1 fixes, manifest (#1493)
* feat(landing): SEO metadata, JSON-LD, H1 fixes, manifest Raises the site's traditional SEO to match its strong agent-readiness foundations (llms.txt, api-catalog, markdown negotiation). Metadata: - Root layout: canonical, og:type/siteName/locale, explicit robots - Per-page canonical + openGraph.url on every route - Rewritten titles/descriptions for /pricing, /enterprise, /den, /download (previously 14-21 char titles and 51-73 char descriptions) - /feedback and /starter-success marked noindex,follow - /feedback removed from sitemap; /privacy and /terms demoted to 0.3 - Title dash normalized to em-dash site-wide Headings: - /den gains a real <h1> (was <h2> only — a11y + SEO bug) - /enterprise H1 rewritten to be enterprise-specific (was a dup of home) - /pricing H1 enriched; tier names promoted to <h2> in pricing-grid - /download nav-card duplicate H2s demoted to spans - lib/agent-markdown.ts H1s realigned with HTML for agent parity Structured data: - New components/structured-data.tsx helper - Organization on every page (root) - SoftwareApplication on / and /download - Product with three Offers on /pricing Manifest & perf: - New app/manifest.ts - Enterprise hero: 12MB PNG -> 1.1MB JPEG (1600px q70) Old PNG left in public/ for a follow-up cleanup commit * chore(landing): remove unused /den marketing page Deletes the /den route and its components (LandingDen, DenHero) along with every internal reference. The cloud product is still linked from nav + footer, pointing at https://app.openworklabs.com. - Removed from sitemap.ts, middleware.ts matcher, and agent-markdown (both the markdown twin and the "Den (team workspace)" home mention) - Removed from llms.txt routing list and site map - Footer "Cloud" link now points to the external app (matches nav) - Nav internal key renamed from "den" to "cloud" and type union updated - WebMCP navigate_to destinations no longer advertise "den" api-catalog still references "OpenWork Den API" since that's the API product name, not the marketing page — untouched. * fix(landing): point nav + footer Cloud link at /cloud redirect Previous removal of /den hardcoded the external app URL into the nav and footer. Switch to the /cloud internal path so the existing cloud redirect is the single source of truth (and works consistently with the rest of the internal links). * docs(landing): restore Cloud mentions in llms.txt and agent-markdown The previous /den removal also stripped every Cloud mention from the agent-facing text. Restore them pointing at /cloud (which redirects to https://app.openworklabs.com) so agents can still route users to the hosted workspace. * fix(landing): link Cloud directly to app.openworklabs.com, not /cloud There's no /cloud route on the marketing site — the hosted app lives at https://app.openworklabs.com. Point every Cloud link (nav, footer, llms.txt, agent-markdown, WebMCP) at the real URL. * feat(landing): replace Trust details link with Security Review CTA The old small Trust details text link under the Book a call button got lost. Surface it as a proper secondary CTA next to Book a call, and rename to Security Review to set expectations for enterprise buyers. Also updates the enterprise markdown twin to mirror the label. * fix(landing): preserve og:type/siteName/locale/images on pages overriding openGraph Next.js replaces (not merges) the openGraph object across layout/page boundaries. Earlier per-page metadata added only og:url, which silently wiped og:type, og:siteName, og:locale, and og:image off every page that did so — exactly the fields the root layout relied on for defaults. Introduce lib/seo.ts with a baseOpenGraph constant and spread it into every per-page openGraph override. Caught by running the PR's test plan against the dev server: 12 failing checks → 0 after this fix. * feat(landing): rasterize SVG into favicon.ico + PNG icon set Adds proper raster icons for browsers/OSes that don't render SVG favicons (older Safari, Android home-screen, Windows taskbar): - app/favicon.ico — 32×32, PNG-in-ICO container - app/icon.png — 192×192 - app/apple-icon.png — 180×180 Uses Next.js App Router icon-file convention — Next auto-generates the <link rel="icon"> / <link rel="apple-touch-icon"> tags, so the manual icons: metadata block in app/layout.tsx is removed. Manifest updated to advertise the 192×192 PNG (the Android PWA pick) alongside the 180 apple-icon and the SVG mark. All three PNGs generated from public/openwork-mark.svg with sharp, centered on a white square canvas with ~10% padding so the mark doesn't bleed to the edges at any size. Generator script at /tmp/generate-favicons.mjs (not committed) — re-run any time the source SVG changes. * fix(landing): match /enterprise H1 styling + copy to / The /enterprise hero H1 used slightly different Tailwind classes (mb-6, leading-[1.05]) than the homepage H1 (mb-5, leading-[1.1]), giving a visibly different type rhythm. Align to the home page classes. Copy updated to 'A privacy-first alternative to Claude Cowork for your org' — clearer positioning than 'OpenWork for the enterprise'. Mirrored in the agent-markdown enterprise twin for parity. * fix(landing): use 'organization' not 'org' in /enterprise H1 * fix(landing): restore original /enterprise hero subhead copy * fix(landing): open Docs in a new tab from the main nav Footer's Docs link already opens in a new tab; the top nav didn't. Add an explicit newTab flag to the nav items array and honor it in both the desktop and mobile menus so visitors keep the marketing tab open while reading docs.
This commit is contained in:
BIN
ee/apps/landing/app/apple-icon.png
Normal file
BIN
ee/apps/landing/app/apple-icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.1 KiB |
@@ -1,22 +0,0 @@
|
||||
import { LandingDen } from "../../components/landing-den";
|
||||
import { getGithubData } from "../../lib/github";
|
||||
|
||||
export const metadata = {
|
||||
title: "OpenWork — Den",
|
||||
description:
|
||||
"Always-on AI workers that handle repetitive work for your team and report back in Slack, Telegram, or the desktop app.",
|
||||
};
|
||||
|
||||
export default async function Den() {
|
||||
const github = await getGithubData();
|
||||
const cal = process.env.NEXT_PUBLIC_CAL_URL || "/enterprise#book";
|
||||
|
||||
return (
|
||||
<LandingDen
|
||||
stars={github.stars}
|
||||
downloadHref={github.downloads.macos}
|
||||
getStartedHref="https://app.openworklabs.com"
|
||||
callHref={cal}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -1,11 +1,42 @@
|
||||
import { SiteFooter } from "../../components/site-footer";
|
||||
import { SiteNav } from "../../components/site-nav";
|
||||
import { StructuredData } from "../../components/structured-data";
|
||||
import { getGithubData } from "../../lib/github";
|
||||
import { baseOpenGraph } from "../../lib/seo";
|
||||
|
||||
const downloadSchema = {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "SoftwareApplication",
|
||||
name: "OpenWork",
|
||||
description:
|
||||
"Open source Claude Cowork alternative. Desktop app for macOS, Windows, and Linux that lets teams use 50+ LLMs with their own provider keys.",
|
||||
url: "https://openworklabs.com/download",
|
||||
downloadUrl: "https://github.com/different-ai/openwork/releases/latest",
|
||||
applicationCategory: "BusinessApplication",
|
||||
operatingSystem: "macOS, Windows, Linux",
|
||||
offers: {
|
||||
"@type": "Offer",
|
||||
price: "0",
|
||||
priceCurrency: "USD"
|
||||
},
|
||||
publisher: {
|
||||
"@type": "Organization",
|
||||
name: "OpenWork",
|
||||
url: "https://openworklabs.com"
|
||||
}
|
||||
};
|
||||
|
||||
export const metadata = {
|
||||
title: "OpenWork - Download",
|
||||
title: "Download OpenWork — macOS, Windows, Linux",
|
||||
description:
|
||||
"Download OpenWork desktop for macOS, Windows, and Linux. Includes AUR install instructions and direct package downloads.",
|
||||
alternates: {
|
||||
canonical: "/download"
|
||||
},
|
||||
openGraph: {
|
||||
...baseOpenGraph,
|
||||
url: "https://openworklabs.com/download"
|
||||
}
|
||||
};
|
||||
|
||||
export default async function Download() {
|
||||
@@ -15,6 +46,7 @@ export default async function Download() {
|
||||
|
||||
return (
|
||||
<div className="min-h-screen">
|
||||
<StructuredData data={downloadSchema} />
|
||||
<SiteNav
|
||||
stars={github.stars}
|
||||
downloadHref={github.downloads.macos}
|
||||
@@ -52,21 +84,21 @@ export default async function Download() {
|
||||
href="#macos"
|
||||
className="feature-card border-sky-100 bg-sky-50/60 transition hover:border-sky-200"
|
||||
>
|
||||
<h2 className="mb-2 text-[16px] font-semibold text-gray-900">macOS</h2>
|
||||
<span className="mb-2 block text-[16px] font-semibold text-gray-900">macOS</span>
|
||||
<p className="text-[14px] text-gray-700">Apple Silicon and Intel builds</p>
|
||||
</a>
|
||||
<a
|
||||
href="#windows"
|
||||
className="feature-card border-violet-100 bg-violet-50/50 transition hover:border-violet-200"
|
||||
>
|
||||
<h2 className="mb-2 text-[16px] font-semibold text-gray-900">Windows</h2>
|
||||
<span className="mb-2 block text-[16px] font-semibold text-gray-900">Windows</span>
|
||||
<p className="text-[14px] text-gray-700">x64 MSI installer</p>
|
||||
</a>
|
||||
<a
|
||||
href="#linux"
|
||||
className="feature-card border-emerald-100 bg-emerald-50/60 transition hover:border-emerald-200"
|
||||
>
|
||||
<h2 className="mb-2 text-[16px] font-semibold text-gray-900">Linux</h2>
|
||||
<span className="mb-2 block text-[16px] font-semibold text-gray-900">Linux</span>
|
||||
<p className="text-[14px] text-gray-700">AUR, .deb, and .rpm options</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -1,9 +1,18 @@
|
||||
import { LandingEnterprise } from "../../components/landing-enterprise";
|
||||
import { getGithubData } from "../../lib/github";
|
||||
import { baseOpenGraph } from "../../lib/seo";
|
||||
|
||||
export const metadata = {
|
||||
title: "OpenWork — Enterprise",
|
||||
description: "Secure hosting for safe, permissioned AI employees."
|
||||
title: "OpenWork Enterprise — Self-hosted AI agents for teams",
|
||||
description:
|
||||
"Deploy shared skills, MCPs, and agent workflows across your org. Self-hosted or managed, 50+ LLM providers, HIPAA / SOC 2 / ISO 27001 / GDPR ready.",
|
||||
alternates: {
|
||||
canonical: "/enterprise"
|
||||
},
|
||||
openGraph: {
|
||||
...baseOpenGraph,
|
||||
url: "https://openworklabs.com/enterprise"
|
||||
}
|
||||
};
|
||||
|
||||
export default async function Enterprise() {
|
||||
|
||||
BIN
ee/apps/landing/app/favicon.ico
Normal file
BIN
ee/apps/landing/app/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 810 B |
@@ -2,10 +2,22 @@ import Link from "next/link";
|
||||
import { AppFeedbackForm, type AppFeedbackPrefill } from "../../components/app-feedback-form";
|
||||
import { OpenWorkMark } from "../../components/openwork-mark";
|
||||
import { SiteFooter } from "../../components/site-footer";
|
||||
import { baseOpenGraph } from "../../lib/seo";
|
||||
|
||||
export const metadata = {
|
||||
title: "OpenWork - Feedback",
|
||||
title: "OpenWork — Feedback",
|
||||
description: "Send app feedback to the OpenWork team with prefilled runtime context.",
|
||||
alternates: {
|
||||
canonical: "/feedback"
|
||||
},
|
||||
robots: {
|
||||
index: false,
|
||||
follow: true
|
||||
},
|
||||
openGraph: {
|
||||
...baseOpenGraph,
|
||||
url: "https://openworklabs.com/feedback"
|
||||
}
|
||||
};
|
||||
|
||||
type PageProps = {
|
||||
|
||||
BIN
ee/apps/landing/app/icon.png
Normal file
BIN
ee/apps/landing/app/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.5 KiB |
@@ -3,6 +3,17 @@ import { Inter, JetBrains_Mono } from "next/font/google";
|
||||
import Script from "next/script";
|
||||
import { BotIdClient } from "botid/client";
|
||||
import { WebMcpProvider } from "../components/webmcp-provider";
|
||||
import { StructuredData } from "../components/structured-data";
|
||||
|
||||
const organizationSchema = {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "Organization",
|
||||
name: "OpenWork",
|
||||
legalName: "Different AI",
|
||||
url: "https://openworklabs.com",
|
||||
logo: "https://openworklabs.com/openwork-mark.svg",
|
||||
sameAs: ["https://github.com/different-ai/openwork"]
|
||||
};
|
||||
|
||||
const inter = Inter({
|
||||
subsets: ["latin"],
|
||||
@@ -18,18 +29,25 @@ const jetbrains = JetBrains_Mono({
|
||||
|
||||
export const metadata = {
|
||||
metadataBase: new URL("https://openworklabs.com"),
|
||||
title: "OpenWork — The open source Claude Cowork alternative",
|
||||
title: "OpenWork — Open source Claude Cowork alternative for teams",
|
||||
description:
|
||||
"Bring your own model and provider, wire in your tools and context, and ship reusable agent setups across your org — with guardrails built in.",
|
||||
alternates: {
|
||||
canonical: "/"
|
||||
},
|
||||
robots: {
|
||||
index: true,
|
||||
follow: true
|
||||
},
|
||||
openGraph: {
|
||||
type: "website",
|
||||
siteName: "OpenWork",
|
||||
locale: "en_US",
|
||||
images: ["/og-image-clean.png"]
|
||||
},
|
||||
twitter: {
|
||||
card: "summary_large_image",
|
||||
images: ["/og-image-clean.png"]
|
||||
},
|
||||
icons: {
|
||||
icon: "/openwork-mark.svg"
|
||||
}
|
||||
};
|
||||
|
||||
@@ -46,6 +64,7 @@ export default function RootLayout({
|
||||
return (
|
||||
<html lang="en" className={`${inter.variable} ${jetbrains.variable}`}>
|
||||
<head>
|
||||
<StructuredData data={organizationSchema} />
|
||||
<BotIdClient protect={protectedRoutes} />
|
||||
<Script
|
||||
id="posthog"
|
||||
|
||||
33
ee/apps/landing/app/manifest.ts
Normal file
33
ee/apps/landing/app/manifest.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import type { MetadataRoute } from "next";
|
||||
|
||||
export default function manifest(): MetadataRoute.Manifest {
|
||||
return {
|
||||
name: "OpenWork",
|
||||
short_name: "OpenWork",
|
||||
description:
|
||||
"Open source Claude Cowork alternative. Bring your own model, wire in your tools, and ship reusable agent setups across your org.",
|
||||
start_url: "/",
|
||||
display: "standalone",
|
||||
background_color: "#ffffff",
|
||||
theme_color: "#011627",
|
||||
icons: [
|
||||
{
|
||||
src: "/icon.png",
|
||||
type: "image/png",
|
||||
sizes: "192x192",
|
||||
purpose: "any"
|
||||
},
|
||||
{
|
||||
src: "/apple-icon.png",
|
||||
type: "image/png",
|
||||
sizes: "180x180",
|
||||
purpose: "any"
|
||||
},
|
||||
{
|
||||
src: "/openwork-mark.svg",
|
||||
type: "image/svg+xml",
|
||||
sizes: "any"
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
@@ -1,6 +1,40 @@
|
||||
import { LandingHome } from "../components/landing-home";
|
||||
import { getGithubData } from "../lib/github";
|
||||
import { headers } from "next/headers";
|
||||
import { StructuredData } from "../components/structured-data";
|
||||
import { baseOpenGraph } from "../lib/seo";
|
||||
|
||||
export const metadata = {
|
||||
alternates: {
|
||||
canonical: "/"
|
||||
},
|
||||
openGraph: {
|
||||
...baseOpenGraph,
|
||||
url: "https://openworklabs.com"
|
||||
}
|
||||
};
|
||||
|
||||
const softwareApplicationSchema = {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "SoftwareApplication",
|
||||
name: "OpenWork",
|
||||
description:
|
||||
"Open source Claude Cowork alternative. Desktop app that lets teams use 50+ LLMs, bring their own provider keys, and ship reusable agent setups with guardrails.",
|
||||
url: "https://openworklabs.com",
|
||||
applicationCategory: "BusinessApplication",
|
||||
operatingSystem: "macOS, Windows, Linux",
|
||||
offers: {
|
||||
"@type": "Offer",
|
||||
price: "0",
|
||||
priceCurrency: "USD",
|
||||
url: "https://openworklabs.com/pricing"
|
||||
},
|
||||
publisher: {
|
||||
"@type": "Organization",
|
||||
name: "OpenWork",
|
||||
url: "https://openworklabs.com"
|
||||
}
|
||||
};
|
||||
|
||||
export default async function Home() {
|
||||
const github = await getGithubData();
|
||||
@@ -9,11 +43,14 @@ export default async function Home() {
|
||||
const isMobileVisitor = /android|iphone|ipad|ipod|mobile/.test(userAgent);
|
||||
|
||||
return (
|
||||
<LandingHome
|
||||
stars={github.stars}
|
||||
downloadHref={github.downloads.macos}
|
||||
callHref={cal}
|
||||
isMobileVisitor={isMobileVisitor}
|
||||
/>
|
||||
<>
|
||||
<StructuredData data={softwareApplicationSchema} />
|
||||
<LandingHome
|
||||
stars={github.stars}
|
||||
downloadHref={github.downloads.macos}
|
||||
callHref={cal}
|
||||
isMobileVisitor={isMobileVisitor}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,12 +2,63 @@ import { LandingBackground } from "../../components/landing-background";
|
||||
import { PricingGrid } from "../../components/pricing-grid";
|
||||
import { SiteFooter } from "../../components/site-footer";
|
||||
import { SiteNav } from "../../components/site-nav";
|
||||
import { StructuredData } from "../../components/structured-data";
|
||||
import { getGithubData } from "../../lib/github";
|
||||
import { baseOpenGraph } from "../../lib/seo";
|
||||
|
||||
const pricingSchema = {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "Product",
|
||||
name: "OpenWork",
|
||||
description:
|
||||
"OpenWork is an open source Claude Cowork alternative — a desktop app for teams to use 50+ LLMs, bring their own keys, and share reusable agent setups with guardrails.",
|
||||
brand: { "@type": "Brand", name: "OpenWork" },
|
||||
offers: [
|
||||
{
|
||||
"@type": "Offer",
|
||||
name: "Solo",
|
||||
price: "0",
|
||||
priceCurrency: "USD",
|
||||
url: "https://openworklabs.com/download",
|
||||
availability: "https://schema.org/InStock",
|
||||
description: "Free forever. Open source desktop app with bring-your-own-keys."
|
||||
},
|
||||
{
|
||||
"@type": "Offer",
|
||||
name: "Team Starter",
|
||||
price: "50",
|
||||
priceCurrency: "USD",
|
||||
url: "https://app.openworklabs.com/checkout",
|
||||
availability: "https://schema.org/InStock",
|
||||
priceSpecification: {
|
||||
"@type": "UnitPriceSpecification",
|
||||
price: "50",
|
||||
priceCurrency: "USD",
|
||||
unitText: "MONTH"
|
||||
},
|
||||
description: "5 seats, API access, Skill Hub Manager, distributed keys."
|
||||
},
|
||||
{
|
||||
"@type": "Offer",
|
||||
name: "Enterprise",
|
||||
url: "https://openworklabs.com/enterprise",
|
||||
description:
|
||||
"Custom pricing. Enterprise rollout support, deployment guidance, and custom commercial terms."
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
export const metadata = {
|
||||
title: "OpenWork — Pricing",
|
||||
title: "OpenWork Pricing — Free desktop, $50/mo cloud, enterprise",
|
||||
description:
|
||||
"Free desktop app, cloud workers from $50/month, and enterprise licensing."
|
||||
"OpenWork is free forever for solo use with bring-your-own-keys. Cloud workers from $50/month per seat, plus custom enterprise licensing with self-hosted deployment.",
|
||||
alternates: {
|
||||
canonical: "/pricing"
|
||||
},
|
||||
openGraph: {
|
||||
...baseOpenGraph,
|
||||
url: "https://openworklabs.com/pricing"
|
||||
}
|
||||
};
|
||||
|
||||
export default async function PricingPage() {
|
||||
@@ -16,6 +67,7 @@ export default async function PricingPage() {
|
||||
|
||||
return (
|
||||
<div className="relative min-h-screen overflow-hidden text-[#011627]">
|
||||
<StructuredData data={pricingSchema} />
|
||||
<LandingBackground />
|
||||
|
||||
<div className="relative z-10 flex min-h-screen flex-col items-center pb-3 pt-1 md:pb-4 md:pt-2">
|
||||
@@ -31,7 +83,7 @@ export default async function PricingPage() {
|
||||
<main className="mx-auto flex w-full max-w-6xl flex-col gap-16 px-6 pb-24 md:gap-20 md:px-8 md:pb-28">
|
||||
<section className="max-w-4xl pt-6 md:pt-10">
|
||||
<h1 className="mb-6 text-4xl font-medium leading-[1.05] tracking-tight md:text-5xl lg:text-6xl">
|
||||
Pricing
|
||||
OpenWork pricing — free, team, and enterprise
|
||||
</h1>
|
||||
</section>
|
||||
|
||||
|
||||
@@ -2,7 +2,10 @@ import { LegalPage } from "../../components/legal-page";
|
||||
|
||||
export const metadata = {
|
||||
title: "OpenWork — Privacy Policy",
|
||||
description: "Privacy policy for Different AI, doing business as OpenWork."
|
||||
description: "Privacy policy for Different AI, doing business as OpenWork.",
|
||||
alternates: {
|
||||
canonical: "/privacy"
|
||||
}
|
||||
};
|
||||
|
||||
export default function PrivacyPage() {
|
||||
|
||||
@@ -2,25 +2,23 @@ import type { MetadataRoute } from "next";
|
||||
|
||||
const BASE_URL = "https://openworklabs.com";
|
||||
|
||||
const paths = [
|
||||
"/",
|
||||
"/den",
|
||||
"/download",
|
||||
"/enterprise",
|
||||
"/pricing",
|
||||
"/feedback",
|
||||
"/privacy",
|
||||
"/terms",
|
||||
"/trust",
|
||||
"/docs",
|
||||
const paths: { path: string; priority: number }[] = [
|
||||
{ path: "/", priority: 1 },
|
||||
{ path: "/download", priority: 0.7 },
|
||||
{ path: "/enterprise", priority: 0.7 },
|
||||
{ path: "/pricing", priority: 0.7 },
|
||||
{ path: "/trust", priority: 0.7 },
|
||||
{ path: "/docs", priority: 0.7 },
|
||||
{ path: "/privacy", priority: 0.3 },
|
||||
{ path: "/terms", priority: 0.3 },
|
||||
];
|
||||
|
||||
export default function sitemap(): MetadataRoute.Sitemap {
|
||||
const lastModified = new Date();
|
||||
return paths.map((path) => ({
|
||||
return paths.map(({ path, priority }) => ({
|
||||
url: `${BASE_URL}${path}`,
|
||||
lastModified,
|
||||
changeFrequency: "weekly",
|
||||
priority: path === "/" ? 1 : 0.7,
|
||||
priority,
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -3,8 +3,15 @@ import { SiteNav } from "../../components/site-nav";
|
||||
import { getGithubData } from "../../lib/github";
|
||||
|
||||
export const metadata = {
|
||||
title: "OpenWork - Starter Success",
|
||||
title: "OpenWork — Starter Success",
|
||||
description: "Thanks for pre-ordering OpenWork Team Starter.",
|
||||
alternates: {
|
||||
canonical: "/starter-success"
|
||||
},
|
||||
robots: {
|
||||
index: false,
|
||||
follow: true
|
||||
}
|
||||
};
|
||||
|
||||
export default async function StarterSuccessPage() {
|
||||
|
||||
@@ -2,7 +2,10 @@ import { LegalPage } from "../../components/legal-page";
|
||||
|
||||
export const metadata = {
|
||||
title: "OpenWork — Terms of Use",
|
||||
description: "Terms of use for Different AI, doing business as OpenWork."
|
||||
description: "Terms of use for Different AI, doing business as OpenWork.",
|
||||
alternates: {
|
||||
canonical: "/terms"
|
||||
}
|
||||
};
|
||||
|
||||
export default function TermsPage() {
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
import { LandingTrustOverview } from "../../components/landing-trust";
|
||||
import { getGithubData } from "../../lib/github";
|
||||
import { baseOpenGraph } from "../../lib/seo";
|
||||
|
||||
export const metadata = {
|
||||
title: "OpenWork — Security & Data Privacy",
|
||||
description:
|
||||
"How OpenWork handles data, subprocessors, incident response, and compliance for self-hosted enterprise deployments."
|
||||
"How OpenWork handles data, subprocessors, incident response, and compliance for self-hosted enterprise deployments.",
|
||||
alternates: {
|
||||
canonical: "/trust"
|
||||
},
|
||||
openGraph: {
|
||||
...baseOpenGraph,
|
||||
url: "https://openworklabs.com/trust"
|
||||
}
|
||||
};
|
||||
|
||||
export default async function TrustPage() {
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
"use client";
|
||||
|
||||
type DenHeroProps = {
|
||||
getStartedHref: string;
|
||||
};
|
||||
|
||||
export function DenHero(props: DenHeroProps) {
|
||||
const getStartedExternal = /^https?:\/\//.test(props.getStartedHref);
|
||||
|
||||
return (
|
||||
<section className="pt-8 md:pt-14">
|
||||
<div className="max-w-[42rem]">
|
||||
<h2 className="max-w-[12.4ch] text-3xl font-medium tracking-tight text-gray-900 md:max-w-[12.1ch] md:text-[3.2rem] md:leading-[0.98] lg:max-w-none lg:text-[3.35rem] xl:text-[3.7rem]">
|
||||
<span className="block lg:whitespace-nowrap">Agents that never sleep</span>
|
||||
</h2>
|
||||
<p className="mt-5 max-w-[35rem] text-lg leading-relaxed text-gray-700 md:text-[1rem] md:leading-8 lg:text-[1.02rem]">
|
||||
Cloud gives you a personal cloud workspace for long-running tasks, background automation, and the same agent workflows you already use locally in OpenWork, without keeping your own machine awake.
|
||||
</p>
|
||||
|
||||
<div className="mt-8 flex flex-col items-start gap-4 sm:flex-row sm:items-center sm:gap-5">
|
||||
<a
|
||||
href={props.getStartedHref}
|
||||
className="doc-button min-w-[290px] justify-center px-8 text-[1.08rem] font-semibold"
|
||||
rel={getStartedExternal ? "noreferrer" : undefined}
|
||||
target={getStartedExternal ? "_blank" : undefined}
|
||||
>
|
||||
Get started
|
||||
</a>
|
||||
<div className="flex flex-col text-[0.98rem] text-gray-500 sm:max-w-[14rem]">
|
||||
<span className="font-semibold text-gray-700">$50/mo per worker</span>
|
||||
<span>Free for a limited time</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
import { LandingBackground } from "./landing-background";
|
||||
import { DenHero } from "./den-hero";
|
||||
import { SiteFooter } from "./site-footer";
|
||||
import { SiteNav } from "./site-nav";
|
||||
|
||||
type Props = {
|
||||
stars: string;
|
||||
downloadHref: string;
|
||||
getStartedHref: string;
|
||||
callHref: string;
|
||||
};
|
||||
|
||||
export function LandingDen(props: Props) {
|
||||
return (
|
||||
<div className="relative min-h-screen overflow-hidden text-[#011627]">
|
||||
<LandingBackground />
|
||||
|
||||
<div className="relative z-10 flex min-h-screen flex-col items-center pb-3 pt-1 md:pb-4 md:pt-2">
|
||||
<div className="w-full">
|
||||
<SiteNav
|
||||
stars={props.stars}
|
||||
callUrl={props.callHref}
|
||||
downloadHref={props.downloadHref}
|
||||
active="den"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mx-auto flex w-full max-w-6xl flex-col gap-14 px-6 pb-24 md:px-8 md:pb-28">
|
||||
<DenHero getStartedHref={props.getStartedHref} />
|
||||
|
||||
<SiteFooter />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -78,8 +78,8 @@ export function LandingEnterprise(props: Props) {
|
||||
|
||||
<main className="mx-auto flex w-full max-w-5xl flex-col gap-16 px-6 pb-24 md:gap-20 md:px-8 md:pb-28">
|
||||
<section className="max-w-4xl">
|
||||
<h1 className="mb-6 text-4xl font-medium leading-[1.05] tracking-tight md:text-5xl lg:text-6xl">
|
||||
The Open Source alternative to Claude Cowork.
|
||||
<h1 className="mb-5 text-4xl font-medium leading-[1.1] tracking-tight md:text-5xl lg:text-6xl">
|
||||
A privacy-first alternative to Claude Cowork<br />for your organization
|
||||
</h1>
|
||||
|
||||
<p className="max-w-3xl text-lg leading-relaxed text-slate-600 md:text-xl">
|
||||
@@ -97,6 +97,9 @@ export function LandingEnterprise(props: Props) {
|
||||
>
|
||||
Book a call
|
||||
</a>
|
||||
<a href="/trust" className="secondary-button">
|
||||
Security Review
|
||||
</a>
|
||||
|
||||
<div className="flex items-center gap-2 opacity-80 sm:ml-4">
|
||||
<span className="text-[13px] font-medium text-gray-500">
|
||||
@@ -112,12 +115,6 @@ export function LandingEnterprise(props: Props) {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-4">
|
||||
<a href="/trust" className="text-[14px] font-medium text-[#011627] transition-colors hover:text-slate-700">
|
||||
Trust details
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="space-y-6">
|
||||
|
||||
@@ -294,7 +294,7 @@ export function LandingHome(props: Props) {
|
||||
|
||||
<div
|
||||
className="relative flex min-h-[400px] w-full items-center justify-center overflow-hidden rounded-3xl border border-gray-100 bg-cover bg-center p-6 lg:w-2/3 md:p-10"
|
||||
style={{ backgroundImage: "url('/enterprise-showcase-bg.png')" }}
|
||||
style={{ backgroundImage: "url('/enterprise-showcase-bg.jpg')" }}
|
||||
>
|
||||
{showEnterpriseShowcase ? (
|
||||
<div className="grid w-full [&>*]:col-start-1 [&>*]:row-start-1">
|
||||
|
||||
@@ -46,7 +46,7 @@ function PricingCardView({ card }: { card: PricingCard }) {
|
||||
<div className="relative z-10 flex flex-col h-full min-h-[160px] justify-between">
|
||||
<div>
|
||||
<div className="flex justify-between items-start mb-6">
|
||||
<h3 className="text-[17px] font-medium tracking-tight">{card.title}</h3>
|
||||
<h2 className="text-[17px] font-medium tracking-tight">{card.title}</h2>
|
||||
</div>
|
||||
|
||||
{card.isCustomPricing ? (
|
||||
|
||||
@@ -27,9 +27,14 @@ export function SiteFooter() {
|
||||
<Link href="/download" className="transition-colors hover:text-gray-800">
|
||||
Desktop
|
||||
</Link>
|
||||
<Link href="/den" className="transition-colors hover:text-gray-800">
|
||||
<a
|
||||
href="https://app.openworklabs.com"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="transition-colors hover:text-gray-800"
|
||||
>
|
||||
Cloud
|
||||
</Link>
|
||||
</a>
|
||||
<Link href="/enterprise" className="transition-colors hover:text-gray-800">
|
||||
Enterprise
|
||||
</Link>
|
||||
|
||||
@@ -11,7 +11,7 @@ type Props = {
|
||||
downloadHref?: string;
|
||||
mobilePrimaryHref?: string;
|
||||
mobilePrimaryLabel?: string;
|
||||
active?: "home" | "pricing" | "download" | "enterprise" | "den" | "docs";
|
||||
active?: "home" | "pricing" | "download" | "enterprise" | "cloud" | "docs";
|
||||
};
|
||||
|
||||
export function SiteNav(props: Props) {
|
||||
@@ -32,13 +32,16 @@ export function SiteNav(props: Props) {
|
||||
const callExternal = /^https?:\/\//.test(callHref);
|
||||
const mobilePrimaryExternal = /^https?:\/\//.test(mobilePrimaryHref);
|
||||
const navItems = [
|
||||
{ href: "/docs", label: "Docs", key: "docs" },
|
||||
{ href: "/docs", label: "Docs", key: "docs", newTab: true },
|
||||
{ href: "/pricing", label: "Pricing", key: "pricing" },
|
||||
{ href: "/download", label: "Desktop", key: "download" },
|
||||
{ href: "https://app.openworklabs.com", label: "Cloud", key: "den" },
|
||||
{ href: "https://app.openworklabs.com", label: "Cloud", key: "cloud" },
|
||||
{ href: "/enterprise", label: "Enterprise", key: "enterprise" }
|
||||
] as const;
|
||||
|
||||
const opensInNewTab = (item: (typeof navItems)[number]) =>
|
||||
("newTab" in item && item.newTab) || /^(?:https?:\/\/)/.test(item.href);
|
||||
|
||||
const navLink = (isActive: boolean) =>
|
||||
isActive
|
||||
? "text-[#011627]"
|
||||
@@ -64,7 +67,7 @@ export function SiteNav(props: Props) {
|
||||
<Link
|
||||
key={item.key}
|
||||
href={item.href}
|
||||
{...(/^(?:https?:\/\/)/.test(item.href) ? { target: "_blank", rel: "noreferrer" } : {})}
|
||||
{...(opensInNewTab(item) ? { target: "_blank", rel: "noreferrer" } : {})}
|
||||
className={navLink(props.active === item.key)}
|
||||
>
|
||||
{item.label}
|
||||
@@ -117,7 +120,7 @@ export function SiteNav(props: Props) {
|
||||
<Link
|
||||
key={item.key}
|
||||
href={item.href}
|
||||
{...(/^(?:https?:\/\/)/.test(item.href) ? { target: "_blank", rel: "noreferrer" } : {})}
|
||||
{...(opensInNewTab(item) ? { target: "_blank", rel: "noreferrer" } : {})}
|
||||
className={`rounded-2xl px-4 py-3 ${navLink(
|
||||
props.active === item.key
|
||||
)}`}
|
||||
|
||||
12
ee/apps/landing/components/structured-data.tsx
Normal file
12
ee/apps/landing/components/structured-data.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
type StructuredDataProps = {
|
||||
data: Record<string, unknown> | Array<Record<string, unknown>>;
|
||||
};
|
||||
|
||||
export function StructuredData({ data }: StructuredDataProps) {
|
||||
return (
|
||||
<script
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(data) }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -18,7 +18,7 @@ const destinations: Record<string, string> = {
|
||||
download: "/download",
|
||||
pricing: "/pricing",
|
||||
enterprise: "/enterprise",
|
||||
den: "/den",
|
||||
cloud: "https://app.openworklabs.com",
|
||||
docs: "/docs",
|
||||
trust: "/trust",
|
||||
feedback: "/feedback",
|
||||
@@ -91,7 +91,7 @@ const tools: Tool[] = [
|
||||
{
|
||||
name: "navigate_to",
|
||||
description:
|
||||
"Navigate the current tab to a key section of openworklabs.com. Use this when the user expresses intent to view pricing, download, enterprise, den (cloud), docs, trust, or feedback.",
|
||||
"Navigate the current tab to a key section of openworklabs.com. Use this when the user expresses intent to view pricing, download, enterprise, cloud, docs, trust, or feedback.",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
|
||||
@@ -14,9 +14,9 @@ const home = `# OpenWork
|
||||
|
||||
- **Try it free** — [Download the desktop app](https://openworklabs.com/download)
|
||||
- **Hosted cloud workers** — [Pricing](https://openworklabs.com/pricing) (\\$50/mo per worker)
|
||||
- **Sign in to the hosted workspace** — [Cloud](https://app.openworklabs.com)
|
||||
- **SSO / audit / procurement** — [Enterprise](https://openworklabs.com/enterprise)
|
||||
- **Docs** — [openworklabs.com/docs](https://openworklabs.com/docs)
|
||||
- **Den (team workspace)** — [openworklabs.com/den](https://openworklabs.com/den)
|
||||
|
||||
## For agents
|
||||
|
||||
@@ -28,7 +28,7 @@ const home = `# OpenWork
|
||||
Backed by Y Combinator.
|
||||
`
|
||||
|
||||
const pricing = `# Pricing
|
||||
const pricing = `# OpenWork pricing — free, team, and enterprise
|
||||
|
||||
> OpenWork has three tiers: free open-source desktop, \\$50/mo Team Starter, and custom Enterprise.
|
||||
|
||||
@@ -59,9 +59,9 @@ const pricing = `# Pricing
|
||||
Prices exclude taxes.
|
||||
`
|
||||
|
||||
const enterprise = `# OpenWork for Enterprise
|
||||
const enterprise = `# A privacy-first alternative to Claude Cowork for your organization
|
||||
|
||||
> Secure hosting for safe, permissioned AI employees. SSO, audit, custom deployment, and procurement support.
|
||||
> The open-source Claude Cowork alternative — self-hosted, permissioned, and compliance-ready. SSO, audit, custom deployment, and procurement support.
|
||||
|
||||
## What Enterprise includes
|
||||
|
||||
@@ -79,32 +79,10 @@ const enterprise = `# OpenWork for Enterprise
|
||||
## Next step
|
||||
|
||||
- [Book a call](https://openworklabs.com/enterprise#book)
|
||||
- See [Trust & security](https://openworklabs.com/trust) for data handling, subprocessors, and incident SLA
|
||||
- [Security Review](https://openworklabs.com/trust) — data handling, subprocessors, and incident SLA
|
||||
- See [Pricing](https://openworklabs.com/pricing) for tier comparison
|
||||
`
|
||||
|
||||
const den = `# Den — Agents that never sleep
|
||||
|
||||
> Cloud workspace for long-running tasks, background automation, and the same agent workflows you use locally in OpenWork, without keeping your own machine awake.
|
||||
|
||||
## What Den is
|
||||
|
||||
- Personal cloud workspace for OpenWork agents
|
||||
- Runs long-running and scheduled tasks without a local machine
|
||||
- Same skills, plugins, and MCP servers as the desktop app
|
||||
- \\$50/mo per worker — free for a limited time
|
||||
|
||||
## Get started
|
||||
|
||||
- CTA: [Start a cloud worker](https://app.openworklabs.com/checkout)
|
||||
- Free for a limited time
|
||||
|
||||
## Related
|
||||
|
||||
- [Pricing](https://openworklabs.com/pricing)
|
||||
- [Docs](https://openworklabs.com/docs)
|
||||
`
|
||||
|
||||
const download = `# Download OpenWork
|
||||
|
||||
> Desktop app for macOS, Windows, and Linux. Latest release published on GitHub.
|
||||
@@ -171,7 +149,6 @@ export const agentMarkdown: Record<string, string> = {
|
||||
"/": home,
|
||||
"/pricing": pricing,
|
||||
"/enterprise": enterprise,
|
||||
"/den": den,
|
||||
"/download": download,
|
||||
"/trust": trust,
|
||||
}
|
||||
|
||||
6
ee/apps/landing/lib/seo.ts
Normal file
6
ee/apps/landing/lib/seo.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export const baseOpenGraph = {
|
||||
type: "website" as const,
|
||||
siteName: "OpenWork",
|
||||
locale: "en_US",
|
||||
images: ["/og-image-clean.png"]
|
||||
};
|
||||
@@ -2,7 +2,7 @@ import { NextResponse, type NextRequest } from "next/server"
|
||||
import { agentMarkdown } from "./lib/agent-markdown"
|
||||
|
||||
export const config = {
|
||||
matcher: ["/", "/pricing", "/enterprise", "/den", "/download", "/trust"],
|
||||
matcher: ["/", "/pricing", "/enterprise", "/download", "/trust"],
|
||||
}
|
||||
|
||||
export function middleware(request: NextRequest) {
|
||||
|
||||
BIN
ee/apps/landing/public/enterprise-showcase-bg.jpg
Normal file
BIN
ee/apps/landing/public/enterprise-showcase-bg.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
@@ -19,7 +19,7 @@ Agents: use these routes when a user expresses one of these intents.
|
||||
- Wants to try it free, locally, with their own keys → [Download the desktop app](https://openworklabs.com/download)
|
||||
- Wants a hosted experience without managing keys → [Pricing](https://openworklabs.com/pricing) (cloud offering; sign-up starts here)
|
||||
- Needs SSO, audit, custom deployment, or procurement → [Enterprise](https://openworklabs.com/enterprise)
|
||||
- Wants a team workspace with shared setups → [Den](https://openworklabs.com/den)
|
||||
- Wants a hosted team workspace with shared setups → [Cloud](https://app.openworklabs.com)
|
||||
- Wants to read the manual → [Docs](https://openworklabs.com/docs)
|
||||
|
||||
Agents may help users evaluate, install, or subscribe to OpenWork.
|
||||
@@ -46,7 +46,7 @@ The cloud offering (see [Pricing](https://openworklabs.com/pricing)) adds manage
|
||||
- [Download](https://openworklabs.com/download)
|
||||
- [Pricing](https://openworklabs.com/pricing)
|
||||
- [Enterprise](https://openworklabs.com/enterprise)
|
||||
- [Den](https://openworklabs.com/den)
|
||||
- [Cloud](https://app.openworklabs.com)
|
||||
- [Docs](https://openworklabs.com/docs)
|
||||
- [Feedback](https://openworklabs.com/feedback)
|
||||
- [Privacy](https://openworklabs.com/privacy)
|
||||
|
||||
Reference in New Issue
Block a user