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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Your API key is held in `sessionStorage` (cleared when you close the tab). Every

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

- **Provider preset** — pick a provider (OpenAI, Anthropic, Mistral, Groq, …) to auto-fill its base URL and wire format, or stay on **Custom / Manifest** to point at your own gateway. Editing the Base URL by hand switches back to Custom.
- **Provider preset** — pick a provider (OpenAI, Anthropic, Mistral, Groq, …) to auto-fill its base URL and wire format. The default, **Custom / Manifest**, pre-fills the Manifest Cloud gateway (`https://app.manifest.build`) — edit the Base URL by hand to point at your own gateway (which switches the pill back to Custom).
- **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).
Expand Down
20 changes: 11 additions & 9 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
PROVIDER_BY_ID,
DEFAULT_PROVIDER_ID,
CUSTOM_PROVIDER,
MANIFEST_BASE_URL,
type Provider,
} from './providers';
import { partitionHeaders, sendRequest, sendRequestStreaming, type SendResult } from './send';
Expand Down Expand Up @@ -97,17 +98,17 @@ function recordFromEntries(entries: HeaderEntry[]): Record<string, string> {
}

function defaultBaseUrl(): string {
// Hosted Wingman has no implicit backend — leave the field empty so the
// user must paste a URL or follow the ?baseUrl= query param. Pre-filling a
// localhost guess on wingman.manifest.build would only produce confusing
// CORS errors.
if (typeof window === 'undefined') return '';
// Wingman is a Manifest gateway tester first, so default the Base URL to the
// canonical Manifest Cloud gateway — the user shouldn't have to type it. On
// localhost we instead guess the local gateway port so `npm run dev` targets
// a locally-running Manifest. The health badge surfaces reachability / CORS.
if (typeof window === 'undefined') return MANIFEST_BASE_URL;
const { protocol, hostname, port } = window.location;
if (hostname === 'localhost' || hostname === '127.0.0.1') {
if (port === '3002' || port === '3000') return `${protocol}//${hostname}:3001`;
return `${protocol}//${hostname}:${port || '3001'}`;
}
return '';
return MANIFEST_BASE_URL;
}

// An external embed (?baseUrl=…, e.g. the Manifest dashboard drawer) is always
Expand Down Expand Up @@ -316,16 +317,17 @@ const App: Component = () => {
};

// Pick a provider preset: switch to its wire format and, for a concrete
// provider, fill the base URL + a usable default model. Selecting "Custom"
// resets to Manifest/BYO defaults so the user can type their own endpoint.
// provider, fill the base URL + a usable default model. Selecting "Custom /
// Manifest" resets to the Manifest gateway defaults (base URL pre-filled, the
// `auto` router model) — the field stays free-text for a BYO endpoint.
const selectProvider = (id: string) => {
const preset = PROVIDER_BY_ID[id];
if (!preset || id === providerId()) return;
setProviderId(id);
writeStorage(STORAGE.provider, id);
setFormatSafely(preset.formatId);
if (preset.id === DEFAULT_PROVIDER_ID) {
persistAndSetBase('');
persistAndSetBase(defaultBaseUrl());
persistAndSetModel('auto');
} else {
persistAndSetBase(preset.baseUrl);
Expand Down
10 changes: 10 additions & 0 deletions src/providers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,16 @@ export interface Provider {
/** The default preset: today's behaviour (free-text base URL → Manifest). */
export const DEFAULT_PROVIDER_ID = 'custom';

/**
* Canonical Manifest Cloud gateway. Pre-filled as the default Base URL on the
* hosted app so the primary use case — testing a Manifest gateway — works
* without typing (or mistyping) the host. Note it's `app.manifest.build`, not
* `api.manifest.build`: the latter has no TLS cert. This host speaks the
* OpenAI/Anthropic-compatible wire format (`{host}/v1/chat/completions`,
* `{host}/v1/messages`). Source: manifest.build llms.txt.
*/
export const MANIFEST_BASE_URL = 'https://app.manifest.build';

export const PROVIDERS: readonly Provider[] = [
{
id: 'custom',
Expand Down
Loading