diff --git a/apps/cloud/src/edge/marketing.test.ts b/apps/cloud/src/edge/marketing.test.ts index 6e788bf2ac..b6af946dda 100644 --- a/apps/cloud/src/edge/marketing.test.ts +++ b/apps/cloud/src/edge/marketing.test.ts @@ -20,6 +20,9 @@ describe("isMarketingPath", () => { "/blog/", "/blog/some-post", "/llms.txt", + "/index.md", + "/setup-prompt.md", + "/pricing.md", "/og-image.png", "/_astro/app.css", // The blog author card loads its avatar from marketing's public/authors; diff --git a/apps/cloud/src/edge/marketing.ts b/apps/cloud/src/edge/marketing.ts index 5199f0cb41..3e0fa5493d 100644 --- a/apps/cloud/src/edge/marketing.ts +++ b/apps/cloud/src/edge/marketing.ts @@ -20,6 +20,9 @@ const MARKETING_PATHS = [ "/google-workspace", "/blog", "/llms.txt", + "/index.md", + "/setup-prompt.md", + "/pricing.md", "/api/detect", "/_astro", "/authors", diff --git a/apps/docs/docs.json b/apps/docs/docs.json index 2b47bbae22..f05ef3ec2f 100644 --- a/apps/docs/docs.json +++ b/apps/docs/docs.json @@ -33,7 +33,7 @@ }, { "group": "Hosted", - "pages": ["hosted/cloud", "hosted/docker", "hosted/cloudflare", "hosted/tracing"] + "pages": ["hosted/cloud", "hosted/docker", "hosted/tracing"] }, { "group": "Concepts", diff --git a/apps/docs/hosted/cloudflare.mdx b/apps/docs/hosted/cloudflare.mdx deleted file mode 100644 index d463ec6427..0000000000 --- a/apps/docs/hosted/cloudflare.mdx +++ /dev/null @@ -1,151 +0,0 @@ ---- -title: Self Hosted Cloudflare -description: "Run Executor as a single Cloudflare Worker in your own account, with Cloudflare Access for auth and D1 for storage." ---- - -Executor runs as a single Cloudflare Worker. Authentication is handled entirely by -[Cloudflare Access](https://developers.cloudflare.com/cloudflare-one/policies/access/) -(there is no separate app login), storage is [D1](https://developers.cloudflare.com/d1/), -tool code runs in QuickJS inside the Worker, and the web console, API, and MCP -endpoint are all served from that one Worker. - -It is single-tenant: every Access-verified user belongs to the one configured org, -and you manage members and credentials in Cloudflare Access rather than in the app. - -## Prerequisites - -- A Cloudflare account with Workers and Zero Trust (Access) enabled. -- The [Executor repo](https://github.com/UsefulSoftwareCo/executor) checked out, with - dependencies installed (`bun install`). - -## Deploy - -From `apps/host-cloudflare`: - -```bash -bunx wrangler login -bun run deploy:setup -``` - -`deploy:setup` is idempotent: it creates or reuses the `executor` D1 database, -writes its id into `wrangler.jsonc`, generates and uploads the `EXECUTOR_SECRET_KEY` -secret, and deploys the Worker. It then prints the one remaining manual step. - -## Put it behind Cloudflare Access - -Until the Worker is behind an Access application, every API and MCP route returns -`401`. In the Zero Trust dashboard: - -1. Go to **Access → Applications → Add an application → Self-hosted**. -2. Set the application domain to `executor-cloudflare..workers.dev`. -3. Add an Access policy (for example, emails ending in `@yourcompany.com`). -4. Copy the application **Audience (AUD)** tag, then redeploy with your Access settings: - - ```bash - bunx wrangler deploy \ - --var ACCESS_AUD: \ - --var ACCESS_TEAM_DOMAIN:.cloudflareaccess.com - ``` - - You can also set `ACCESS_AUD` and `ACCESS_TEAM_DOMAIN` in `wrangler.jsonc` and - redeploy. - -Now visiting the Worker prompts a Cloudflare Access login, and the Worker validates -the issued JWT on every request. - -## Connect an agent - -The Worker serves a streamable-HTTP MCP endpoint at `/mcp`, gated by Access like the -rest of the surfaces. Point your client at -`https://executor-cloudflare..workers.dev/mcp`. - -Because Access sits in front of the Worker, an MCP client that reaches `/mcp` -without a valid Access credential is served Cloudflare's HTML login page instead of -the endpoint. The streamable-HTTP client rejects that with -`Streamable HTTP error: Unexpected content type: text/html`: that is the Access login -page, not a Worker response. A browser passes Access via an interactive login, but an -MCP client cannot follow that redirect, so it has to authenticate another way. Choose -the path that matches your client. - -### For interactive clients: Managed OAuth (recommended) - -Cloudflare Access can run the MCP OAuth flow on the Worker's behalf, so a client -like Claude authenticates with a normal login popup and no manual headers. The Worker -keeps doing exactly what it already does (validate the `Cf-Access-Jwt-Assertion`); no -code or redeploy is needed. - -1. Open the self-hosted Access application gating the Worker (the one from the - previous section). -2. Enable **Managed OAuth** in the application's settings (the option that lets - non-browser clients authenticate over OAuth). With it on, Access answers an - unauthenticated `/mcp` request with an OAuth `401` challenge and serves the OAuth - discovery, authorize, and token endpoints itself, instead of the HTML login page. - Keep the Access application scoped to the whole Worker hostname (as set in the - previous section), not a single path like `/mcp`: the discovery probes clients - send to `/.well-known/oauth-authorization-server` and - `/.well-known/oauth-protected-resource` then land on Access too, which answers - them rather than letting them fall through to the Worker's SPA fallback (which the - Worker itself does not serve, so a path-scoped application would return HTML or - `404` there). -3. Point your client at the `/mcp` URL with no extra configuration. On connect it - discovers the OAuth endpoints, opens a browser popup to log in through Access, and - Cloudflare injects the validated JWT into every subsequent request. - - ```bash - npx add-mcp https://executor-cloudflare..workers.dev/mcp \ - --transport http --name executor - ``` - -This is the same flow Cloudflare documents in -[Secure MCP servers with Access](https://developers.cloudflare.com/cloudflare-one/access-controls/ai-controls/secure-mcp-servers/). - -If a client still won't start the OAuth flow after you enable Managed OAuth (it -classifies `/mcp` as not an MCP endpoint, or you still hit the -`Unexpected content type: text/html` error), its probe isn't consuming Access's -`401` challenge. Fall back to the service-token path below: it authenticates with -request headers and needs no client-side OAuth discovery, so it works regardless of -how the client handles the challenge. - -### For headless clients: service token - -When no human is present to complete the OAuth popup (CI, a background agent), -authenticate machine-to-machine with an Access service token instead. - -1. In the Zero Trust dashboard, go to **Access → Service Auth → Service Tokens → - Create Service Token**. Copy the **Client ID** and **Client Secret** (the secret - is shown once). -2. On the Access application gating the Worker, add a policy with **Action: Service - Auth** that includes that service token (Include → Service Token → your token). - The `Service Auth` action lets the token through without an interactive IdP login. -3. Configure your MCP client to send the token on every request as the - `CF-Access-Client-Id` and `CF-Access-Client-Secret` headers. Access exchanges them - for the `Cf-Access-Jwt-Assertion` JWT (carrying the token's `common_name`) that - the Worker validates, exactly like a human login. - - With [`add-mcp`](https://www.npmjs.com/package/add-mcp), which writes the config - for whichever agents it detects: - - ```bash - npx add-mcp https://executor-cloudflare..workers.dev/mcp \ - --transport http --name executor \ - --header "CF-Access-Client-Id: .access" \ - --header "CF-Access-Client-Secret: " - ``` - - For an `mcpServers` config block: - - ```json - { - "executor": { - "type": "http", - "url": "https://executor-cloudflare..workers.dev/mcp", - "headers": { - "CF-Access-Client-Id": ".access", - "CF-Access-Client-Secret": "" - } - } - } - ``` - -Once connected, see [MCP Proxy](/mcp-proxy) for how the endpoint exposes your -integrations. diff --git a/apps/docs/index.mdx b/apps/docs/index.mdx index 3d11c5cd3d..362d9a4547 100644 --- a/apps/docs/index.mdx +++ b/apps/docs/index.mdx @@ -14,7 +14,7 @@ Choose how to run Executor: - **[CLI](/local/cli)**: a background service on your machine, from the terminal - **[Desktop app](/local/desktop)**: the same runtime, as a desktop app - **[Executor Cloud](/hosted/cloud)**: hosted, with a free tier; nothing to run yourself -- **Self-host**: your own infrastructure, via [Docker](/hosted/docker) or [Cloudflare](/hosted/cloudflare) +- **Self-host**: your own infrastructure, via [Docker](/hosted/docker) ## Set up with your agent diff --git a/apps/docs/mcp-proxy.mdx b/apps/docs/mcp-proxy.mdx index c142f31b3b..14e98d6804 100644 --- a/apps/docs/mcp-proxy.mdx +++ b/apps/docs/mcp-proxy.mdx @@ -52,7 +52,7 @@ Executor: - **Local:** see [CLI](/local/cli) for `executor mcp` and the `add-mcp` command. - **Hosted:** see [Executor Cloud](/hosted/cloud), or self-host on - [Docker](/hosted/docker) or [Cloudflare](/hosted/cloudflare). + [Docker](/hosted/docker). Once a client is connected, every integration you add to Executor appears in that agent automatically. diff --git a/apps/marketing/public/llms.txt b/apps/marketing/public/llms.txt index d98199e407..109a52b3da 100644 --- a/apps/marketing/public/llms.txt +++ b/apps/marketing/public/llms.txt @@ -11,7 +11,7 @@ How it works: - Set policies: decide whether each tool is always allowed, needs approval, or is blocked. - Use it from any MCP-compatible agent: one catalog of tools, shared across every client. -Run it your way: local CLI, a desktop app, hosted Executor Cloud, or self-hosted on Docker or Cloudflare. +Run it your way: local CLI, a desktop app, hosted Executor Cloud, or self-hosted on Docker. ## Docs @@ -20,9 +20,17 @@ Run it your way: local CLI, a desktop app, hosted Executor Cloud, or self-hosted ## Product - [Website](https://executor.sh): product overview and getting started. -- [Pricing](https://executor.sh/#pricing): plans for Executor Cloud. +- [Pricing](https://executor.sh/pricing): plans for Executor Cloud. - [Install](https://executor.sh/#install): install the CLI and connect your first agent. +## For agents + +Markdown representations of the pages above, for machines rather than browsers. + +- [/index.md](https://executor.sh/index.md): the homepage as Markdown — what Executor is, how it works, what you get, ways to run it, pricing, and FAQ. +- [/setup-prompt.md](https://executor.sh/setup-prompt.md): a prompt to paste into a coding agent. It picks the right form of Executor, installs it, connects over MCP, and gets a first connection working. +- [/pricing.md](https://executor.sh/pricing.md): the Executor Cloud plans as Markdown. + ## Source - [GitHub](https://github.com/UsefulSoftwareCo/executor): source for the integration layer, plugins, and hosts. diff --git a/apps/marketing/src/assets/logos/chatgpt.svg b/apps/marketing/src/assets/logos/chatgpt.svg new file mode 100644 index 0000000000..2ebab679f6 --- /dev/null +++ b/apps/marketing/src/assets/logos/chatgpt.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/marketing/src/assets/logos/copilot.svg b/apps/marketing/src/assets/logos/copilot.svg new file mode 100644 index 0000000000..466d4a896a --- /dev/null +++ b/apps/marketing/src/assets/logos/copilot.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/marketing/src/assets/logos/gemini.svg b/apps/marketing/src/assets/logos/gemini.svg new file mode 100644 index 0000000000..f8d7189dc6 --- /dev/null +++ b/apps/marketing/src/assets/logos/gemini.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/marketing/src/assets/logos/grok.svg b/apps/marketing/src/assets/logos/grok.svg new file mode 100644 index 0000000000..642d7b6b0d --- /dev/null +++ b/apps/marketing/src/assets/logos/grok.svg @@ -0,0 +1,4 @@ + + + + diff --git a/apps/marketing/src/assets/logos/opencode.svg b/apps/marketing/src/assets/logos/opencode.svg new file mode 100644 index 0000000000..542a9188c3 --- /dev/null +++ b/apps/marketing/src/assets/logos/opencode.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/apps/marketing/src/assets/logos/windsurf.svg b/apps/marketing/src/assets/logos/windsurf.svg new file mode 100644 index 0000000000..bcaf639146 --- /dev/null +++ b/apps/marketing/src/assets/logos/windsurf.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/marketing/src/assets/logos/zed.svg b/apps/marketing/src/assets/logos/zed.svg new file mode 100644 index 0000000000..eca91c5382 --- /dev/null +++ b/apps/marketing/src/assets/logos/zed.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/marketing/src/assets/pfps/0xRaduan.jpg b/apps/marketing/src/assets/pfps/0xRaduan.jpg new file mode 100644 index 0000000000..eabf0da7ce Binary files /dev/null and b/apps/marketing/src/assets/pfps/0xRaduan.jpg differ diff --git a/apps/marketing/src/assets/pfps/BenceRedmond.jpg b/apps/marketing/src/assets/pfps/BenceRedmond.jpg new file mode 100644 index 0000000000..d05ed270f8 Binary files /dev/null and b/apps/marketing/src/assets/pfps/BenceRedmond.jpg differ diff --git a/apps/marketing/src/assets/pfps/DanielLockyer.jpg b/apps/marketing/src/assets/pfps/DanielLockyer.jpg new file mode 100644 index 0000000000..5667967ba9 Binary files /dev/null and b/apps/marketing/src/assets/pfps/DanielLockyer.jpg differ diff --git a/apps/marketing/src/assets/pfps/IanMitchel1.jpg b/apps/marketing/src/assets/pfps/IanMitchel1.jpg new file mode 100644 index 0000000000..c5be45d4d3 Binary files /dev/null and b/apps/marketing/src/assets/pfps/IanMitchel1.jpg differ diff --git a/apps/marketing/src/assets/pfps/JamieBrock19060.jpg b/apps/marketing/src/assets/pfps/JamieBrock19060.jpg new file mode 100644 index 0000000000..7bc9bc1498 Binary files /dev/null and b/apps/marketing/src/assets/pfps/JamieBrock19060.jpg differ diff --git a/apps/marketing/src/assets/pfps/JosXa_.jpg b/apps/marketing/src/assets/pfps/JosXa_.jpg new file mode 100644 index 0000000000..89743a7de9 Binary files /dev/null and b/apps/marketing/src/assets/pfps/JosXa_.jpg differ diff --git a/apps/marketing/src/assets/pfps/KyleFinken.jpg b/apps/marketing/src/assets/pfps/KyleFinken.jpg new file mode 100644 index 0000000000..737bf346fd Binary files /dev/null and b/apps/marketing/src/assets/pfps/KyleFinken.jpg differ diff --git a/apps/marketing/src/assets/pfps/MejiasDev.jpg b/apps/marketing/src/assets/pfps/MejiasDev.jpg new file mode 100644 index 0000000000..72145403d0 Binary files /dev/null and b/apps/marketing/src/assets/pfps/MejiasDev.jpg differ diff --git a/apps/marketing/src/assets/pfps/NathanFlurry.jpg b/apps/marketing/src/assets/pfps/NathanFlurry.jpg new file mode 100644 index 0000000000..70a21634d9 Binary files /dev/null and b/apps/marketing/src/assets/pfps/NathanFlurry.jpg differ diff --git a/apps/marketing/src/assets/pfps/RocketmanSh.jpg b/apps/marketing/src/assets/pfps/RocketmanSh.jpg new file mode 100644 index 0000000000..1c26090fcd Binary files /dev/null and b/apps/marketing/src/assets/pfps/RocketmanSh.jpg differ diff --git a/apps/marketing/src/assets/pfps/Swedish_chef.jpg b/apps/marketing/src/assets/pfps/Swedish_chef.jpg new file mode 100644 index 0000000000..8512153e48 Binary files /dev/null and b/apps/marketing/src/assets/pfps/Swedish_chef.jpg differ diff --git a/apps/marketing/src/assets/pfps/ThomsenDrake.jpg b/apps/marketing/src/assets/pfps/ThomsenDrake.jpg new file mode 100644 index 0000000000..513b4d9847 Binary files /dev/null and b/apps/marketing/src/assets/pfps/ThomsenDrake.jpg differ diff --git a/apps/marketing/src/assets/pfps/UltraLinx.jpg b/apps/marketing/src/assets/pfps/UltraLinx.jpg new file mode 100644 index 0000000000..6d1fe9ac58 Binary files /dev/null and b/apps/marketing/src/assets/pfps/UltraLinx.jpg differ diff --git a/apps/marketing/src/assets/pfps/WillUndrll.jpg b/apps/marketing/src/assets/pfps/WillUndrll.jpg new file mode 100644 index 0000000000..8266af2b56 Binary files /dev/null and b/apps/marketing/src/assets/pfps/WillUndrll.jpg differ diff --git a/apps/marketing/src/assets/pfps/adamghaida.jpg b/apps/marketing/src/assets/pfps/adamghaida.jpg new file mode 100644 index 0000000000..4e5eef2443 Binary files /dev/null and b/apps/marketing/src/assets/pfps/adamghaida.jpg differ diff --git a/apps/marketing/src/assets/pfps/aidansunbury.jpg b/apps/marketing/src/assets/pfps/aidansunbury.jpg new file mode 100644 index 0000000000..2cda2814ff Binary files /dev/null and b/apps/marketing/src/assets/pfps/aidansunbury.jpg differ diff --git a/apps/marketing/src/assets/pfps/aidenybai.jpg b/apps/marketing/src/assets/pfps/aidenybai.jpg new file mode 100644 index 0000000000..d1cc805a25 Binary files /dev/null and b/apps/marketing/src/assets/pfps/aidenybai.jpg differ diff --git a/apps/marketing/src/assets/pfps/alexrigler.jpg b/apps/marketing/src/assets/pfps/alexrigler.jpg new file mode 100644 index 0000000000..d11aea26d3 Binary files /dev/null and b/apps/marketing/src/assets/pfps/alexrigler.jpg differ diff --git a/apps/marketing/src/assets/pfps/andrelandgraf.jpg b/apps/marketing/src/assets/pfps/andrelandgraf.jpg new file mode 100644 index 0000000000..ded1e7aafe Binary files /dev/null and b/apps/marketing/src/assets/pfps/andrelandgraf.jpg differ diff --git a/apps/marketing/src/assets/pfps/aryasaatvik.jpg b/apps/marketing/src/assets/pfps/aryasaatvik.jpg new file mode 100644 index 0000000000..d1898e3d83 Binary files /dev/null and b/apps/marketing/src/assets/pfps/aryasaatvik.jpg differ diff --git a/apps/marketing/src/assets/pfps/benapatton.jpg b/apps/marketing/src/assets/pfps/benapatton.jpg new file mode 100644 index 0000000000..0f0274e54d Binary files /dev/null and b/apps/marketing/src/assets/pfps/benapatton.jpg differ diff --git a/apps/marketing/src/assets/pfps/branalytc.jpg b/apps/marketing/src/assets/pfps/branalytc.jpg new file mode 100644 index 0000000000..6eeea32e61 Binary files /dev/null and b/apps/marketing/src/assets/pfps/branalytc.jpg differ diff --git a/apps/marketing/src/assets/pfps/capeflow.jpg b/apps/marketing/src/assets/pfps/capeflow.jpg new file mode 100644 index 0000000000..79363cf2ba Binary files /dev/null and b/apps/marketing/src/assets/pfps/capeflow.jpg differ diff --git a/apps/marketing/src/assets/pfps/chribjel.jpg b/apps/marketing/src/assets/pfps/chribjel.jpg new file mode 100644 index 0000000000..53836dc2c3 Binary files /dev/null and b/apps/marketing/src/assets/pfps/chribjel.jpg differ diff --git a/apps/marketing/src/assets/pfps/coreyhainesco.jpg b/apps/marketing/src/assets/pfps/coreyhainesco.jpg new file mode 100644 index 0000000000..da74d623b4 Binary files /dev/null and b/apps/marketing/src/assets/pfps/coreyhainesco.jpg differ diff --git a/apps/marketing/src/assets/pfps/cramforce.jpg b/apps/marketing/src/assets/pfps/cramforce.jpg new file mode 100644 index 0000000000..5274afd335 Binary files /dev/null and b/apps/marketing/src/assets/pfps/cramforce.jpg differ diff --git a/apps/marketing/src/assets/pfps/davis7.jpg b/apps/marketing/src/assets/pfps/davis7.jpg new file mode 100644 index 0000000000..005c1f80d6 Binary files /dev/null and b/apps/marketing/src/assets/pfps/davis7.jpg differ diff --git a/apps/marketing/src/assets/pfps/dillon_mulroy.jpg b/apps/marketing/src/assets/pfps/dillon_mulroy.jpg new file mode 100644 index 0000000000..5d312411a7 Binary files /dev/null and b/apps/marketing/src/assets/pfps/dillon_mulroy.jpg differ diff --git a/apps/marketing/src/assets/pfps/erikrogne.jpg b/apps/marketing/src/assets/pfps/erikrogne.jpg new file mode 100644 index 0000000000..eab27bce01 Binary files /dev/null and b/apps/marketing/src/assets/pfps/erikrogne.jpg differ diff --git a/apps/marketing/src/assets/pfps/ethanniser.jpg b/apps/marketing/src/assets/pfps/ethanniser.jpg new file mode 100644 index 0000000000..7ee63d82f3 Binary files /dev/null and b/apps/marketing/src/assets/pfps/ethanniser.jpg differ diff --git a/apps/marketing/src/assets/pfps/euboid.jpg b/apps/marketing/src/assets/pfps/euboid.jpg new file mode 100644 index 0000000000..bf5fbbbede Binary files /dev/null and b/apps/marketing/src/assets/pfps/euboid.jpg differ diff --git a/apps/marketing/src/assets/pfps/flybayer.jpg b/apps/marketing/src/assets/pfps/flybayer.jpg new file mode 100644 index 0000000000..b9987e3541 Binary files /dev/null and b/apps/marketing/src/assets/pfps/flybayer.jpg differ diff --git a/apps/marketing/src/assets/pfps/georgekontridze.jpg b/apps/marketing/src/assets/pfps/georgekontridze.jpg new file mode 100644 index 0000000000..5414ae8636 Binary files /dev/null and b/apps/marketing/src/assets/pfps/georgekontridze.jpg differ diff --git a/apps/marketing/src/assets/pfps/grfwings.jpg b/apps/marketing/src/assets/pfps/grfwings.jpg new file mode 100644 index 0000000000..1a17f01cdf Binary files /dev/null and b/apps/marketing/src/assets/pfps/grfwings.jpg differ diff --git a/apps/marketing/src/assets/pfps/itschrisjayden.jpg b/apps/marketing/src/assets/pfps/itschrisjayden.jpg new file mode 100644 index 0000000000..90285efc75 Binary files /dev/null and b/apps/marketing/src/assets/pfps/itschrisjayden.jpg differ diff --git a/apps/marketing/src/assets/pfps/itsmeblueguy.jpg b/apps/marketing/src/assets/pfps/itsmeblueguy.jpg new file mode 100644 index 0000000000..f452dfedcb Binary files /dev/null and b/apps/marketing/src/assets/pfps/itsmeblueguy.jpg differ diff --git a/apps/marketing/src/assets/pfps/jachands.jpg b/apps/marketing/src/assets/pfps/jachands.jpg new file mode 100644 index 0000000000..214b938d86 Binary files /dev/null and b/apps/marketing/src/assets/pfps/jachands.jpg differ diff --git a/apps/marketing/src/assets/pfps/jeremyosih.jpg b/apps/marketing/src/assets/pfps/jeremyosih.jpg new file mode 100644 index 0000000000..16d165c6b8 Binary files /dev/null and b/apps/marketing/src/assets/pfps/jeremyosih.jpg differ diff --git a/apps/marketing/src/assets/pfps/johnyeo_.jpg b/apps/marketing/src/assets/pfps/johnyeo_.jpg new file mode 100644 index 0000000000..0d5c20eb83 Binary files /dev/null and b/apps/marketing/src/assets/pfps/johnyeo_.jpg differ diff --git a/apps/marketing/src/assets/pfps/jwwwel.jpg b/apps/marketing/src/assets/pfps/jwwwel.jpg new file mode 100644 index 0000000000..b6073ca6da Binary files /dev/null and b/apps/marketing/src/assets/pfps/jwwwel.jpg differ diff --git a/apps/marketing/src/assets/pfps/kaeden_dev.jpg b/apps/marketing/src/assets/pfps/kaeden_dev.jpg new file mode 100644 index 0000000000..30f7b678e1 Binary files /dev/null and b/apps/marketing/src/assets/pfps/kaeden_dev.jpg differ diff --git a/apps/marketing/src/assets/pfps/kr0der.jpg b/apps/marketing/src/assets/pfps/kr0der.jpg new file mode 100644 index 0000000000..d7129d8cc2 Binary files /dev/null and b/apps/marketing/src/assets/pfps/kr0der.jpg differ diff --git a/apps/marketing/src/assets/pfps/miaugladiator1.jpg b/apps/marketing/src/assets/pfps/miaugladiator1.jpg new file mode 100644 index 0000000000..0f9fc052c6 Binary files /dev/null and b/apps/marketing/src/assets/pfps/miaugladiator1.jpg differ diff --git a/apps/marketing/src/assets/pfps/mteamisloading.jpg b/apps/marketing/src/assets/pfps/mteamisloading.jpg new file mode 100644 index 0000000000..6e634e9ca1 Binary files /dev/null and b/apps/marketing/src/assets/pfps/mteamisloading.jpg differ diff --git a/apps/marketing/src/assets/pfps/mynameisyahia.jpg b/apps/marketing/src/assets/pfps/mynameisyahia.jpg new file mode 100644 index 0000000000..567b643cf7 Binary files /dev/null and b/apps/marketing/src/assets/pfps/mynameisyahia.jpg differ diff --git a/apps/marketing/src/assets/pfps/nateberkopec.jpg b/apps/marketing/src/assets/pfps/nateberkopec.jpg new file mode 100644 index 0000000000..1f6581a89b Binary files /dev/null and b/apps/marketing/src/assets/pfps/nateberkopec.jpg differ diff --git a/apps/marketing/src/assets/pfps/philzona.jpg b/apps/marketing/src/assets/pfps/philzona.jpg new file mode 100644 index 0000000000..6f6141df90 Binary files /dev/null and b/apps/marketing/src/assets/pfps/philzona.jpg differ diff --git a/apps/marketing/src/assets/pfps/rmedranollamas.jpg b/apps/marketing/src/assets/pfps/rmedranollamas.jpg new file mode 100644 index 0000000000..7fb6c4a50a Binary files /dev/null and b/apps/marketing/src/assets/pfps/rmedranollamas.jpg differ diff --git a/apps/marketing/src/assets/pfps/ryan_morr.jpg b/apps/marketing/src/assets/pfps/ryan_morr.jpg new file mode 100644 index 0000000000..c5928b4cc5 Binary files /dev/null and b/apps/marketing/src/assets/pfps/ryan_morr.jpg differ diff --git a/apps/marketing/src/assets/pfps/samhogan.jpg b/apps/marketing/src/assets/pfps/samhogan.jpg new file mode 100644 index 0000000000..41a105d3dd Binary files /dev/null and b/apps/marketing/src/assets/pfps/samhogan.jpg differ diff --git a/apps/marketing/src/assets/pfps/samilaa.jpg b/apps/marketing/src/assets/pfps/samilaa.jpg new file mode 100644 index 0000000000..91657b5df3 Binary files /dev/null and b/apps/marketing/src/assets/pfps/samilaa.jpg differ diff --git a/apps/marketing/src/assets/pfps/sensho.jpg b/apps/marketing/src/assets/pfps/sensho.jpg new file mode 100644 index 0000000000..94a94e6316 Binary files /dev/null and b/apps/marketing/src/assets/pfps/sensho.jpg differ diff --git a/apps/marketing/src/assets/pfps/sergical.jpg b/apps/marketing/src/assets/pfps/sergical.jpg new file mode 100644 index 0000000000..753aeedaf5 Binary files /dev/null and b/apps/marketing/src/assets/pfps/sergical.jpg differ diff --git a/apps/marketing/src/assets/pfps/shuv1337.png b/apps/marketing/src/assets/pfps/shuv1337.png new file mode 100644 index 0000000000..440fee7cd8 Binary files /dev/null and b/apps/marketing/src/assets/pfps/shuv1337.png differ diff --git a/apps/marketing/src/assets/pfps/somto_odera.jpg b/apps/marketing/src/assets/pfps/somto_odera.jpg new file mode 100644 index 0000000000..b3f90fee9a Binary files /dev/null and b/apps/marketing/src/assets/pfps/somto_odera.jpg differ diff --git a/apps/marketing/src/assets/pfps/zaherg.jpg b/apps/marketing/src/assets/pfps/zaherg.jpg new file mode 100644 index 0000000000..adc3b04179 Binary files /dev/null and b/apps/marketing/src/assets/pfps/zaherg.jpg differ diff --git a/apps/marketing/src/assets/pfps/zevtnax.png b/apps/marketing/src/assets/pfps/zevtnax.png new file mode 100644 index 0000000000..ed49cf48ef Binary files /dev/null and b/apps/marketing/src/assets/pfps/zevtnax.png differ diff --git a/apps/marketing/src/assets/rhys-sullivan.jpg b/apps/marketing/src/assets/rhys-sullivan.jpg new file mode 100644 index 0000000000..0a98109c47 Binary files /dev/null and b/apps/marketing/src/assets/rhys-sullivan.jpg differ diff --git a/apps/marketing/src/components/animated-beam-demo.tsx b/apps/marketing/src/components/animated-beam-demo.tsx index ee9af946ac..8da56d86dd 100644 --- a/apps/marketing/src/components/animated-beam-demo.tsx +++ b/apps/marketing/src/components/animated-beam-demo.tsx @@ -332,7 +332,7 @@ function Row({ return (
{children} -
+
{label} diff --git a/apps/marketing/src/components/variants/doc-body.astro b/apps/marketing/src/components/variants/doc-body.astro new file mode 100644 index 0000000000..efddbab7ee --- /dev/null +++ b/apps/marketing/src/components/variants/doc-body.astro @@ -0,0 +1,374 @@ +--- +// Document-style homepage body. Reads like a short paper about the product: +// one modest headline, plain first-person prose, six beats separated by +// hairlines, the two demos kept as numbered figures, and one way to start. +// Sits inside the rail layout; the rail's "On this page" list points at the +// section ids below. +import { AnimatedBeamDemo } from "../animated-beam-demo"; +import { ContextBloatDemo } from "../context-bloat-demo"; +import { setupPrompt } from "../../content/site-copy"; +import Testimonials from "./testimonials.astro"; +import "../../styles/variants/doc.css"; +// Avatar goes through Vite so it ships as a hashed asset; new /public files +// are not picked up by the deploy pipeline. +import rhysAvatar from "../../assets/rhys-sullivan.jpg"; +// Brand marks for the hover reveals in the intro steps. +const logos = import.meta.glob("../../assets/logos/*.svg", { + eager: true, + query: "?url", + import: "default", +}); +const logo = (name: string): string => { + const hit = Object.entries(logos).find(([k]) => k.endsWith(`/${name}.svg`)); + if (!hit) throw new Error(`missing logo ${name}`); + return hit[1]; +}; +const toolLogos = ["github", "linear", "sentry", "stripe", "notion", "slack", "jira", "gmail"]; +const agentLogos = ["claude", "codex", "cursor", "chatgpt", "gemini", "copilot", "windsurf", "opencode", "zed", "grok"]; +// The three marks on the copy button; the full list would crowd it. +const agentLogosShort = ["claude", "codex", "cursor"]; +const X_URL = "https://x.com/RhysSullivan"; + +interface Props { + gh: string; +} +const { gh } = Astro.props; +--- + +
+ {/* ── Intro ── */} +
+

Executor is an MCP gateway.

+
    +
  1. + 01 + + Connect + + everything + + + to Executor. + +
  2. +
  3. + 02 + + Give + + your agent + + + the Executor tool. + +
  4. +
  5. + 03 + That's it. Get started. +
  6. +
+

One MCP. All your accounts.

+
+ + + +
+
+
+

Sign in, point your agent at one URL, done. Free for up to three people and 100,000 executions a month.

+
Start on Cloud
+
+ + +
+
+ +
+
+ +
+
+ Figure 1. Three clients reach six tools through one + MCP endpoint. Each tool keeps the protocol it came from. +
+
+ + {/* ── 01 Mission ── */} +
+

What we think

+

+ Every agent wants its own copy of every integration. You set up GitHub in Claude + Code, then again in Cursor, then again in Codex. Same OAuth, same API key pasted in + five places, and none of them agree on what a tool is allowed to do. +

+

+ Your tools should belong to you, not to whichever agent you opened today. Executor + turns everything into one shape: a name, an input, an output. An MCP server, an + OpenAPI spec, and a GraphQL API all look the same to the agent, so you connect once + and every agent gets it. +

+

+ And the safe way has to be the easy way, or nobody does it. Executor knows a GET + from a DELETE, asks before the scary ones, and runs everything in a sandbox where + the model never sees a raw token. +

+
+ + {/* ── 02 Context ── */} +
+

Thousands of tools, one in the prompt

+

+ The model only ever sees one tool. Executor looks up what it needs when the code + asks for it, so you can connect fifty services and the prompt stays the same size. + Toggle a few below and watch the numbers. +

+
+ +
+ Figure 2. Prompt size with your selection of + integrations connected, with and without Executor. +
+
+
+ + {/* ── 03 Safety ── */} +
+

Safe by default

+
+
+
Policies come from the source.
+
+ GET versus DELETE for OpenAPI, destructiveHint for MCP, mutations for GraphQL. + Agents run the safe calls on their own and ask before the rest. You can override + any tool. +
+
+
+
Secrets never reach the model.
+
+ Calls run in an isolated JavaScript sandbox. Credentials are attached host-side + at call time and never enter the sandbox, the agent, or the model's context. +
+
+
+
Set up once, whole team has it.
+
+ Admins add workspace connections everyone shares. Individuals add their own. + New teammates get the catalog on day one, with the same policies, and you can + block a tool for the whole workspace in one click. +
+
+
+
Open source, so you can check.
+
+ Cloud stores credentials in WorkOS Vault. Local and self-hosted keep them on + your machine, or in 1Password. The whole thing is on GitHub if you would + rather read the code than take our word for it. + Source ↗ +
+
+
+
+ + {/* ── 04 Run it ── */} +
+

Run it where you want

+

+ Same tools, same policies, four ways to run them. +

+
+
+ Cloud + Hosted, free to start, nothing to install. We keep it updated. + Start on Cloud +
+
+ Desktop + An app for Mac, Windows, and Linux. Everything stays on your machine. + + Download for Mac + Download for Mac (Intel) + Download for Windows + Download for Linux + + +
+
+ CLI + A background service for headless machines and servers. + npm i -g executor +
+
+ Self-hosted + One Docker image on your own box. Same code Cloud runs. + Self-hosting docs +
+
+

+ Not an agent? Cloud also has an HTTP API with user and workspace keys, so a script + or a service can call the same tools with the same policies. +

+
+ + {/* ── What people say ── */} +
+

What people say

+

Mostly that they stopped copying API keys into five different agents.

+ +
+ + {/* ── 05 Pricing ── */} +
+

Pricing

+

+ Cloud is free for up to three people. Team is $15 per member per month. Running it + yourself is free. +

+

+ See pricing ↗ · Talk to us about Enterprise +

+
+ + {/* ── 06 Writing ── */} +
+

Writing

+ +
+ + {/* ── 07 About ── */} +
+

About

+

+ Executor is backed by Y Combinator. It started because I wanted my own agents to + reach my accounts in a way that was not scary. Most setups make you choose between + locked down and useless, or wide open and risky. I wanted a third option. +

+ + + + Rhys Sullivan + Founder. Say hi on X, @RhysSullivan ↗ + + +

+ Discord ↗ +

+
+
+ + + + diff --git a/apps/marketing/src/components/variants/rail-b.astro b/apps/marketing/src/components/variants/rail-b.astro new file mode 100644 index 0000000000..ad09a6596f --- /dev/null +++ b/apps/marketing/src/components/variants/rail-b.astro @@ -0,0 +1,135 @@ +--- +// Rail, variant B: "Gridline rail". +// Compact and technical: mark-only wordmark, a tight link stack, an outlined +// CTA, and a numbered mono index. A full-height hairline closes the right edge. +import "../../styles/variants/rail.css"; +import ycBackedBy from "../../assets/yc-backed-by.svg?url"; +import rhysAvatar from "../../assets/rhys-sullivan.jpg"; + +const X_URL = "https://x.com/RhysSullivan"; +const EMAIL = "rhys@executor.sh"; + +interface Props { + stars: string | null; + /** Which primary link is the current page. Defaults to home. */ + current?: "home" | "pricing" | "blog"; +} +const { stars, current = "home" } = Astro.props; +const cur = (k: string) => (k === current ? "page" : undefined); + +const GH = "https://github.com/UsefulSoftwareCo/executor"; +const SIGN_IN = "/api/auth/login"; + +const sections = [ + { id: "mission", label: "What we think" }, + { id: "context", label: "Context" }, + { id: "safety", label: "Safety" }, + { id: "install", label: "Run it" }, + { id: "people", label: "What people say" }, + { id: "pricing", label: "Pricing" }, + { id: "writing", label: "Writing" }, + { id: "about", label: "About" }, +]; +const pad = (index: number) => String(index + 1).padStart(2, "0"); +--- + + + + diff --git a/apps/marketing/src/components/variants/rail-spy.ts b/apps/marketing/src/components/variants/rail-spy.ts new file mode 100644 index 0000000000..817e311f22 --- /dev/null +++ b/apps/marketing/src/components/variants/rail-spy.ts @@ -0,0 +1,80 @@ +// Scroll-spy for the sidebar rail. +// +// The rail renders a `.rail__toc` whose links point at the page's section +// anchors. This marks the link for the section the reader is in with +// `aria-current="true"`, and mirrors the id onto the aside as +// `data-active-section` so CSS can react without extra classes. +// +// "The section the reader is in" is the last section whose top has crossed a +// line a little below the top of the viewport. An anchor jump lands a section +// exactly on that line, so a clicked link is always the one that lights up, +// even for short sections near the end of the page. At the very bottom of the +// page the final section wins, since the reader cannot scroll any further. +// +// Tolerates missing sections and stays idempotent if the initializer runs +// again on the same element. + +const BOUND = "railSpyBound"; +const LINE_PX = 120; + +export function initRailSpy(root: HTMLElement): void { + if (root.dataset[BOUND] === "1") return; + root.dataset[BOUND] = "1"; + + const links = Array.from(root.querySelectorAll(".rail__toc a[href^='#']")); + if (links.length === 0) return; + + const pairs: Array<{ section: HTMLElement; link: HTMLAnchorElement }> = []; + for (const link of links) { + const id = decodeURIComponent(link.getAttribute("href")!.slice(1)); + const section = id ? document.getElementById(id) : null; + if (section == null) { + const row = link.closest(".rail__toc-item") ?? link; + row.style.display = "none"; + continue; + } + pairs.push({ section, link }); + } + if (pairs.length === 0) return; + + const apply = (): void => { + const line = window.scrollY + LINE_PX; + const atBottom = + window.innerHeight + window.scrollY >= document.documentElement.scrollHeight - 2; + + let active: HTMLElement | null = null; + if (atBottom) { + active = pairs[pairs.length - 1]!.section; + } else { + for (const { section } of pairs) { + if (section.offsetTop <= line) active = section; + else break; + } + } + + if (active == null) { + delete root.dataset.activeSection; + } else { + root.dataset.activeSection = active.id; + } + for (const { section, link } of pairs) { + if (section === active) link.setAttribute("aria-current", "true"); + else link.removeAttribute("aria-current"); + } + }; + + let scheduled = false; + const schedule = (): void => { + if (scheduled) return; + scheduled = true; + window.requestAnimationFrame(() => { + scheduled = false; + apply(); + }); + }; + + window.addEventListener("scroll", schedule, { passive: true }); + window.addEventListener("resize", schedule); + window.addEventListener("hashchange", schedule); + apply(); +} diff --git a/apps/marketing/src/components/variants/testimonials.astro b/apps/marketing/src/components/variants/testimonials.astro new file mode 100644 index 0000000000..debaf1f363 --- /dev/null +++ b/apps/marketing/src/components/variants/testimonials.astro @@ -0,0 +1,98 @@ +--- +// "What people say": every hand-picked post about Executor on X, as small +// cards that drift sideways in rows. Each card links to the original post. +// The rows pause on hover and stop entirely for reduced-motion readers, who +// see a wrapped grid instead. +import { testimonials } from "../../content/testimonials"; +import "../../styles/variants/testimonials.css"; + +// Avatars ship as hashed assets. Both extensions exist because one handle's +// picture is a PNG on X. +const avatars = import.meta.glob<{ default: ImageMetadata }>( + "../../assets/pfps/*.{jpg,png}", + { eager: true }, +); +const avatarFor = (handle: string): string | null => { + for (const [path, mod] of Object.entries(avatars)) { + if (path.endsWith(`/${handle}.jpg`) || path.endsWith(`/${handle}.png`)) return mod.default.src; + } + return null; +}; + +// Deal the list into rows in turn so long and short posts mix. +const ROWS = 3; +const rows: (typeof testimonials)[number][][] = Array.from({ length: ROWS }, () => []); +testimonials.forEach((t, i) => rows[i % ROWS]!.push(t)); + +// "Add yours" opens X's composer with a draft the reader can edit or delete. +const composeUrl = `https://x.com/intent/post?text=${encodeURIComponent( + "I've been using @executorsh to ", +)}`; +--- + +
+ { + rows.map((row, r) => ( +
+ {/* Two copies of the row make the marquee loop seamless. */} + {[0, 1].map((copy) => ( + + ))} +
+ )) + } +
+ + diff --git a/apps/marketing/src/content/site-copy.ts b/apps/marketing/src/content/site-copy.ts new file mode 100644 index 0000000000..a66099e6aa --- /dev/null +++ b/apps/marketing/src/content/site-copy.ts @@ -0,0 +1,183 @@ +// --------------------------------------------------------------------------- +// Shared marketing copy. One source for text that must read the same in the +// HTML homepage and in the machine-readable Markdown endpoints +// (`/index.md`, `/setup-prompt.md`, `/pricing.md`). +// +// Import from here rather than duplicating strings: the homepage renders the +// human view, the endpoints render the agent view, and both must stay in step. +// --------------------------------------------------------------------------- + +/** + * Copied to the clipboard by the "Set up with your agent" hero CTA, and served + * verbatim at `/setup-prompt.md`. A visitor pastes it into their coding agent + * (Claude, Cursor, ...) and the agent picks the right form of Executor, + * installs it, connects over MCP, and gets a first connection working. Keep in + * sync with the docs "Set up with your agent" section (apps/docs/index.mdx). + */ +export const setupPrompt = `Help me set up Executor and get my first connection working. + +Executor is an open source integration layer for AI agents: one place to configure every integration (MCP servers, OpenAPI specs, GraphQL APIs) and connect to them over MCP. + +Start by helping me pick the right form to run it in. Chat with me about it rather than jumping straight to a yes/no question, and recommend one. If I just want the fastest path, suggest Executor Cloud (free tier, nothing to install). All forms expose the same functionality, just packaged differently: + +Local (everything stays on my machine): +- Desktop app: a desktop app for Mac, Windows, and Linux. Best for a regular desktop environment. +- CLI (\`executor\`): best for a headless or server environment. +Both run a local HTTP server as a background service that any MCP client can connect to. + +Hosted (use it from multiple agents, including cloud ones, with nothing running locally): +- Executor Cloud: hosted, generous free tier, sign in and start immediately. +- Self-hosted: a Docker image. + +How to think about it: +- Want all your data on your own machine? Go local: the desktop app for a regular environment, the CLI for a headless one. +- Want to use it from multiple agents (including cloud agents like ChatGPT), or not run anything locally? Go hosted: Executor Cloud is the fastest start; the self-hosted Docker version gives you full control. + +Terms you'll come across: +- Integration: anything you add (an MCP server, an OpenAPI spec, a GraphQL API). +- Connection: one configured instance of an integration. An integration can have many connections, and a connection doesn't have to be authenticated. +- Policy: whether each tool is always allowed, requires approval, or is blocked. Policies start from a sensible default derived from the imported spec (for example, GET requests on an OpenAPI spec are allowed by default). + +Once you know which form I want: +1. Walk me through installing it. +2. Connect Executor to you over MCP. Most MCP clients only load servers at startup, so after adding it I may need to restart the client or open a new chat before the Executor tools appear. Tell me if that's needed and wait for me to do it before continuing. +3. Once the tools are available, help me add my first integration and get one tool working end to end. + +Docs: https://executor.sh/docs +Source (and the place to start if something breaks): https://github.com/UsefulSoftwareCo/executor`; + +/** Canonical GitHub repository. */ +export const GITHUB_URL = "https://github.com/UsefulSoftwareCo/executor"; + +/** One-line description of the product, used as the Markdown tagline. */ +export const tagline = + "Executor is an MCP gateway. Anything that speaks MCP, like Claude Code, Cursor, or Codex, points at one endpoint and reaches every tool you connect."; + +/** Cache-Control for the Markdown endpoints. Short, so copy edits land fast. */ +export const MARKDOWN_CACHE_CONTROL = "public, max-age=300"; + +/** Content-Type for the Markdown endpoints. */ +export const MARKDOWN_CONTENT_TYPE = "text/markdown; charset=utf-8"; + +export type PricingTier = { + readonly name: string; + readonly price: string; + readonly audience: string; + readonly featuresLabel?: string; + readonly features: ReadonlyArray; + readonly cta: string; +}; + +/** + * Pricing tiers. The `/pricing` page and `/pricing.md` both read this list, + * so it is the single source of truth. + */ +export const pricingTiers: ReadonlyArray = [ + { + name: "Free", + price: "$0 / month", + audience: "For small teams getting started", + features: ["Up to 3 members", "100,000 executions per month", "Unlimited integrations"], + cta: "Start free: https://executor.sh/cloud", + }, + { + name: "Team", + price: "$15 / member / month", + audience: "For growing organizations (recommended)", + features: [ + "14-day free trial, then $15 / member / month", + "Unlimited executions", + "Verified domains & join by team domain", + ], + cta: "Start free trial: https://executor.sh/cloud", + }, + { + name: "Enterprise", + price: "Custom", + audience: "For orgs with custom needs", + featuresLabel: "Everything in Team, plus", + features: [ + "Self-hosted or dedicated cloud deployment support", + "SSO / SAML & SCIM provisioning", + "Audit logs for every tool call", + "Dedicated support & onboarding", + "Security reviews, DPA & SOC 2 on request", + ], + cta: "Contact rhys@executor.sh", + }, +]; + +export type Capability = { + readonly title: string; + readonly body: string; + readonly comingSoon?: boolean; +}; + +/** The six capability cards from the homepage "What you get" section. */ +export const capabilities: ReadonlyArray = [ + { + title: "One tool shape", + body: "MCP, OpenAPI, GraphQL, or a custom integration. Under the hood they all become a tool name, an input schema, and an output schema.", + }, + { + title: "Call it any way", + body: "Today it is a code-mode MCP. It could just as well be the Executor CLI, a one-off script, a gen-UI dashboard, or a reusable workflow. Same tools, every surface.", + }, + { + title: "Trace every call", + body: "One place to see every run and tool call. Audit any decision after the fact.", + comingSoon: true, + }, + { + title: "Set up once, whole team has it", + body: "Per-user credentials and shared ones. No onboarding ritual, no toggling MCPs on and off mid-task.", + }, + { + title: "Destructive actions pull you back in", + body: "Executor keeps the semantics it imported: GET vs DELETE for OpenAPI, destructiveHint for MCP, mutations for GraphQL. Agents auto-run the safe stuff and ask before the rest.", + }, + { + title: "Sandboxed execution", + body: "Tool calls run in an isolated JavaScript sandbox. Secrets are injected host-side at call time and never enter the sandbox heap, so the agent and model never see a raw token.", + }, +]; + +export type Faq = { readonly question: string; readonly answer: string }; + +/** The homepage FAQ. */ +export const faqs: ReadonlyArray = [ + { + question: "Where does my code run, and what touches my credentials?", + answer: + "Tool calls run in an isolated JavaScript sandbox. Credentials are resolved host-side at call time and injected into the outbound request only. They never enter the sandbox heap, the code your agent wrote, the agent, or the model.", + }, + { + question: "Can the agent or the model ever see a raw token?", + answer: + "No. Secrets stay host-side by design. The sandbox calls a tool by name; Executor attaches the credential to the real request outside the sandbox, so a token is never present in anything the model can read.", + }, + { + question: "What can call Executor?", + answer: + "Any MCP client (Claude Code, Cursor, Codex, and others), the Executor CLI, or a native client you drop in. Because tools share one shape, the calling surface is interchangeable.", + }, + { + question: "How does it know what is safe to auto-run?", + answer: + "Executor preserves the semantics of whatever it imported: GET vs DELETE for OpenAPI, destructiveHint for MCP, and mutations for GraphQL. That tells the agent what it can run on its own and what should pull you back into the loop.", + }, + { + question: "Is it open source? Can I self-host?", + answer: + "Yes. Executor is open source and built on the SDK we publish to npm. Run the desktop app locally, self-host the server, or use the hosted cloud. Same code paths, different deployment.", + }, +]; + +/** Response helper shared by the Markdown endpoints. */ +export const markdownResponse = (body: string): Response => + new Response(body, { + headers: { + "Content-Type": MARKDOWN_CONTENT_TYPE, + "Cache-Control": MARKDOWN_CACHE_CONTROL, + }, + }); diff --git a/apps/marketing/src/content/testimonials.ts b/apps/marketing/src/content/testimonials.ts new file mode 100644 index 0000000000..da0cee113d --- /dev/null +++ b/apps/marketing/src/content/testimonials.ts @@ -0,0 +1,388 @@ +// Public posts about Executor on X, hand-picked. Text is quoted as written, +// with @-mentions and links reduced to plain words so a card reads on its own. +// Avatars live in src/assets/pfps/. and ship as hashed assets. +// +// Order is the display order; the marquee rows draw from this list in turn, so +// the first twelve are what a reader sees before the rows start to move. + +export interface Testimonial { + readonly handle: string; + readonly name: string; + readonly id: string; + readonly text: string; +} + +export const testimonials: readonly Testimonial[] = [ + { + handle: "euboid", + name: "Wilson Wilson", + id: "2098424364207108181", + text: "I disconnected all my MCPs and switched to executor.sh. Best thing I've done this year. Claude, Codex, Hermes, Grok, Cursor - it's so much overhead remembering where you've connected what. Now I just have one MCP - and all my agents inherit all my tools. Highly recommend!", + }, + { + handle: "nateberkopec", + name: "Nate Berkopec", + id: "2099995262802641129", + text: "For the last 3 months I've been telling all my clients to move everything to MCPs and executor.sh.", + }, + { + handle: "cramforce", + name: "Malte Ubl", + id: "2042626213697720632", + text: "I've been working on integrating Rhys's excellent `executor` package into just-bash.", + }, + { + handle: "aidenybai", + name: "Aiden Bai", + id: "2052907020202979362", + text: "executor is very good software", + }, + { + handle: "chribjel", + name: "Christoffer Bjelke", + id: "2092939357980123545", + text: "update: it *is* the perfect wrapper. now executor.sh is my only mcp for all my agents, and i just configure my other connected services through executor. all integrations can have multiple connections, so i can have personal/work accounts for every integration. so so nice, literally perfect", + }, + { + handle: "ethanniser", + name: "Ethan Niser", + id: "2094632205570207834", + text: "I dont think I've ever oauthed so much in my life until AI. connections, connections, connections. s/o executor.sh", + }, + { + handle: "ThomsenDrake", + name: "Drake Thomsen", + id: "2093758541798506783", + text: "Moving my connectors onto executor.sh is such a refreshing experience. I now only have to configure a single connector when I'm setting up a new agent instead of 15 different OAuths or API keys", + }, + { + handle: "NathanFlurry", + name: "Nathan Flurry", + id: "2078663475782685025", + text: "really digging executor + browserbase for unifying my integrations. i use agents in a lot of places: linux rig over ssh, amp orbs for bg agents, claude/chatgpt on mobile. this setup lets me sign in once (browser or mcp) and access it everywhere", + }, + { + handle: "samhogan", + name: "Sam Hogan", + id: "2094813654202089970", + text: "My favorite features: Quick set-up. Easily share MCPs between agent harnesses with centralized authentication and access control. Auth once, use everywhere. Codemode execution by default", + }, + { + handle: "DanielLockyer", + name: "Daniel Lockyer", + id: "2084357678105563583", + text: "I've been using Executor since this mini-pitch and honestly it's great. I just configure my MCPs/OpenAPIs in one place and all my clients can use the connections. I've also used the blocklist feature to remove dangerous actions from some MCPs, very nice. Can recommend!", + }, + { + handle: "JosXa_", + name: "JosXa", + id: "2054494157096329504", + text: "I compared dozens of MCP aggregators and we finally have a winner. Executor is going to become a standard, I can smell it", + }, + { + handle: "davis7", + name: "Ben Davis", + id: "2036226001877999868", + text: "Really like what Rhys is working on with executor. I think it or something like it is probably the future", + }, + { + handle: "BenceRedmond", + name: "Bence Redmond", + id: "2084739965972529153", + text: "The use case for Executor is obvious to anyone who's had to manage MCP servers across Claude Code/Codex/Hermes/etc. Incredibly useful, and resolves so many frustrations that I've had with agent configs over the past few months. The product roadmap is also super exciting!", + }, + { + handle: "ryan_morr", + name: "Ryan Morrissey", + id: "2084728209946665280", + text: 'Executor is awesome and has solved so many problems for us. Most of our prompts for hermes nowadays include the words "use executor" or "can you use executor". One of the few products I find myself recommending to people daily', + }, + { + handle: "johnyeo_", + name: "John Yeo", + id: "2069495395001012464", + text: "executor is the one tool we were looking for to unify our MCP servers across our team, we all use it daily now", + }, + { + handle: "philzona", + name: "Phil Zona", + id: "2074193951377052115", + text: 'Finally trying executor.sh and I get why people like it so much. Setup was extremely simple relative to what it actually does. Its concept of "toolkits" is cool too. Very very impressed so far', + }, + { + handle: "kaeden_dev", + name: "Kaeden", + id: "2069516381381599593", + text: "I was thinking about building some custom Effect-based MCPs like you did with YNAB, until I realized that Executor already did everything I wanted. It's been incredibly useful.", + }, + { + handle: "shuv1337", + name: "shuv", + id: "2099997871403876730", + text: "executor is so good. i have limited toolkits deployed to my whole team via grok bot enterprise, plus a much more robust set of tools for myself/my clankers. it's incredibly useful", + }, + { + handle: "sergical", + name: "Serge", + id: "2090812289427386851", + text: "very much enjoying Executor. configure different connections for the same MCP: I have a work and a personal Cloudflare account so I don't need to re-auth into different ones. manage all the connections between different clients. 1Password to manage secrets instead of storing them in plain text. really well done", + }, + { + handle: "kr0der", + name: "Anthony Kroeger", + id: "2070191407722361022", + text: "wait executor might be one of the best things ever made for AI agents", + }, + { + handle: "KyleFinken", + name: "Kyle Finken", + id: "2063665744718758055", + text: "Incredibly surprised i haven't seen more people talking about poke + executor. By far the best experience for personal agent integrations rn", + }, + { + handle: "somto_odera", + name: "Somto", + id: "2099100010558226582", + text: "Moved all of my MCP tooling to executor.sh - self deployed and tbh, its delightful not worrying about setup on my local or VM - one interface and everything is available", + }, + { + handle: "zaherg", + name: "Zaher", + id: "2068721541664170003", + text: "I can't express how much I enjoy having Executor added to my toolset. there are many projects, but this one is something I can see myself keep using", + }, + { + handle: "andrelandgraf", + name: "Andre Landgraf", + id: "2052978148912807954", + text: "Def check out Executor! I use it primarily for code mode across a few REST APIs and MCP servers. So nice when you'd otherwise have to run thousands of tool calls across meetup RSVP reviews etc", + }, + { + handle: "JamieBrock19060", + name: "James Brock", + id: "2071491693099331802", + text: "I've installed and configured Executor with my Claude and Codex. The three of us love it. Been able to cull so many tools loading at session launch. App is being updated like crazy. Interesting to see where this goes!", + }, + { + handle: "itschrisjayden", + name: "chris jayden", + id: "2099826643028074541", + text: "I underestimated executor.sh, it's amazing! I run it locally on my mac mini, and my other devices just pull from it, amazing!", + }, + { + handle: "jwwwel", + name: "joel", + id: "2096890755629367713", + text: "damn executor.sh is so goated. i can really bring my favourite mcps to whatever harness without needing to set it up again, so nice!!", + }, + { + handle: "coreyhainesco", + name: "Corey Haines", + id: "2090133127243190284", + text: "Really liking executor.sh to make it easy to port agent integrations across platforms.", + }, + { + handle: "shuv1337", + name: "shuv", + id: "2083436548394193027", + text: "executor.sh is excellent. i have to admit, i spent way too much time forking, integrating and maintaining pieces of it in my own project and hadn't used it 'as is' in a while. it's become very good so i'm back to just running local executor now. great work!", + }, + { + handle: "branalytc", + name: "brandon", + id: "2098659909131022704", + text: "finally setup executor.sh and it's working great! happy to finally have a unified mcp!!", + }, + { + handle: "itsmeblueguy", + name: "blueguy", + id: "2099969058280284299", + text: "And executor.sh is all about MCPs eheh. Btw, been using it and loving :D", + }, + { + handle: "zevtnax", + name: "Mrigank Krishan", + id: "2099696720552854008", + text: "I'm using executor.sh for this - been great so far!", + }, + { + handle: "miaugladiator1", + name: "jan", + id: "2092940116142518517", + text: "can vouch, been using it for like a month and its so peak", + }, + { + handle: "RocketmanSh", + name: "SH", + id: "2092986417651540477", + text: "Yep it's perfect!", + }, + { + handle: "ryan_morr", + name: "Ryan Morrissey", + id: "2083702861465362545", + text: "It's truly a stellar product we use it everyday", + }, + { + handle: "WillUndrll", + name: "Will Undrell", + id: "2069491705636680079", + text: "Congrats bro! I'm a huge fan of Executor, I use it exclusively now and it works a dream!", + }, + { + handle: "IanMitchel1", + name: "Ian", + id: "2088675335403667694", + text: "Been hard with a baby but I've been trying to consciously spend more time using and learning different AI tools. I can't even imagine doing this without Executor. How did you all live like that?", + }, + { + handle: "mteamisloading", + name: "mteam.gwei", + id: "2087307035947724943", + text: "Set up Executor Cloud today to give my cloud agents access to all my accounts from one plane. Great experience! Well done", + }, + { + handle: "jachands", + name: "Jacob Hands", + id: "2075693541523669036", + text: "Btw executor is fantastic", + }, + { + handle: "adamghaida", + name: "adam ghaida", + id: "2082163483395936275", + text: "thanks rhys :)) executor has definitely been incredibly powerful for our users and also a joy here", + }, + { + handle: "Swedish_chef", + name: "Axel", + id: "2082161772514160818", + text: "Executor makes it so incredibly easy for end users", + }, + { + handle: "alexrigler", + name: "Alex Rigler", + id: "2064741056835715141", + text: "Folks should really take a look at Executor. It is awesome and a great codebase too", + }, + { + handle: "grfwings", + name: "Griffin", + id: "2052902747473879245", + text: "Executor is a great tool, please give it a try!", + }, + { + handle: "jeremyosih", + name: "Jeremy Osih", + id: "2043813713195487315", + text: "executor is soo good, thx for everything !", + }, + { + handle: "rmedranollamas", + name: "Ramón Medrano Llamas", + id: "2072032965270360423", + text: "executor may be a masterstroke now that it clicked on me", + }, + { + handle: "sensho", + name: "sensho", + id: "2069504511690080560", + text: "WE all love executor", + }, + { + handle: "georgekontridze", + name: "George Kontridze", + id: "2099406752358043678", + text: "Executor is also great", + }, + { + handle: "zaherg", + name: "Zaher", + id: "2083113232080724033", + text: "all I had to do is to add it as integration to my executor, and now I have access to it as mcp without deploying any MCP", + }, + { + handle: "UltraLinx", + name: "Oliur", + id: "2088533267511218619", + text: "Whilst setting this up I kinda realised a lot of apps don't have MCP gateways, so I ended up having Claude build the MCPs for me and host it on the same VPS as Executor. HUGE time saver. And I can choose which MCPs to share with my team or keep them private.", + }, + { + handle: "capeflow", + name: "Florian Bühringer", + id: "2065232432450949618", + text: "Cloudflare artifacts + executor.sh with agent-native skill authoring. Centralized - shared - instantly updated across the team (as it's behind execute). Works like a charm", + }, + { + handle: "aryasaatvik", + name: "Saatvik", + id: "2073992713255649365", + text: "one of my favorite use cases for executor.sh codemode is great for correlating across multiple sources and tools and you can reuse auth across all agents", + }, + { + handle: "kr0der", + name: "Anthony Kroeger", + id: "2094006573605834870", + text: "we're saved, add this to Claude Code/Cursor ASAP via Executor. both are lacking computer use at the moment so this is super useful since i use both", + }, + { + handle: "samilaa", + name: "Sami Laakkonen", + id: "2070264715822174416", + text: "be me. setting up openclaws for my team. access & integration hell. 1h later demo of Executor from Rhys. problem solved", + }, + { + handle: "MejiasDev", + name: "Jose Mejias", + id: "2094904849099796833", + text: "Pi + Herdr + Collie + Executor is unbeaten. Is not even close.", + }, + { + handle: "dillon_mulroy", + name: "Dillon Mulroy", + id: "2067248377222308290", + text: "self hosted executor - no more managing a bunch of disparate mcp servers", + }, + { + handle: "mynameisyahia", + name: "Yahia Bakour", + id: "2072938289582297219", + text: "Check out Executor! A really novel approach to orchestrating tool calls for agents without blowing up your context window with provider-specific semantics. Feels obvious in hindsight", + }, + { + handle: "0xRaduan", + name: "Raduan Al-Shedivat", + id: "2042547575132160004", + text: "all problems of MCPs are solved with executor", + }, + { + handle: "NathanFlurry", + name: "Nathan Flurry", + id: "2073133993122492900", + text: 'tldr why i\'m excited for executor/integrations: everyone needs a "company brain". everyone thinks memory is the issue. but nobody gets past the integrations phase. tackle integrations first, everything else comes later. and most importantly oss + self-hostable', + }, + { + handle: "flybayer", + name: "Brandon Ravion", + id: "2062225610919788681", + text: "executor.sh to get code mode for any API or MCP", + }, + { + handle: "benapatton", + name: "Ben Patton", + id: "2097672856477839558", + text: "What are we doing people?! Just use executor.sh or find an alternative.", + }, + { + handle: "aidansunbury", + name: "Aidan Sunbury", + id: "2086974526706086293", + text: "We use Executor for MCP access, tailscale aperture for inference access, infisical for secret access", + }, + { + handle: "erikrogne", + name: "Erik Rogne", + id: "2098769030735864132", + text: "Finally. This is a great idea", + }, +]; diff --git a/apps/marketing/src/pages/index.astro b/apps/marketing/src/pages/index.astro index 2184b4ec12..6000f5db44 100644 --- a/apps/marketing/src/pages/index.astro +++ b/apps/marketing/src/pages/index.astro @@ -1,11 +1,11 @@ --- import Layout from "../layouts/Layout.astro"; -import { AnimatedBeamDemo } from "../components/animated-beam-demo"; -import { ContextBloatDemo } from "../components/context-bloat-demo"; // Imported so Vite emits it as a hashed build asset (served from /_astro/...); // the deploy pipeline does not pick up newly added /public files. -import ycBackedBy from "../assets/yc-backed-by.svg?url"; import { getStars, formatStars } from "../lib/github"; +import { setupPrompt } from "../content/site-copy"; +import RailB from "../components/variants/rail-b.astro"; +import DocBody from "../components/variants/doc-body.astro"; const GH = "https://github.com/UsefulSoftwareCo/executor"; // Cloud sign-in. Same origin in prod: the cloud Worker owns executor.sh and @@ -14,42 +14,6 @@ const GH = "https://github.com/UsefulSoftwareCo/executor"; const SIGN_IN = "/api/auth/login"; const stars = await getStars(); -// Copied to the clipboard by the "Set up with your agent" hero CTA. A visitor -// pastes it into their coding agent (Claude, Cursor, ...) and the agent picks -// the right form of Executor, installs it, connects over MCP, and gets a first -// connection working. Keep in sync with the docs "Set up with your agent" -// section (apps/docs/index.mdx). -const setupPrompt = `Help me set up Executor and get my first connection working. - -Executor is an open source integration layer for AI agents: one place to configure every integration (MCP servers, OpenAPI specs, GraphQL APIs) and connect to them over MCP. - -Start by helping me pick the right form to run it in. Chat with me about it rather than jumping straight to a yes/no question, and recommend one. If I just want the fastest path, suggest Executor Cloud (free tier, nothing to install). All forms expose the same functionality, just packaged differently: - -Local (everything stays on my machine): -- Desktop app: a native app for Mac, Windows, and Linux. Best for a regular desktop environment. -- CLI (\`executor\`): best for a headless or server environment. -Both run a local HTTP server as a background service that any MCP client can connect to. - -Hosted (use it from multiple agents, including cloud ones, with nothing running locally): -- Executor Cloud: hosted, generous free tier, sign in and start immediately. -- Self-hosted: a Docker image or a Cloudflare Worker. - -How to think about it: -- Want all your data on your own machine? Go local: the desktop app for a regular environment, the CLI for a headless one. -- Want to use it from multiple agents (including cloud agents like ChatGPT), or not run anything locally? Go hosted: Executor Cloud is the fastest start; the self-hosted Docker or Cloudflare versions give you full control. - -Terms you'll come across: -- Integration: anything you add (an MCP server, an OpenAPI spec, a GraphQL API). -- Connection: one configured instance of an integration. An integration can have many connections, and a connection doesn't have to be authenticated. -- Policy: whether each tool is always allowed, requires approval, or is blocked. Policies start from a sensible default derived from the imported spec (for example, GET requests on an OpenAPI spec are allowed by default). - -Once you know which form I want: -1. Walk me through installing it. -2. Connect Executor to you over MCP. Most MCP clients only load servers at startup, so after adding it I may need to restart the client or open a new chat before the Executor tools appear. Tell me if that's needed and wait for me to do it before continuing. -3. Once the tools are available, help me add my first integration and get one tool working end to end. - -Docs: https://executor.sh/docs -Source (and the place to start if something breaks): https://github.com/UsefulSoftwareCo/executor`; --- @@ -66,688 +30,11 @@ Source (and the place to start if something breaks): https://github.com/UsefulSo >
-
- {/* ─── NAV ─── */} - - - {/* ─── HERO ─── */} -
- -
-
-

- Connect any agent to everything. -

-

- Executor is an MCP gateway. Anything that speaks MCP, like Claude - Code, Cursor, or Codex, points at one endpoint and reaches every - tool you connect. -

-
-
- -
-
- {/* ── CTA: For humans / For agents ── */} -
-
- - - -
- - {/* For humans: sign up or run it yourself */} - - - {/* For agents: hand setup to a coding agent, or read the docs */} -
- - Read docs -
-
- - - {/* Shadow lives here, not in the SVG: WebKit rasterizes SVG - filter regions in at 1x, blurring the badge on mobile. */} - Backed by Y Combinator - -
-
-
- - {/* ─── WEDGE ─── */} -
-
-

- Wiring tools to agents is fiddly, per-client, and easy to get wrong. - Executor makes every tool, from any protocol, look the - same: one name, one input - schema, one output schema, so any agent can call any of them - the same way. -

-
-
- - {/* ─── NO CONTEXT BLOAT (interactive) ─── */} -
-
-
-
Context efficiency
-

- Thousands of tools, no bloat. -

-

- Connect everything you use and Executor still shows the model a - single tool. It searches your catalog and loads a tool's schema - only when the code actually calls it, so the prompt never - balloons. -

-
- - -
-
- - {/* ─── CAPABILITIES ─── */} -
-
-
-
What you get
-

- The model reasons. Executor handles the - rest. -

-
- -
- {/* One tool shape */} -
- 01 -

One tool shape

-

- MCP, OpenAPI, GraphQL, or a custom integration. Under the hood - they all become a tool name, an input schema, and an output - schema. -

-
- - {/* Code-mode */} -
- 02 -

Call it any way

-

- Today it is a code-mode MCP. It could just as well be the - Executor CLI, a one-off script, a gen-UI dashboard, or a reusable - workflow. Same tools, every surface. -

-
- - {/* Trace every call (coming soon) */} -
- Coming soon - 03 -

Trace every call

-

- One place to see every run and tool call. Audit any - decision after the fact. -

-
-
- run_7421 - - 1.42s -
-
- sentry.getIssue - - 184ms -
-
- github.searchCode - - 391ms -
-
- linear.createIssue - - 612ms -
-
-
- - {/* Teams */} -
- 04 -

Set up once, whole team has it

-

- Per-user credentials and shared ones. No onboarding ritual, no - toggling MCPs on and off mid-task. -

-
- - {/* Destructive gating */} -
- 05 -

Destructive actions pull you back in

-

- Executor keeps the semantics it imported: GET vs DELETE for - OpenAPI, destructiveHint for MCP, mutations for GraphQL. Agents - auto-run the safe stuff and ask before the rest. -

-
- - {/* Sandboxed */} -
- 06 -

Sandboxed execution

-

- Tool calls run in an isolated JavaScript sandbox. Secrets are - injected host-side at call time and never enter the sandbox - heap, so the agent and model never see a raw token. -

-
-
-
-
- - {/* ─── FOUNDER NOTE (condensed) ─── */} -
-
-
Why we built it
-
-

- Your agent should be able to reach your company's resources in a - way that isn't scary. Most setups make you choose between locked - down and useless, or wide open and risky. -

-

- Executor doesn't care what you add. Once a tool is in that one - shape, a name and two schemas, you can call it however you want and - the same guardrails apply everywhere. That is the whole idea: make - the safe path the easy path. -

-
-
-
- - {/* ─── CHOOSE YOUR PATH ─── */} -
-
-
-
Get started
-

- Pick your path. -

-
- -
- - - - - - {/* Cloud (recommended: fastest start) */} -
-

- Cloud -

- executor.sh/cloud -

- Hosted Executor. Auth, sync, policies, and your whole team online - in five minutes. Free tier to start. -

- Try Cloud → -

- Looking for self-hosted? → -

-
- - {/* Desktop (native app, local) */} -
-

- Desktop -

- Mac · Windows · Linux -

- A native app that runs entirely on your machine. Your - integrations, credentials, and sessions never leave the device. - MIT licensed. -

- -
- - {/* CLI (headless / servers) */} -
-

- CLI -

- npm i -g executor -

- Run Executor as a background service and drive it from your - terminal. Best for headless and server environments. MIT - licensed. -

- Read the docs → -

- View source → -

-
-
-
-
- - {/* ─── FAQ ─── */} -
-
-
-

FAQ

-
- -
-
- - Where does my code run, and what touches my credentials? - - -
- Tool calls run in an isolated JavaScript sandbox. Credentials are - resolved host-side at call time and injected into the outbound - request only. They never enter the sandbox heap, the code your - agent wrote, the agent, or the model. -
-
- -
- - Can the agent or the model ever see a raw token? - - -
- No. Secrets stay host-side by design. The sandbox calls a tool by - name; Executor attaches the credential to the real request - outside the sandbox, so a token is never present in anything the - model can read. -
-
+
+
+
-
- - What can call Executor? - - -
- Any MCP client (Claude Code, Cursor, Codex, and others), the - Executor CLI, or a native client you drop in. Because tools share - one shape, the calling surface is interchangeable. -
-
- -
- - How does it know what is safe to auto-run? - - -
- Executor preserves the semantics of whatever it imported: GET vs - DELETE for OpenAPI, destructiveHint for MCP, and mutations for - GraphQL. That tells the agent what it can run on its own and what - should pull you back into the loop. -
-
- -
- - Is it open source? Can I self-host? - - -
- Yes. Executor is MIT licensed and built on the SDK we publish to - npm. Run the desktop app locally, self-host the server, or use - the hosted cloud. Same code paths, different deployment. -
-
-
-
-
- - {/* ─── PRICING ─── */} -
-
-
-
Pricing
-

- Start free, pay per member. -

-
- -
- {/* Free */} -
-
- Get started -
-

- Free -

-

For small teams getting started

-
- $0 - / month -
- Start free → -
    - {[ - "Up to 3 members", - "100,000 executions per month", - "Unlimited integrations", - ].map((f) => ( -
  • - {f} -
  • - ))} -
-
- - {/* Team */} -
-
- Recommended -
-

- Team -

-

For growing organizations

-
- $15 - / member / month -
- Start free trial → -
    - {[ - "14-day free trial, then $15 / member / month", - "Unlimited executions", - "Verified domains & join by team domain", - ].map((f) => ( -
  • - {f} -
  • - ))} -
-
- - {/* Enterprise */} -
-
- Custom needs -
-

- Enterprise -

-

For orgs with custom needs

-
- Custom -
- Contact us → -

Everything in Team, plus

-
    - {[ - "Self-hosted or dedicated cloud deployment support", - "SSO / SAML & SCIM provisioning", - "Audit logs for every tool call", - "Dedicated support & onboarding", - "Security reviews, DPA & SOC 2 on request", - ].map((f) => ( -
  • - {f} -
  • - ))} -
-
-
-
-
- - {/* ─── FINAL CTA ─── */} -
-
-

- Connect your agent to - everything. -

- -

- or try Executor Cloud → -

-
-
+ {/* ─── FOOTER ─── */}
@@ -772,6 +59,7 @@ Source (and the place to start if something breaks): https://github.com/UsefulSo
+
@@ -828,115 +116,6 @@ Source (and the place to start if something breaks): https://github.com/UsefulSo } - - - -