Skip to content
Open
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
18 changes: 9 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ Oracle chooses API mode when an OpenAI key is available and browser mode otherwi
| Browser | You want Oracle to use a signed-in ChatGPT or Gemini browser session. | Install Chrome and complete the one-time login flow. |
| Render | You want to inspect, copy, or paste the bundle yourself. | No account or key is required. |

API mode supports OpenAI, Azure OpenAI, Anthropic, Gemini, xAI, OpenRouter, and compatible endpoints. Browser mode uses Chrome automation for ChatGPT and a cookie-based Gemini client. See [browser mode](docs/browser-mode.md) and [provider endpoints](docs/openai-endpoints.md) for setup and limits.
API mode supports OpenAI, Azure OpenAI, Anthropic, Gemini, xAI, OpenRouter, OrcaRouter, and compatible endpoints. Browser mode uses Chrome automation for ChatGPT and a cookie-based Gemini client. See [browser mode](docs/browser-mode.md) and [provider endpoints](docs/openai-endpoints.md) for setup and limits.

## Control the context

Expand Down Expand Up @@ -102,14 +102,14 @@ For agent integrations, run the `oracle-mcp` stdio server or install the Oracle

## Documentation

| Topic | Guide |
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| Installation and first run | [Install](docs/install.md) · [Quickstart](docs/quickstart.md) |
| Browser automation | [Browser mode](docs/browser-mode.md) · [Linux](docs/linux.md) · [Windows](docs/windows.md) |
| Providers | [OpenAI and Azure](docs/openai-endpoints.md) · [Anthropic](docs/anthropic.md) · [Gemini](docs/gemini.md) · [OpenRouter](docs/openrouter.md) |
| Runs and models | [Sessions](docs/sessions.md) · [Follow-ups](docs/followup.md) · [Multi-model](docs/multimodel.md) |
| Configuration and commands | [Configuration](docs/configuration.md) · [CLI reference](docs/cli-reference.md) |
| Agent integrations | [Agents](docs/agents.md) · [MCP](docs/mcp.md) · [Bridge](docs/bridge.md) |
| Topic | Guide |
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Installation and first run | [Install](docs/install.md) · [Quickstart](docs/quickstart.md) |
| Browser automation | [Browser mode](docs/browser-mode.md) · [Linux](docs/linux.md) · [Windows](docs/windows.md) |
| Providers | [OpenAI and Azure](docs/openai-endpoints.md) · [Anthropic](docs/anthropic.md) · [Gemini](docs/gemini.md) · [OpenRouter](docs/openrouter.md) · [OrcaRouter](docs/orcarouter.md) |
| Runs and models | [Sessions](docs/sessions.md) · [Follow-ups](docs/followup.md) · [Multi-model](docs/multimodel.md) |
| Configuration and commands | [Configuration](docs/configuration.md) · [CLI reference](docs/cli-reference.md) |
| Agent integrations | [Agents](docs/agents.md) · [MCP](docs/mcp.md) · [Bridge](docs/bridge.md) |

## Related projects

Expand Down
2 changes: 1 addition & 1 deletion docs/cli-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ Notes:
| `--azure-deployment` | Azure deployment name. |
| `--azure-api-version` | Azure API version. |

See [OpenAI / Azure / OpenRouter](openai-endpoints.md) and [OpenRouter](openrouter.md).
See [OpenAI / Azure / OpenRouter / OrcaRouter](openai-endpoints.md), [OpenRouter](openrouter.md), and [OrcaRouter](orcarouter.md).

## Browser mode

Expand Down
1 change: 1 addition & 0 deletions docs/followup.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ Without `--followup-model`, Oracle errors with the available lineage.
| Anthropic | ❌ no Oracle-side response id chaining yet |
| Gemini | ❌ |
| OpenRouter | ❌ |
| OrcaRouter | ❌ |
| Custom `--base-url` | ❌ — unknown whether the upstream preserves the id |

If you try to follow up on an unsupported provider, Oracle errors clearly instead of silently starting fresh.
Expand Down
2 changes: 1 addition & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ oracle --followup <id> -p "Re-evaluate with this new context" --file "src/**/*.t

## What Oracle does

- **One CLI to a stable of pro agents.** GPT-5.5 Pro (default), GPT-5.5, GPT-5.4 Pro, GPT-5.4, GPT-5.2 Pro, GPT-5.1 Pro, GPT-5.1 Codex, Gemini 3.1 Pro, Gemini 3.5 Flash, Gemini 3.1 Flash-Lite, Claude Sonnet 4.6, Claude Opus 4.1 — plus any OpenRouter id.
- **One CLI to a stable of pro agents.** GPT-5.5 Pro (default), GPT-5.5, GPT-5.4 Pro, GPT-5.4, GPT-5.2 Pro, GPT-5.1 Pro, GPT-5.1 Codex, Gemini 3.1 Pro, Gemini 3.5 Flash, Gemini 3.1 Flash-Lite, Claude Sonnet 4.6, Claude Opus 4.1 — plus any OpenRouter or OrcaRouter id.
- **Engines, plural.** API mode for reliability, browser mode (Chrome over CDP) when you don't want to pay or want the Pro tier, `--render --copy` when neither is an option.
- **Multi-model in one run.** Aggregate cost, token usage, and lineage across providers in a single command.
- **Recoverable panels.** `doctor --providers`, `--preflight`, `--route`, and `--allow-partial` make provider/key failures clear without losing successful model output.
Expand Down
1 change: 1 addition & 0 deletions docs/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ API mode is opt-in and reads keys from the environment. Set whichever providers
| Google | `GEMINI_API_KEY` | Gemini 3.1 Pro, Gemini 3.5 Flash, Gemini 3.1 Flash-Lite |
| Anthropic | `ANTHROPIC_API_KEY` | Claude Sonnet 4.6, Claude Opus 4.1 |
| OpenRouter | `OPENROUTER_API_KEY` | Any OpenRouter id (e.g. `minimax/minimax-m2`) |
| OrcaRouter | `ORCAROUTER_API_KEY` | Any OrcaRouter id (e.g. `orcarouter/auto`) |

If no key is set, Oracle defaults to **browser mode** and drives ChatGPT directly — see [Browser Mode](browser-mode.md) for the manual-login flow.

Expand Down
6 changes: 3 additions & 3 deletions docs/mythical-pro-agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ The headline frontier models — the ones marked **Pro** — are slow, expensive
| Claude Opus 4.1 | API only | `claude-4.1-opus` | — | Deepest single-shot reasoning |
| Claude Sonnet 4.6 | API only | `claude-4.6-sonnet` | — | Fast Claude |

Plus any **OpenRouter** id — e.g. `minimax/minimax-m2`, `openai/gpt-4o-mini`, `qwen/qwen-2.5-coder-32b-instruct` — when you set `OPENROUTER_API_KEY`.
Plus any **OpenRouter** or **OrcaRouter** id — e.g. `minimax/minimax-m2`, `openai/gpt-4o-mini`, `orcarouter/auto` — when you set `OPENROUTER_API_KEY` or `ORCAROUTER_API_KEY`.

## When to reach for which

Expand Down Expand Up @@ -106,7 +106,7 @@ oracle --engine browser --model gpt-5.5-pro \
- **GPT-5.x Pro** (browser): "free" with ChatGPT Pro / Plus subscription, but slow.
- **Gemini 3.1 Pro / 3.5 Flash / 3.1 Flash-Lite** (browser): available through a signed-in Google account, subject to account access.
- **Claude Opus 4.1**: per-token API only.
- **OpenRouter ids**: pricing varies wildly per provider; always preview with `--dry-run summary`.
- **OpenRouter / OrcaRouter ids**: pricing varies wildly per provider; always preview with `--dry-run summary`.

`--files-report` plus `--dry-run summary` is the right reflex before any Pro run on a large bundle. Token counts ≠ dollars, but they're a close-enough proxy.

Expand All @@ -125,4 +125,4 @@ oracle --engine browser --model gpt-5.5-pro \
| Deep Research | — | ✅ | — |
| `--render --copy` fallback | ✅ | ✅ | ✅ |

See provider-specific docs for the gory details: [OpenAI / Azure / OpenRouter](openai-endpoints.md), [Gemini](gemini.md), [Anthropic](anthropic.md), [Grok](grok.md).
See provider-specific docs for the gory details: [OpenAI / Azure / OpenRouter / OrcaRouter](openai-endpoints.md), [Gemini](gemini.md), [Anthropic](anthropic.md), [Grok](grok.md).
13 changes: 13 additions & 0 deletions docs/openai-endpoints.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,3 +171,16 @@ oracle --model minimax/minimax-m2 --prompt "Summarize the notes"
- If `OPENROUTER_API_KEY` is set and no provider-specific key is available for the chosen model, Oracle defaults the base URL to `https://openrouter.ai/api/v1`.
- You can still set `--base-url` explicitly; if it points at OpenRouter (with or without a trailing `/responses`), Oracle will use `OPENROUTER_API_KEY` and forward optional attribution headers (`OPENROUTER_REFERER` / `OPENROUTER_TITLE`).
- Multi-model runs accept OpenRouter ids alongside built-in ones. See `docs/openrouter.md` for details.

## OrcaRouter

Oracle can also talk to OrcaRouter (OpenAI-compatible) with any model id:

```bash
export ORCAROUTER_API_KEY="sk-orca-..."
oracle --model orcarouter/auto --prompt "Summarize the notes"
```

- If `ORCAROUTER_API_KEY` is set and no provider-specific key is available for the chosen model, Oracle defaults the base URL to `https://api.orcarouter.ai/v1`.
- You can still set `--base-url` explicitly; if it points at OrcaRouter (with or without a trailing `/responses`), Oracle will use `ORCAROUTER_API_KEY` and forward optional attribution headers (`ORCAROUTER_REFERER` / `ORCAROUTER_TITLE`).
- Multi-model runs accept OrcaRouter ids alongside built-in ones. See `docs/orcarouter.md` for details.
40 changes: 40 additions & 0 deletions docs/orcarouter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# OrcaRouter

Oracle can target any OpenAI-compatible model on OrcaRouter with minimal setup.

## Setup

```bash
export ORCAROUTER_API_KEY="sk-orca-..."
# Optional but recommended for attribution:
export ORCAROUTER_REFERER="https://your-app.example"
export ORCAROUTER_TITLE="Oracle CLI"
```

- If you set `ORCAROUTER_API_KEY` and don’t provide another provider key, Oracle automatically routes API runs to `https://api.orcarouter.ai/v1`.
- You can still point explicitly with `--base-url https://api.orcarouter.ai/v1` (Oracle will trim a trailing `/responses` if you include it).
- First-party keys win: if `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `GEMINI_API_KEY`, or `XAI_API_KEY` is present, Oracle will prefer those providers unless you set an OrcaRouter base URL.

## Models

- `--model` accepts any OrcaRouter model id, e.g. `orcarouter/auto`, `openai/gpt-5.5`, `anthropic/claude-sonnet-5`.
- `--models` can mix first-party and OrcaRouter ids:
`oracle --engine api --models "gpt-5-pro,orcarouter/auto,anthropic/claude-sonnet-5" -p "Summarize..."`.
- `orcarouter/auto` is OrcaRouter's adaptive routing model — it selects the best upstream per request based on your routing config at https://www.orcarouter.ai/console/routing. The full catalog is at https://www.orcarouter.ai/models.

## Headers

When hitting OrcaRouter, Oracle forwards optional attribution headers:

- `HTTP-Referer` from `ORCAROUTER_REFERER` (or `ORCAROUTER_HTTP_REFERER`)
- `X-Title` from `ORCAROUTER_TITLE`

## Sessions and logs

- Model ids that contain `/` are stored with a safe slug (`/` → `__`) for per-model log filenames, but the original id remains visible in session metadata and CLI output.

## Tips

- If a model id isn’t found in the OrcaRouter catalog, Oracle still sends the request with the id you provided.
- Pricing/context limits are pulled from the `/api/pricing` catalog when available; otherwise, Oracle uses conservative defaults (200k tokens, cost unknown).
- `orcarouter/auto` routes to whatever upstream OrcaRouter picks, so context limits and pricing are best-effort until the route resolves.
2 changes: 1 addition & 1 deletion src/cli/engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export function resolveEngine({
}

function hasApiEnvironment(env: NodeJS.ProcessEnv): boolean {
return Boolean(env.OPENAI_API_KEY || env.OPENROUTER_API_KEY);
return Boolean(env.OPENAI_API_KEY || env.OPENROUTER_API_KEY || env.ORCAROUTER_API_KEY);
}

function normalizeEngineMode(raw: unknown): EngineMode | null {
Expand Down
1 change: 1 addition & 0 deletions src/cli/sessionRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ export async function performSessionRun({
const modelConfig = await resolveModelConfig(primaryModel, {
baseUrl: runOptions.baseUrl,
openRouterApiKey: process.env.OPENROUTER_API_KEY,
orcaRouterApiKey: process.env.ORCAROUTER_API_KEY,
modelOverrides: runOptions.modelOverrides,
});
const files = await readFiles(runOptions.file ?? [], {
Expand Down
31 changes: 26 additions & 5 deletions src/oracle/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import type {
} from "./types.js";
import { createGeminiClient } from "./gemini.js";
import { createClaudeClient } from "./claude.js";
import { isOpenRouterBaseUrl } from "./modelResolver.js";
import { isOpenRouterBaseUrl, isOrcaRouterBaseUrl } from "./modelResolver.js";
import { isCustomBaseUrl } from "./baseUrl.js";

export function buildAzureResponsesBaseUrl(endpoint: string): string {
Expand All @@ -39,12 +39,13 @@ export function createDefaultClientFactory(): ClientFactory {
},
): ClientLike => {
const openRouter = isOpenRouterBaseUrl(options?.baseUrl);
const orcaRouter = isOrcaRouterBaseUrl(options?.baseUrl);
const customProxy = isCustomBaseUrl(options?.baseUrl);

// When using any custom/proxy base URL (OpenRouter, LiteLLM, vLLM, Together, etc.),
// When using any custom/proxy base URL (OpenRouter, OrcaRouter, LiteLLM, vLLM, Together, etc.),
// route ALL models through the OpenAI chat/completions adapter instead of native SDKs
// which would reject the proxy's API key.
if (!openRouter && !customProxy) {
if (!openRouter && !orcaRouter && !customProxy) {
if (options?.model?.startsWith("gemini")) {
// Gemini client uses its own SDK; allow passing the already-resolved id for transparency/logging.
return createGeminiClient(key, options.model, options.resolvedModelId);
Expand All @@ -57,7 +58,9 @@ export function createDefaultClientFactory(): ClientFactory {
let instance: OpenAI;
const defaultHeaders: Record<string, string> | undefined = openRouter
? buildOpenRouterHeaders()
: undefined;
: orcaRouter
? buildOrcaRouterHeaders()
: undefined;

const httpTimeoutMs =
typeof options?.httpTimeoutMs === "number" &&
Expand All @@ -80,7 +83,7 @@ export function createDefaultClientFactory(): ClientFactory {
});
}

if (openRouter || customProxy) {
if (openRouter || orcaRouter || customProxy) {
return buildOpenRouterCompletionClient(instance);
}

Expand Down Expand Up @@ -113,6 +116,24 @@ function buildOpenRouterHeaders(): Record<string, string> | undefined {
return headers;
}

function buildOrcaRouterHeaders(): Record<string, string> | undefined {
const headers: Record<string, string> = {};
// OrcaRouter accepts the same attribution headers as OpenRouter so the
// console can attribute traffic to Oracle.
const referer =
process.env.ORCAROUTER_REFERER ??
process.env.ORCAROUTER_HTTP_REFERER ??
"https://github.com/steipete/oracle";
const title = process.env.ORCAROUTER_TITLE ?? "Oracle CLI";
if (referer) {
headers["HTTP-Referer"] = referer;
}
if (title) {
headers["X-Title"] = title;
}
return headers;
}

function loadCustomClientFactory(): ClientFactory | null {
const override = process.env.ORACLE_CLIENT_FACTORY;
if (!override) {
Expand Down
Loading