Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 46 additions & 9 deletions .claude/skills/emulate/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,18 @@ description: Use the @executor-js/emulate service emulators (GitHub, Google, Str

# Emulate: production-fidelity service emulators

`@executor-js/emulate` (our fork of Vercel Labs' emulate, developed in
`vendor/emulate` but ALWAYS consumed as the published npm package — never
import from `vendor/`) provides stateful, wire-level emulators for 16
services: `github vercel google okta microsoft spotify slack apple aws
resend stripe mongoatlas clerk x workos autumn`. These are not mocks: real
SDKs and real product code run against them unmodified — the cloud e2e
target points the actual WorkOS SDK (sealed sessions, JWKS, hosted AuthKit
login) and Autumn billing at emulators and exercises the product's real
auth code.
`@executor-js/emulate` (our fork of Vercel Labs' emulate) provides stateful,
wire-level emulators for 16 services: `github vercel google okta microsoft
spotify slack apple aws resend stripe mongoatlas clerk x workos autumn`.
These are not mocks: real SDKs and real product code run against them
unmodified — the cloud e2e target points the actual WorkOS SDK (sealed
sessions, JWKS, hosted AuthKit login) and Autumn billing at emulators and
exercises the product's real auth code.

**This repo only consumes the published npm package.** There is no
`vendor/emulate` submodule — everything imports `@executor-js/emulate` from
npm. The emulator _source_ is its own standalone project; to change or deploy
an emulator, see "Changing or deploying an emulator" at the bottom.

## Two ways to get one

Expand Down Expand Up @@ -83,6 +86,40 @@ before `createEmulator` so minted MCP access tokens carry the right audience.
app's real dev stack (recipe in `e2e/setup/cloud.boot.ts`), fronted with
tailscale HTTPS.

## Changing or deploying an emulator

The emulators live in their own repo — **not** in executor and **not** a
submodule: `github.com/UsefulSoftwareCo/emulate` (clone it as a sibling of
executor). It's a pnpm + turbo monorepo on node ≥ 24 — a different toolchain
from executor's bun, which is exactly why it's standalone rather than
vendored here.

You have full autonomy over it: **work directly on `main`, and commit, push,
publish, and deploy without asking.** The loop:

1. Edit in the emulate repo, then **rebuild the package you changed.** It
resolves through built `dist`, so a source-only edit does nothing until you
build it — this is the single most common mistake. `@emulators/*` packages
are a `workspace:*` graph, so adding or renaming one needs an install +
rebuild too.
2. Publish a version bump of `@executor-js/emulate` to npm.
3. Deploy the hosted Cloudflare emulators (the `emulate-hosts` worker behind
`*.emulators.dev`) when behavior the hosted instances serve has changed.
4. Back in executor, bump the `@executor-js/emulate` dependency to the version
you just published. Never point a consumer at a local checkout to ship —
publish, then bump.

The emulate repo's own `AGENTS.md` / `README.md` carry the current build,
publish, and deploy commands (npm + Cloudflare creds are in 1Password). Read
them there rather than memorizing flags here — they move.

**A hot deploy can redden other people's e2e.** `*.emulators.dev` service
hosts are shared infrastructure; a control-plane regression there has failed
unrelated PRs' suites before. When a scenario needs isolation or a behavior
that isn't deployed yet, pin to a published package version or mint a private
per-run instance (`POST /_emulate/instances`) instead of mutating the shared
service host.

## Gotchas

- **Secure cookies need HTTPS off-localhost.** Browser-driven flows work on
Expand Down
9 changes: 0 additions & 9 deletions .gitmodules

This file was deleted.

1 change: 0 additions & 1 deletion .oxfmtrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
"dist",
"vendor",
"e2e/runs",
"integrationsdotsh",
"node_modules",
"packages/core/fumadb",
"bun.lock",
Expand Down
1 change: 0 additions & 1 deletion .oxlintrc.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,6 @@
"vendor/",
"emulators/",
"e2e/runs/",
"integrationsdotsh/",
"node_modules/",
"packages/core/fumadb/",
"packages/core/sdk/src/vendor/json-schema-to-typescript/",
Expand Down
6 changes: 6 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ with zero setup. See the `emulate` skill
(`.claude/skills/emulate/SKILL.md`) for the control-plane reference and
recipes.

The emulators are a standalone project (`github.com/UsefulSoftwareCo/emulate`),
not vendored here — this repo only consumes the published `@executor-js/emulate`
package. You have full autonomy to change, publish, and deploy the emulators,
working directly on their `main`; the skill covers the loop. Don't re-introduce
a `vendor/` submodule for them.

## Attribution

Do not add any AI assistant, Claude, Anthropic, or Co-Authored-By
Expand Down
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,7 @@ bun run test:e2e # full-stack e2e: boots the cloud and self-host apps and driv
```

The browser e2e scenarios need Playwright's Chromium once per machine:
`bunx playwright install chromium`. The git submodules under `vendor/` are
optional — see [vendor/README.md](vendor/README.md).
`bunx playwright install chromium`.

## Community

Expand Down
12 changes: 7 additions & 5 deletions RUNNING.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@ artifacts dev servers fail without) plus Playwright chromium. A fresh
worktree that skips it dies with "Failed to resolve entry for package
'@executor-js/vite-plugin'".

The `vendor/` submodules (emulate, mcporter) are NOT required — nothing
imports from `vendor/` at runtime; those packages come from npm
(`@executor-js/emulate`, `@executor-js/mcporter`). `bun run bootstrap
--forks` inits them only when deliberately developing a fork (see
`vendor/README.md`).
Our two upstream forks — `@executor-js/emulate` (service emulators) and
`@executor-js/mcporter` (headless MCP client) — are consumed purely as
published npm packages; nothing in this repo references them by path. There
are no `vendor/` submodules. Each fork is its own standalone repo
(`github.com/UsefulSoftwareCo/emulate`, `github.com/UsefulSoftwareCo/mcporter`):
develop on its `main`, publish a bump, then bump the dependency here. The
`emulate` skill covers the emulator publish/deploy loop.

## Dev servers

Expand Down
7 changes: 4 additions & 3 deletions e2e/src/surfaces/mcp.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// MCP surface: our mcporter fork (@executor-js/mcporter on npm; develop it in
// the vendor/mcporter submodule) as a programmatic MCP client, with headless
// OAuth via the target's consent strategy. Session methods are Effects;
// MCP surface: our mcporter fork (@executor-js/mcporter on npm; developed in
// its own repo, github.com/UsefulSoftwareCo/mcporter) as a programmatic MCP
// client, with headless OAuth via the target's consent strategy. Session
// methods are Effects;
// mcporter itself is promise-native underneath. Assertions are vitest's job.
import { createHash, randomBytes, randomUUID } from "node:crypto";
import { mkdtempSync, writeFileSync } from "node:fs";
Expand Down
11 changes: 3 additions & 8 deletions scripts/bootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@
// Playwright browser the e2e suite drives. Idempotent and safe to re-run;
// each step prints what it is doing.
//
// The vendor/ submodules are intentionally NOT initialized: nothing imports
// from vendor/ at runtime — those forks are consumed as published npm
// packages (see vendor/README.md). Pass --forks only when deliberately
// developing a fork.
// There are no fork submodules: our upstream forks (@executor-js/emulate,
// @executor-js/mcporter) are consumed purely as published npm packages and
// developed in their own standalone repos. Nothing to init here.
import { execFileSync } from "node:child_process";
import { existsSync } from "node:fs";
import { resolve } from "node:path";
Expand All @@ -19,10 +18,6 @@ const run = (label: string, cmd: string, args: ReadonlyArray<string>) => {
execFileSync(cmd, [...args], { cwd: repoRoot, stdio: "inherit" });
};

if (process.argv.includes("--forks")) {
run("vendor fork submodules", "git", ["submodule", "update", "--init", "--recursive"]);
}

// `bun install` runs the workspace prepare hook, which builds
// @executor-js/vite-plugin and @executor-js/react — the two artifacts the
// apps' vite dev servers fail without in a fresh worktree.
Expand Down
1 change: 0 additions & 1 deletion scripts/migrate-schema-class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ const IGNORE_DIRS = new Set([
"build",
".local",
".changeset",
"integrationsdotsh",
]);

// Files we intentionally leave alone — they contain `Schema.Class` strings as
Expand Down
34 changes: 0 additions & 34 deletions vendor/README.md

This file was deleted.

1 change: 0 additions & 1 deletion vendor/emulate
Submodule emulate deleted from 894206
1 change: 0 additions & 1 deletion vendor/mcporter
Submodule mcporter deleted from 8f3e44
Loading