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
33 changes: 22 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,29 +1,35 @@
# Wingman

Hosted single-page playground for testing a [Manifest](https://manifest.build) gateway. Impersonate any of the agents Manifest tracks β€” **OpenClaw**, **Hermes**, **OpenAI SDK**, **Vercel AI SDK**, **LangChain**, plain **cURL**, or a raw fetch β€” and inspect the exact request / response that lands on your backend.
Hosted single-page playground for testing LLM APIs straight from the browser. Pick a **wire format** β€” OpenAI **Chat Completions**, OpenAI **Responses**, or **Anthropic Messages** β€” paste a base URL + key, and call any provider that speaks it (Manifest, OpenAI, Anthropic, Together, Fireworks, Groq, DeepSeek, Z.AI, MiniMax, …). Stream the reply token by token or fetch it whole, and inspect the exact request / response on the wire.

It started as a [Manifest](https://manifest.build) gateway tester, so it also impersonates the agents Manifest tracks β€” **OpenClaw**, **Hermes**, **OpenAI SDK**, **Vercel AI SDK**, **LangChain**, plain **cURL**, or a raw fetch β€” to show how the proxy classifies each client.

Live at **<https://wingman.manifest.build>**.

## What it's for

- Verifying routing decisions (which tier did Manifest pick for this prompt?).
- Inspecting how the proxy classifies different SDKs from their User-Agent / `X-Stainless-*` headers.
- Calling any LLM provider by URL + key + format β€” no SDK install, no terminal.
- Comparing the same prompt across formats (Chat Completions vs Responses) or across providers.
- Watching a streamed response arrive token by token, with time-to-first-token.
- Verifying Manifest routing decisions (which tier did it pick for this prompt?).
- Inspecting how a proxy classifies different SDKs from their User-Agent / `X-Stainless-*` headers.
- Reproducing a customer report end-to-end without touching the real CLI.
- Onboarding contributors who want to see what an OpenClaw or Hermes request actually looks like.

## How it works (and what it doesn't do)

Wingman is a **static SPA**. It has no backend of its own β€” every request goes straight from your browser to whatever Manifest gateway you configure in the connection bar. There is no telemetry, no server-side logging, no proxy in between.
Wingman is a **static SPA**. It has no backend of its own β€” every request goes straight from your browser to whatever endpoint you configure in the connection bar. There is no telemetry, no server-side logging, no proxy in between. Streaming responses are read directly from the `fetch` body as Server-Sent Events.

Your API key is held in `sessionStorage` (cleared when you close the tab). Everything else β€” base URL, model, history, system prompts β€” is in `localStorage` and never leaves the browser. The full source is in this repo if you want to audit it.

## Connecting to a gateway

Open <https://wingman.manifest.build>, paste:
Open <https://wingman.manifest.build>, then:

- **Base URL** β€” e.g. `https://your-manifest.example.com` or `http://localhost:3001` (more on cross-origin below).
- **API key** β€” your `mnfst_*` token.
- **Format** β€” the wire protocol: OpenAI Chat Completions (`/v1/chat/completions`), OpenAI Responses (`/v1/responses`), or Anthropic Messages (`/v1/messages`). This sets the endpoint path, auth scheme, body shape, and how the response is parsed.
- **Base URL** β€” e.g. `https://your-manifest.example.com`, `https://api.openai.com`, `https://api.anthropic.com`, or `http://localhost:3001` (more on cross-origin below). Wingman appends the format's path.
- **API key** β€” `Authorization: Bearer` for OpenAI-style formats, `x-api-key` for Anthropic (attached automatically per format).
- **Model** β€” `auto` or a specific model id.
- **Stream** β€” toggle in the composer toolbar to read the reply as it's generated (Server-Sent Events).

You can pre-fill via query string: `?baseUrl=https://your.gateway&apiKey=mnfst_...`. The Manifest dashboard's Wingman drawer does this automatically.

Expand All @@ -42,6 +48,8 @@ The `X-Stainless-*` headers matter: the OpenClaw, Hermes, and OpenAI SDK profile

If the gateway is on a loopback address (`localhost` / `127.0.0.1`) and you load Wingman over HTTPS, Chrome's Private Network Access also wants `Access-Control-Allow-Private-Network: true` on the preflight.

Calling **Anthropic** directly: its API blocks browser origins by default, so Wingman sends the `anthropic-dangerous-direct-browser-access: true` header (alongside `anthropic-version`) automatically when you pick the Anthropic Messages format.

`Access-Control-Allow-Credentials` can stay **false** β€” Wingman uses bearer keys, never cookies.

If you're behind a corporate firewall or running a fully air-gapped Manifest, clone this repo and `npm run dev` β€” Wingman runs entirely client-side.
Expand Down Expand Up @@ -69,9 +77,12 @@ Scripts:

## How it's wired

- **`src/profiles.ts`** β€” catalog of every supported agent/SDK shape. Adding a new one means adding one entry: headers, system prompt, body builder, code snippet builder. The UI picks up the new tile automatically.
- **`src/send.ts`** β€” single fetch wrapper that captures status, latency, request/response headers, and parses JSON when possible. Filters out forbidden headers (`User-Agent`, `Sec-*`, `Cookie`, etc.) that browsers refuse to set on fetch and surfaces them in the UI.
- **`src/App.tsx`** β€” composes the layout: connection bar β†’ profile tiles β†’ form β†’ header editor β†’ SDK code preview β†’ response panel.
- **`src/formats/`** β€” one module per wire format (`openai-chat`, `openai-responses`, `anthropic-messages`). Each owns its endpoint path, auth scheme, body builder, response parsers, and streaming parser. Adding a provider format means adding one file and listing it in `index.ts`.
- **`src/profiles.ts`** β€” catalog of agent/SDK fingerprints layered on a format: headers, system prompt, optional body extras, code snippet. Each profile declares which formats it's compatible with; the UI filters the list to the selected format.
- **`src/snippets.ts`** β€” format-aware SDK / cURL code-snippet builders for the preview panel.
- **`src/send.ts`** β€” fetch wrapper that captures status, latency, request/response headers, and parses JSON. `sendRequestStreaming` reads the SSE body and assembles the text via the format's stream parser. Filters out forbidden headers (`User-Agent`, `Sec-*`, `Cookie`, …) that browsers refuse to set on fetch and surfaces them in the UI.
- **`src/services/sse.ts`** β€” generic Server-Sent Events reader (decodes the stream, splits events).
- **`src/App.tsx`** β€” composes the layout: format + client pickers β†’ connection bar β†’ form β†’ header editor β†’ SDK code preview β†’ response panel.

## Caveats

Expand Down
4 changes: 2 additions & 2 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Manifest Wingman β€” Gateway Tester</title>
<title>Manifest Wingman β€” LLM API Tester</title>
<meta name="theme-color" content="#1a1d23" media="(prefers-color-scheme: dark)" />
<meta name="theme-color" content="#faf8f5" />
<meta
name="description"
content="Send test messages to a Manifest gateway, impersonating OpenClaw, Hermes, OpenAI SDK, Vercel AI SDK, LangChain, or cURL."
content="Test any LLM API from the browser β€” OpenAI Chat Completions & Responses, Anthropic Messages, and every OpenAI-compatible provider. Streaming or buffered, with full request/response inspection."
/>
</head>
<body>
Expand Down
1 change: 1 addition & 0 deletions public/icons/providers/anthropic.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading