507 lines · 18.8 KB · Text
RawDownload
---import Layout from "../layouts/Layout.astro";import { AnimatedBeamDemo } from "../components/animated-beam-demo";import { SelfHostContactModal } from "../components/self-host-contact-modal";--- <Layout>  <main class="bg-soft min-h-dvh relative antialiased">    {/* Pattern + fade — span the top 100vh, behind everything */}    <div      class="pattern-bg pattern-graph"      style="position: absolute; top: 0; left: 0; right: 0; height: 100vh; z-index: 1; pointer-events: none;"    >    </div>    <div      style="position: absolute; top: 0; left: 0; right: 0; height: 100vh; z-index: 2; pointer-events: none; background: linear-gradient(180deg, transparent 0%, transparent 65%, var(--color-surface) 100%);"    >    </div>     <div class="relative z-10">      {/* NAV */}      <nav>        <div          class="max-w-[1200px] mx-auto px-6 sm:px-10 h-14 flex items-center justify-between"        >          <a href="/" class="flex items-center gap-2.5">            <img src="/favicon-192.png" alt="" class="w-5 h-5" />            <span class="text-[15px] font-semibold tracking-[-0.01em] text-ink"              >executor</span            >          </a>          <div class="flex items-center gap-5">            <a              href="https://github.com/RhysSullivan/executor"              class="text-[14px] font-medium text-ink-2 hover:text-ink transition-colors"              >GitHub</a            >            <a href="/cloud" class="btn-primary">Try Cloud →</a>          </div>        </div>      </nav>       {/* HERO — centered, tight */}      <section class="relative">        <div class="relative z-10 px-6 sm:px-10 pt-28 pb-32 sm:pt-36 sm:pb-44">          <div class="max-w-[920px] mx-auto text-center">            <h1              class="rise-1 text-[clamp(2.8rem,7.2vw,6rem)] font-semibold tracking-[-0.025em] leading-[1.02] text-ink mb-12 mx-auto max-w-[18ch] text-balance"            >              Connect any agent to <span class="serif-italic">everything</span>.            </h1>            <div class="rise-2 max-w-[760px] mx-auto mb-12">              <div class="surface-card p-6 sm:p-7">                <AnimatedBeamDemo client:load variant="stripe" />              </div>            </div>            <p              class="rise-3 text-[18px] leading-[1.55] text-ink-2 mx-auto max-w-[44ch] mb-9 text-pretty"            >              One gateway. Every tool. Every team.            </p>             {/* ── Toggle: Try in Cloud / Try locally ── */}            <div              class="rise-4 hero-toggle flex flex-col items-center gap-7"              data-active="cloud"            >              <div                class="tab-toggle"                data-active="cloud"                role="tablist"                aria-label="Choose how to try Executor"              >                <span class="tab-indicator" aria-hidden="true"></span>                <button                  type="button"                  role="tab"                  aria-selected="true"                  data-tab="cloud"                >                  Cloud                </button>                <button                  type="button"                  role="tab"                  aria-selected="false"                  data-tab="local"                >                  Local                </button>              </div>               {/* Cloud pane */}              <div                class="pane flex-col items-center gap-4"                data-pane="cloud"                role="tabpanel"                aria-labelledby="tab-cloud"              >                <p class="pane-desc">                  Built for teams. Plug into any agent via MCP.                </p>                <a href="/cloud" class="btn-primary btn-primary--hero"                  >Try Cloud →</a                >              </div>               {/* Local pane — auto-detected download */}              <div                class="pane flex-col items-center gap-4"                data-pane="local"                role="tabpanel"                aria-labelledby="tab-local"                data-download              >                <p class="pane-desc">                  Everything runs on your machine locally.                </p>                <div class="flex flex-wrap items-center justify-center gap-3">                  <a                    data-os="mac-arm64"                    href="https://github.com/RhysSullivan/executor/releases/latest/download/executor-desktop-mac-arm64.dmg"                    class="btn-primary btn-primary--hero"                  >                    Download for Mac (Apple Silicon)                  </a>                  <a                    data-os="mac-x64"                    href="https://github.com/RhysSullivan/executor/releases/latest/download/executor-desktop-mac-x64.dmg"                    class="btn-primary btn-primary--hero"                  >                    Download for Mac (Intel)                  </a>                  <a                    data-os="win-x64"                    href="https://github.com/RhysSullivan/executor/releases/latest/download/executor-desktop-win-x64.exe"                    class="btn-primary btn-primary--hero"                  >                    Download for Windows                  </a>                  <a                    data-os="linux-x64"                    href="https://github.com/RhysSullivan/executor/releases/latest/download/executor-desktop-linux-x86_64.AppImage"                    class="btn-primary btn-primary--hero"                  >                    Download for Linux                  </a>                   <details                    class="download-other hidden"                    data-download-other                  >                    <summary>Other platforms ▾</summary>                    <div                      class="download-other-list"                      data-download-other-list                    >                    </div>                  </details>                </div>              </div>            </div>          </div>        </div>      </section>       {/* ─── FOUNDER NOTE ─── */}      <section class="py-20 sm:py-28 border-t border-rule">        <div class="max-w-[680px] mx-auto px-6 sm:px-10">          <h2            class="text-[clamp(2rem,3.6vw,2.6rem)] font-semibold tracking-[-0.02em] leading-[1.08] text-ink mb-10 text-balance"          >            What is <span class="serif-italic">Executor</span>?          </h2>           <div            class="space-y-5 text-[16.5px] leading-[1.65] text-ink-2 text-pretty"          >            <p>              Executor is a tool/source discovery and execution layer for              agent-callable tools.            </p>            <p>              Executor doesn't care what you add. MCP, OpenAPI, GraphQL, custom              sources, under the hood they all become a tool name, an input              schema, and an output schema. Once they're in that shape, you can              call them however you want. Today that's implemented as a              code-mode MCP, but it could just as well be the Executor CLI, or a              native client you drop in.            </p>            <p>              Because every tool looks the same, your agent can call them from              gen-UI dashboards it writes on the fly, call them as one off              scripts in a chat, or use them in reusable workflows.            </p>             <h3              class="text-[17px] font-semibold tracking-[-0.01em] text-ink pt-5"            >              Teams            </h3>            <p>              Set your sources up once and your whole team has them. It supports              per-user credentials and shared ones. No more onboarding ritual,              no more toggling MCPs on and off mid-task. Your agents should be              able to reach your company's resources in a way that isn't scary.            </p>             <h3              class="text-[17px] font-semibold tracking-[-0.01em] text-ink pt-5"            >              Sandboxing            </h3>            <p>              Tool calls run in a JavaScript sandbox. The agent has access to              secrets.            </p>             <h3              class="text-[17px] font-semibold tracking-[-0.01em] text-ink pt-5"            >              Destructive actions            </h3>            <p>              Executor preserves the semantics of whatever it imported from: GET              vs DELETE for OpenAPI, <code                class="font-mono text-[14px] text-ink bg-surface-2 border border-rule px-1.5 py-0.5 rounded"                >destructiveHint</code              > for MCP, mutations for GraphQL. That tells the agent what it can auto-run              and what should pull you back into the loop, so it can work autonomously              without taking you out of it.            </p>             <p class="pt-5">              It's all open source, built on the same SDK we publish to npm.              There's two ways to try it, either the local version via the              `executor` cli, or the cloud version.            </p>             <p class="text-ink-3 text-[14.5px] pt-3">— Rhys</p>          </div>        </div>      </section>       {/* ─── GET STARTED ─── */}      <section id="install" class="py-24 sm:py-32 border-t border-rule">        <div class="max-w-[1200px] mx-auto px-6 sm:px-10">          <div class="mb-14 mx-auto text-center max-w-2xl">            <div              class="font-mono text-[12px] uppercase tracking-[0.18em] text-ink-3 mb-4"            >              Get started            </div>            <h2              class="text-[clamp(2.2rem,4.5vw,3.4rem)] font-semibold tracking-[-0.02em] leading-[1.05] text-ink mx-auto max-w-[20ch] text-balance"            >              Pick your <span class="serif-italic">path</span>.            </h2>          </div>           <div            class="grid md:grid-cols-2 gap-6 max-w-[760px] mx-auto"          >            {/* Cloud */}            <div              class="surface-card p-8 flex flex-col"              style="border-color: rgba(99,91,255,0.25)"            >              <div class="flex items-center gap-2 mb-4">                <span                  class="font-mono text-[11px] uppercase tracking-[0.18em]"                  style="color: var(--color-accent)">Recommended</span                >              </div>              <h3                class="text-[22px] font-semibold tracking-[-0.01em] text-ink mb-3"              >                Cloud              </h3>              <p                class="text-[14.5px] leading-[1.55] text-ink-2 mb-3 text-pretty"              >                Hosted Executor. Auth, sync, policies, and the whole team                online in five minutes. Free tier to start.              </p>              <p class="mb-6 text-[13.5px] leading-[1.55] text-ink-3 flex-1">                <SelfHostContactModal client:load variant="inline" />              </p>              <a href="/cloud" class="btn-primary self-start">Try Cloud →</a>            </div>             {/* Desktop */}            <div class="surface-card p-8 flex flex-col">              <div class="flex items-center gap-2 mb-4">                <span                  class="font-mono text-[11px] uppercase tracking-[0.18em] text-ink-3"                  >Open source</span                >              </div>              <h3                class="text-[22px] font-semibold tracking-[-0.01em] text-ink mb-3"              >                Desktop              </h3>              <p                class="text-[14.5px] leading-[1.55] text-ink-2 mb-6 text-pretty flex-1"              >                A native app for Mac, Windows, and Linux. Everything stays                on your machine — sources, secrets, sessions. MIT licensed.              </p>              <button                type="button"                data-jump-to-local                class="btn-primary self-start"              >                Download →              </button>            </div>          </div>        </div>      </section>       {/* ─── FOOTER ─── */}      <footer class="border-t border-rule py-10">        <div          class="max-w-[1200px] mx-auto px-6 sm:px-10 flex flex-col sm:flex-row items-start sm:items-center justify-between gap-6"        >          <div class="flex items-center gap-2.5">            <img src="/favicon-192.png" alt="" class="w-4 h-4 opacity-80" />            <span class="text-[13px] font-medium text-ink-2">executor</span>            <span class="text-[12px] text-ink-3 ml-2"              >© {new Date().getFullYear()}</span            >          </div>          <div class="flex items-center gap-6 text-[13px] text-ink-2">            <a              href="https://github.com/RhysSullivan/executor"              class="hover:text-ink transition-colors">GitHub</a            >            <a href="/privacy" class="hover:text-ink transition-colors"              >Privacy</a            >            <a href="/terms" class="hover:text-ink transition-colors">Terms</a>          </div>        </div>      </footer>    </div>  </main></Layout> <script>  document    .querySelectorAll<HTMLButtonElement>("button[data-copy]")    .forEach((btn) => {      let timer: ReturnType<typeof setTimeout> | null = null;       btn.addEventListener("click", async () => {        const text = btn.dataset.copy ?? "";        try {          await navigator.clipboard.writeText(text);          btn.dataset.copied = "true";        } catch {          btn.dataset.copied = "false";        }        if (timer) clearTimeout(timer);        timer = setTimeout(() => {          delete btn.dataset.copied;        }, 1500);      });    });</script> <script>  // ── Hero tab toggle: Try in Cloud / Try locally ────────────────────  // Drives the sliding pill indicator + pane visibility via a single  // `data-active` attribute on the wrapper. Default "cloud" so no-JS  // visitors still see the recommended path.  const heroToggle = document.querySelector<HTMLElement>(".hero-toggle");  const tabToggle = heroToggle?.querySelector<HTMLElement>(".tab-toggle");  const TAB_PARAM = "try";  const isHeroTab = (v: unknown): v is "cloud" | "local" =>    v === "cloud" || v === "local";  const syncTabToUrl = (tab: "cloud" | "local") => {    const url = new URL(window.location.href);    if (url.searchParams.get(TAB_PARAM) === tab) return;    url.searchParams.set(TAB_PARAM, tab);    window.history.replaceState(window.history.state, "", url);  };  const setActiveTab = (tab: "cloud" | "local", updateUrl = true) => {    if (!heroToggle || !tabToggle) return;    heroToggle.dataset.active = tab;    tabToggle.dataset.active = tab;    tabToggle.querySelectorAll<HTMLButtonElement>("button[data-tab]").forEach(      (btn) => {        btn.setAttribute(          "aria-selected",          btn.dataset.tab === tab ? "true" : "false",        );      },    );    if (updateUrl) syncTabToUrl(tab);  };  if (heroToggle && tabToggle) {    const initial = new URLSearchParams(window.location.search).get(TAB_PARAM);    if (isHeroTab(initial)) setActiveTab(initial, false);    tabToggle.querySelectorAll<HTMLButtonElement>("button[data-tab]").forEach(      (btn) => {        btn.addEventListener("click", () => {          const tab = btn.dataset.tab as "cloud" | "local" | undefined;          if (tab) setActiveTab(tab);        });      },    );    window.addEventListener("popstate", () => {      const next = new URLSearchParams(window.location.search).get(TAB_PARAM);      setActiveTab(isHeroTab(next) ? next : "cloud", false);    });  }   // Desktop card "Download →" CTA jumps to the hero, switches to Local,  // and lets the auto-detect script there pick the right OS button.  document    .querySelectorAll<HTMLButtonElement>("button[data-jump-to-local]")    .forEach((btn) => {      btn.addEventListener("click", () => {        setActiveTab("local");        heroToggle?.scrollIntoView({ behavior: "smooth", block: "center" });      });    });   // ── Auto-detect download platform ──────────────────────────────────  // Shows ONE primary button matching the visitor's OS in the "Try  // locally" pane; tucks the others behind an "Other platforms"  // popover. No-JS / unrecognised UA: all four buttons visible (still  // functional, just less polished).  type DownloadOs = "mac-arm64" | "mac-x64" | "win-x64" | "linux-x64";   type UserAgentData = {    readonly platform: string;    readonly getHighEntropyValues?: (      hints: ReadonlyArray<string>,    ) => Promise<{ readonly architecture?: string; readonly bitness?: string }>;  };   const detectOs = async (): Promise<DownloadOs | null> => {    const navAny = navigator as Navigator & {      readonly userAgentData?: UserAgentData;    };    const uad = navAny.userAgentData;    const platformRaw =      uad?.platform ?? navigator.platform ?? navigator.userAgent ?? "";    const platform = platformRaw.toLowerCase();    const ua = navigator.userAgent.toLowerCase();     if (platform.includes("win") || ua.includes("windows")) return "win-x64";    if (platform.includes("linux") || ua.includes("linux")) return "linux-x64";    if (platform.includes("mac") || ua.includes("mac")) {      // Apple Silicon vs Intel: ask client hints first, then fall back to      // a heuristic. Most modern Macs are arm64.      if (uad?.getHighEntropyValues) {        try {          const hints = await uad.getHighEntropyValues([            "architecture",            "bitness",          ]);          if (hints.architecture === "arm") return "mac-arm64";          if (hints.architecture === "x86") return "mac-x64";        } catch {}      }      return "mac-arm64";    }    return null;  };   void (async () => {    const root = document.querySelector<HTMLElement>("[data-download]");    if (!root) return;    const buttons = Array.from(      root.querySelectorAll<HTMLAnchorElement>("a[data-os]"),    );    const other = root.querySelector<HTMLDetailsElement>(      "[data-download-other]",    );    const otherList = root.querySelector<HTMLDivElement>(      "[data-download-other-list]",    );    if (buttons.length === 0 || !other || !otherList) return;     const detected = await detectOs();    if (!detected) return;     for (const btn of buttons) {      const os = btn.dataset.os as DownloadOs | undefined;      if (os && os !== detected) {        btn.classList.add("hidden");        const clone = btn.cloneNode(true) as HTMLAnchorElement;        clone.classList.remove(          "btn-primary",          "btn-primary--hero",          "hidden",

Lines 1–500 of 507.