Skip to content
Draft
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
172 changes: 30 additions & 142 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,151 +1,39 @@
# Agents
# Agent policy

## Product Vision
Read [PRODUCT.md](PRODUCT.md) before changing service capabilities, protocols,
control-plane behavior, or hosted emulator UX. Release preparation follows
[RELEASING.md](RELEASING.md).

Emulate is a stateful integration test double for real developer APIs. It should help humans and agents stand up fake versions of real services, point applications at them, exercise authenticated flows, inspect what happened, and reset or replay scenarios. Treat it as an integration simulator platform for developer and agent-driven testing, not as a generic mock server.
## Tooling

The guiding principle is: emulate what the real service exposes, not every protocol the platform can technically support. A service should only expose the surfaces that match the real provider or an intentionally documented integration target. GitHub can have REST, GraphQL, OAuth app flows, GitHub App auth, webhooks, and a deliberate MCP surface. Spotify should model its real OAuth and catalog behavior, such as client credentials flows, and should not get GraphQL or MCP just because the platform supports those protocols.
- Use `pnpm` for repository package commands. End-user installation examples
use npm because it is the universal interface.
- In user-facing CLI examples, invoke the package as `npx emulate`; `emulate`
is a zsh shell built-in. It may appear bare only when another process resolves
it as a subprocess executable.
- When adding a dependency, verify and install the current npm release with
`pnpm add <package>` or `npm view <package> version`.
- Do not use emojis. Avoid dash punctuation in prose; rephrase with commas,
parentheses, or separate sentences. Double hyphens are reserved for CLI
flags.

Specifications are inputs, not the whole product. OpenAPI, GraphQL schemas or introspection results, MCP manifests, Google Discovery documents, OAuth metadata, and hand-authored behavior packs can all contribute to a service definition. Use subsets of those specs when that is the honest supported surface. A curated subset with clear coverage is better than a broad fake that quietly lies.
## Emulator UI

Every service should move toward exporting a machine-readable service manifest that describes:
Build every emulator page with the shared design system in
`packages/@emulators/core/src/ui.ts`. Use the existing render functions and CSS
classes; never add package-local HTML documents or custom `<style>` blocks.

- Service identity, display name, and short description
- Supported surfaces: REST, OAuth/OIDC, GraphQL, MCP, webhooks, redirect UIs, provider-specific auth, and provider-specific SDK semantics
- Spec sources and coverage status, including generated, hand-authored, partial, and unsupported operations
- Auth capabilities, grant types, dynamic client registration support, API keys, bearer tokens, app installations, webhook secrets, and other credential types
- Seed schema, available scenarios, reset behavior, and state model
- Inspector tabs, request ledger capabilities, and links to human docs
- Copyable connection details for SDK, CLI, and application usage
Available page primitives include `renderCardPage`, `renderErrorPage`,
`renderSettingsPage`, `renderInspectorPage`, `renderFormPostPage`, and
`renderUserButton`. Add a reusable primitive to core when these cannot express
a legitimate new page type.

Every hosted emulator should be useful without repository context. A human or agent landing on a service host such as `github.emulators.dev` or an instance host such as `github.my-run.emulators.dev` should be able to learn what the service is, create or select an instance, create credentials, seed state, find base URLs, inspect calls, and copy SDK, CLI, or application snippets.
## Documentation and releases

Prefer host-based routing for deployed emulators when possible. The long-term product shape should support service and instance hosts such as:
When commands, flags, routes, seed configuration, SDK integration, or user
behavior changes, update the relevant surfaces together: `README.md`, service
skills, `apps/web/`, and CLI help.

```text
https://github.emulators.dev
https://github.my-test-run.emulators.dev
https://stripe.ci-48291.emulators.dev
```

Provider traffic and control-plane traffic should be separate. Provider routes should remain faithful to the real service. Emulate-specific controls should live under a reserved namespace such as `/_emulate`.

The standard control plane should move toward routes like:

```text
GET /_emulate
GET /_emulate/manifest
GET /_emulate/quickstart
GET /_emulate/openapi
GET /_emulate/graphql
GET /_emulate/mcp
POST /_emulate/instances
POST /_emulate/seed
POST /_emulate/reset
POST /_emulate/credentials
GET /_emulate/state
GET /_emulate/ledger
GET /_emulate/logs
```

The request ledger is a core feature, not a debug afterthought. Emulators should record enough information for people and tests to validate how applications called a service: timestamp, method, host, path, matched route or operation id, sanitized headers, sanitized request body, authenticated identity, response status, response summary, side effects, webhook deliveries, and a correlation id.

The remote host should plug naturally into external SDKs, CLIs, agents, and applications. A user should be able to create an instance, generate credentials, and copy ready-to-use connection configuration without knowing the internals of this repository.

OpenAPI generation should be treated as a skeleton generator and fallback layer. It can create route tables, validators, default fake responses, documentation, and coverage reports. Hand-authored behavior should override generated behavior for stateful flows, auth flows, provider-specific semantics, webhooks, and important edge cases.

When making product or architecture decisions, optimize for:

- Deployed use by applications under test
- Local and in-process use by automated tests
- Human-readable landing pages and inspectors
- Agent-readable manifests, quickstarts, and ledgers
- Faithful auth and credential flows
- Honest support boundaries and visible coverage
- Shared definitions across local, hosted, and SDK surfaces

## Package Manager

Use `pnpm` for all package management commands (not npm or yarn).

Exception: End-user install instructions should use `npm` (e.g. `npx emulate`, `npm install emulate`) since npm is universal.

## CLI Invocation

`emulate` is a zsh built-in command (it sets shell emulation mode). Running bare `emulate` in zsh invokes the shell built-in, not the npm binary. Always use `npx emulate` in user-facing CLI examples, docs, skills, help output, and post-command messages. The only exception is when `emulate` appears as a subprocess argument to another tool (e.g. `portless github.emulate emulate start`), where the binary is resolved by the parent process rather than the shell.

## Dependencies

Always check for the latest npm version when adding dependencies. Use `pnpm add <package>` (without version) to get the latest, or verify with `npm view <package> version` first.

## No Emojis

Do not use emojis anywhere in this repository (code, comments, output, docs).

## Dashes

Never use `--` as a dash in prose, comments, or user-facing output. Use an em dash (\u2014) when a dash is needed, but prefer rephrasing to avoid dashes entirely. The only exception is CLI flags (e.g. `--port`).

## Emulator UI Design System

All emulator UIs (inspector pages, OAuth flows, checkout pages, inboxes, etc.) must use the shared design system in `packages/@emulators/core/src/ui.ts`. Never write inline HTML with custom `<style>` tags or standalone `<!DOCTYPE html>` templates in individual emulator packages.

Use the appropriate shared render function for each page type:

- `renderCardPage` for centered card layouts (OAuth sign-in, email detail, checkout)
- `renderErrorPage` for error states
- `renderSettingsPage` for sidebar + main content layouts (OAuth app settings, Slack inspector)
- `renderInspectorPage` for tabbed data dashboards (AWS inspector)
- `renderFormPostPage` for OAuth `form_post` auto-submit redirects
- `renderUserButton` for user selection buttons in OAuth flows

These functions provide the shared `head()` (Geist fonts, favicon, CSS), `emuBar()` header, and "Powered by emulate" footer automatically. Use the existing CSS classes (`.inspector-table`, `.s-card`, `.org-row`, `.badge`, `.empty`, etc.) rather than adding inline styles.

If a new page type cannot be built with the existing render functions and CSS classes, add the new styles and render function to `core/src/ui.ts` so every emulator can reuse them.

## Docs Updates

When a change affects how humans or agents use emulate (new/changed/removed commands, flags, behavior, routes, seed config, or SDK integration), update all of these:

1. `README.md`
2. `skills/*/SKILL.md` (agent skills for each service)
3. `apps/web/` (docs site pages)
4. CLI `--help` output in `packages/emulate/src/index.ts`

## Releasing

Releases are manual, single-PR affairs. The maintainer controls the changelog voice and format. All packages share a single version number (`emulate` + every `@emulators/*`).

To prepare a release:

1. Create a branch (e.g. `prepare-v0.5.0`)
2. Bump the version in `packages/emulate/package.json`
3. Run `pnpm sync-versions` to update all `@emulators/*` packages
4. Write the changelog entry in `CHANGELOG.md`, wrapped in `<!-- release:start -->` and `<!-- release:end -->` markers
5. Remove the `<!-- release:start -->` and `<!-- release:end -->` markers from the previous release entry (only the latest release should have markers)
6. Open a PR and merge to `main`

CI compares the version in `packages/emulate/package.json` to what's on npm. If it differs, it builds, publishes all packages with provenance, and creates the GitHub release automatically. The release body is extracted from the content between the markers.

<!-- opensrc:start -->

## Source Code Reference

Source code for dependencies is available in `opensrc/` for deeper understanding of implementation details.

See `opensrc/sources.json` for the list of available packages and their versions.

Use this source code when you need to understand how a package works internally, not just its types/interface.

### Fetching Additional Source Code

To fetch source code for a package or repository you need to understand, run:

```bash
npx opensrc <package> # npm package (e.g., npx opensrc zod)
npx opensrc pypi:<package> # Python package (e.g., npx opensrc pypi:requests)
npx opensrc crates:<package> # Rust crate (e.g., npx opensrc crates:serde)
npx opensrc <owner>/<repo> # GitHub repo (e.g., npx opensrc vercel/ai)
```

<!-- opensrc:end -->
Never merge a release or any other change without explicit maintainer approval
for that merge. Preparing a branch and PR is allowed; publishing remains a
maintainer-controlled action.
59 changes: 59 additions & 0 deletions PRODUCT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Product direction

Emulate is a stateful integration simulator for real developer APIs. It lets
humans and agents stand up service-shaped environments, authenticate, exercise
flows, inspect calls and side effects, and reset or replay scenarios. It is not
a generic mock server.

## Fidelity

Model what a real provider exposes, not every protocol the platform can
technically host. GitHub can legitimately include REST, GraphQL, OAuth,
GitHub App authentication, webhooks, and a deliberate MCP surface. A provider
without GraphQL or MCP should not acquire those surfaces merely for symmetry.

Specifications are inputs rather than the complete product. OpenAPI, GraphQL
schemas, MCP manifests, discovery documents, OAuth metadata, and hand-authored
behavior packs may each contribute. Honest curated coverage is better than a
broad emulator that quietly invents unsupported semantics.

## Service manifest

Each service should expose a machine-readable manifest describing:

- identity and purpose
- supported protocols and provider-specific behavior
- source specifications and coverage status
- authentication and credential capabilities
- seed schema, scenarios, reset behavior, and state model
- inspector and request-ledger capabilities
- copyable connection details for SDKs, CLIs, agents, and applications

Hosted emulators must be understandable without repository context. A visitor
should be able to create or select an instance, issue credentials, seed state,
find endpoints, inspect calls, and copy connection examples.

## Routing and control plane

Prefer service and instance hosts such as `github.emulators.dev` and
`github.my-test-run.emulators.dev`. Provider traffic must remain faithful to the
real service. Emulate controls belong under the reserved `/_emulate` namespace.

The common control plane should converge on discoverable manifest, quickstart,
specification, instance, seed, reset, credential, state, ledger, and log routes.

## Ledger and behavior

The request ledger is a core contract. Record enough sanitized information for
tests and people to understand the request, authenticated identity, matched
operation, response, side effects, webhook deliveries, timing, and correlation.

OpenAPI generation provides route skeletons, validators, baseline responses,
documentation, and coverage reporting. Hand-authored behavior overrides it for
stateful workflows, authentication, provider-specific semantics, webhooks, and
important edge cases.

Optimize decisions for deployed application testing, local automated tests,
human-readable inspectors, agent-readable manifests, faithful credentials,
honest support boundaries, and shared definitions across hosted and in-process
surfaces.
19 changes: 19 additions & 0 deletions RELEASING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Releasing

Releases are manual, single-PR changes. The maintainer owns the changelog voice,
merge decision, and publication timing. All packages share one version.

1. Create a `prepare-v<version>` branch.
2. Update `packages/emulate/package.json`.
3. Run `pnpm sync-versions`.
4. Add the changelog entry between `<!-- release:start -->` and
`<!-- release:end -->` markers.
5. Remove those markers from the previous release so only the newest entry is
selected.
6. Run the repository verification and open a PR.
7. Wait for explicit maintainer approval before merging.

After an approved merge, CI detects the npm version change, builds and publishes
all packages with provenance, creates the GitHub release, and uses the marked
changelog content as the release body. Monitor the workflow and verify the
published packages before reporting completion.
Loading