npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@riskihajar/opencode-omniroute-auth

v1.3.1

Published

OpenCode authentication plugin for OmniRoute API with /connect command and dynamic model fetching

Downloads

1,469

Readme

OpenCode OmniRoute Auth Plugin

OpenCode plugin for using OmniRoute as a first-class provider with:

  • /connect omniroute auth flow
  • dynamic /v1/models discovery
  • OmniRoute-specific metadata enrichment
  • safer combo model capability handling
  • practical Responses API compatibility work for real OpenCode usage

This package is intentionally released separately because it goes beyond a minimal fork and tracks OmniRoute/OpenCode interoperability fixes faster.

Why this package exists

If you only want a generic fork, the upstream/mainline style plugin may be enough.

This package exists for teams actually running OmniRoute in OpenCode and needing things that work in practice, not just on paper.

What is different here

  • Real apiMode: "responses" wiring
    • chat uses @ai-sdk/openai
    • responses uses @ai-sdk/openai
  • OmniRoute responses compatibility fixes
    • normalizes/removes unsupported token limit fields on /responses
    • converts reasoningEffort aliases into reasoning.effort
  • Variant support for reasoning models
    • keeps low / medium / high
    • merges generated variants with custom ones like xhigh
  • OmniRoute-aware model metadata enrichment
    • models.dev enrichment for context/output limits
    • routed-provider matching using OmniRoute root / owned_by metadata
    • combo model lowest-common-capability calculation
  • Safer OpenCode runtime behavior
    • provider bootstrap normalization
    • local/runtime testing focused on actual OpenCode behavior

Why release it separately

Because OmniRoute behavior and OpenCode provider behavior do not always line up cleanly.

This package is for shipping OmniRoute-specific fixes without waiting for a generic upstream plugin direction, especially around:

  • Responses API behavior
  • reasoning variants
  • custom/proxy provider quirks
  • combo model metadata correctness

Highlights

  • /connect omniroute support
  • ✅ API key authentication
  • ✅ dynamic model fetching from /v1/models
  • ✅ model caching with TTL
  • ✅ fallback models when API/model listing fails
  • ✅ combo model capability enrichment from /api/combos
  • chat and responses runtime modes
  • ✅ reasoning variant support for OmniRoute reasoning models
  • ✅ request normalization for OmniRoute Responses API quirks
  • ✅ routed model enrichment for Anthropic/Gemini families behind providers like antigravity
  • ✅ image capability hydration for GPT-5/Codex-style routed models

Installation

npm install -g @riskihajar/opencode-omniroute-auth

Then add it to your OpenCode config:

{
  "plugin": [
    "@riskihajar/opencode-omniroute-auth"
  ]
}

For local development you can also point OpenCode directly to the repository path:

{
  "plugin": [
    "/absolute/path/to/opencode-omniroute-auth"
  ]
}

Quick start

1. Connect

Run:

/connect omniroute

Then paste your OmniRoute API key.

2. Select model

Use:

/models

3. Done

The plugin will:

  • register the omniroute provider
  • fetch available models from OmniRoute
  • enrich model metadata when possible
  • inject auth headers for OmniRoute requests

Vision / image input

Pain point this plugin fixes

OmniRoute model listings from /v1/models often do not include enough capability metadata for OpenCode to know whether a model supports image input.

In practice that can cause a bad failure mode for image prompts:

  • OpenCode renders the dropped image as [Image 1]
  • the provider registry thinks the selected model is text-only
  • the image attachment is rejected before the request is sent
  • OpenCode injects an error message into the user payload instead of sending input_image

That degraded payload looks like this:

{
  "role": "user",
  "content": [
    {
      "type": "input_text",
      "text": "[Image 1] ini gambar apa?"
    },
    {
      "type": "input_text",
      "text": "ERROR: Cannot read \"MAP-Midtrans-04-04-2026_10_14_PM.png\" (this model does not support image input). Inform the user."
    }
  ]
}

This plugin now fixes that for supported GPT-5/Codex-style OmniRoute models by keeping image capability hydration stable across provider bootstrap and runtime refresh.

How image capability is detected

The plugin decides image support in this order:

  1. explicit provider.omniroute.options.modelMetadata
  2. OmniRoute/runtime metadata when available
  3. models.dev enrichment when available
  4. conservative GPT-5/Codex-family fallback heuristics for known routed model families

If a model is considered vision-capable, the provider model now exposes both:

  • capabilities.attachment = true
  • modalities.input = ['text', 'image']

That combination is important because OpenCode uses provider capability metadata before deciding whether to serialize a dropped/pasted image as input_image.

How to confirm it works

When image handling works, your request payload should contain a real image part, not just an injected error string:

{
  "role": "user",
  "content": [
    {
      "type": "input_text",
      "text": "[Image 1] gambar apa yok?"
    },
    {
      "type": "input_image",
      "image_url": "data:image/png;base64,..."
    }
  ]
}

If a model truly does not support image input

The plugin prefers explicit metadata over heuristics.

  • If OmniRoute or your manual modelMetadata says a model does not support image input, that model should remain text-only.
  • If metadata is missing, the plugin only enables image support for known GPT-5/Codex-style families where real-world OpenCode usage would otherwise degrade badly.
  • Everything else stays on the safe default unless explicit metadata or enrichment says otherwise.

If you know a routed model should stay text-only, pin it explicitly:

{
  "provider": {
    "omniroute": {
      "options": {
        "modelMetadata": {
          "some-provider/some-text-only-model": {
            "supportsVision": false
          }
        }
      }
    }
  }
}

Configuration

Minimal example:

{
  "plugin": [
    "@riskihajar/opencode-omniroute-auth"
  ],
  "provider": {
    "omniroute": {
      "options": {
        "baseURL": "http://localhost:20128/v1",
        "apiMode": "chat"
      }
    }
  }
}

Options

| Option | Type | Description | |---|---|---| | provider.omniroute.options.baseURL | string | OmniRoute base URL. Default: http://localhost:20128/v1 | | provider.omniroute.options.apiMode | 'chat' \| 'responses' | Runtime API mode. Default: chat | | provider.omniroute.options.refreshOnList | boolean | Refresh model list on provider load. Default: true | | provider.omniroute.options.modelCacheTtl | number | Cache TTL in ms | | provider.omniroute.options.modelsDev | object | Configure models.dev enrichment | | provider.omniroute.options.modelMetadata | object \| array | Override/add model metadata, including per-model apiMode and resetEmbeddedReasoningVariant |

API modes

chat

Uses:

  • @ai-sdk/openai

Best when your OmniRoute/OpenCode flow is primarily Chat Completions compatible.

responses

Uses:

  • @ai-sdk/openai

This is important.

responses mode here is not just config decoration. It changes the runtime provider implementation so OpenCode can use native Responses API behavior.

Example:

{
  "provider": {
    "omniroute": {
      "options": {
        "apiMode": "responses"
      }
    }
  }
}

Per-model apiMode override

Not every routed model behaves the same way behind OmniRoute.

Some models work best with Responses API, while others still stream in Chat Completions shape even when the provider is globally configured for responses.

Because of that, this plugin supports per-model apiMode overrides.

Example: keep global responses, but pin a specific model to chat:

{
  "provider": {
    "omniroute": {
      "options": {
        "apiMode": "responses",
        "modelMetadata": {
          "minimax/minimax-m1": {
            "apiMode": "chat"
          }
        }
      }
    }
  }
}

Example: force a specific model to stay on responses:

{
  "provider": {
    "omniroute": {
      "options": {
        "apiMode": "responses",
        "modelMetadata": {
          "some-provider/some-model": {
            "apiMode": "responses"
          }
        }
      }
    }
  }
}

You can also set apiMode directly inside provider.omniroute.models entries when you define custom seeded models.

If a routed model id already ends with an embedded reasoning suffix like -high or -low, you can clear that forced winner override and restore the normal variant picker with resetEmbeddedReasoningVariant: true.

Example:

{
  "provider": {
    "omniroute": {
      "options": {
        "modelMetadata": {
          "antigravity/gemini-3.1-pro-high": {
            "resetEmbeddedReasoningVariant": true,
            "reasoning": true
          }
        }
      }
    }
  }
}

By default, the plugin still applies a conservative fallback for known models that appear to break under Responses streaming, but an explicit per-model override wins.

Current built-in fallback behavior in global responses mode:

  • Cursor default aliases cu/default and cursor/default fall back to chat
  • Anthropic-family routed models such as Claude / Opus / Sonnet / Haiku fall back to chat
  • Gemini-family routed models fall back to chat
  • MLX/Qwen-style routed models such as mlx/mlx-community/Qwen3.5-4B-MLX-8bit fall back to chat when OmniRoute streams Chat Completions chunks on the Responses path
  • suffixes like -thinking, -reasoning, -high, -medium, -low, -minimal, -max, -xhigh, and -none are normalized before that decision

This matters because some routed models may advertise support for both Chat Completions and Responses, but still emit chat.completion.chunk events when called through /v1/responses. In that case, the plugin prefers the safer chat runtime unless you explicitly override the model back to responses.

For Cursor specifically:

  • cu/default and cursor/default are forced to chat because OmniRoute currently returns Chat Completions streaming there even when OpenCode is globally configured for responses
  • Cursor-routed Claude models continue to follow the existing Claude-family fallback behavior instead of getting a broader Cursor-wide special case

Reasoning variants

For reasoning-capable models, this plugin can expose variants like:

  • low
  • medium
  • high

and merge them with explicit model variants like:

  • xhigh

This matters for OmniRoute/Codex-style models where upstream metadata is often incomplete or uneven.

OmniRoute-specific behavior

1. Dynamic model fetching

Models are fetched from:

  • /v1/models

2. Combo model enrichment

Combo models are resolved using OmniRoute combo metadata from:

  • /api/combos

Capabilities are calculated conservatively using the lowest common denominator across resolvable backing models.

3. Responses API normalization

When using apiMode: "responses", the plugin normalizes request payloads for OmniRoute quirks, including:

  • removing unsupported token limit fields
  • converting reasoning aliases into the shape expected by Responses requests
  • stripping OpenCode/OpenAI-style aliases that OmniRoute currently rejects on /v1/responses, including temperature, reasoningSummary, reasoning_summary, reasoningEffort, reasoning_effort, and textVerbosity

This plugin intentionally stays on @ai-sdk/openai for both Chat and Responses modes and treats OmniRoute compatibility as a payload-shaping problem rather than relying on a separate OpenAI-compatible runtime.

For the current OmniRoute behavior tested locally, the plugin preserves Responses fields that are accepted for Codex/GPT-5-style models, including:

  • store
  • prompt_cache_key
  • parallel_tool_calls
  • truncation
  • service_tier
  • top_p
  • presence_penalty
  • frequency_penalty
  • metadata
  • include

models.dev enrichment

OmniRoute model listings do not always provide enough metadata for a good OpenCode UX.

This plugin can enrich models with data derived from models.dev, especially:

  • context window
  • output token limit
  • tool support
  • reasoning support
  • image support when modalities.input includes image

It also tries harder to resolve routed OmniRoute models by:

  • checking the listed model id
  • checking OmniRoute root
  • normalizing runtime suffixes like -thinking or -high
  • mapping routed provider aliases such as antigravity to the most likely upstream family when matching models.dev

Example:

{
  "provider": {
    "omniroute": {
      "options": {
        "modelsDev": {
          "enabled": true,
          "url": "https://models.dev/api.json",
          "timeoutMs": 1000,
          "cacheTtl": 86400000,
          "providerAliases": {
            "cx": "openai"
          }
        }
      }
    }
  }
}

Custom model metadata

You can override or add model metadata manually.

JSON example:

{
  "provider": {
    "omniroute": {
      "options": {
        "modelMetadata": {
          "virtual/my-custom-model": {
            "contextWindow": 50000,
            "maxTokens": 2048,
            "apiMode": "chat"
          }
        }
      }
    }
  }
}

opencode.js example with matchers:

{
  provider: {
    omniroute: {
      options: {
        modelMetadata: [
          { match: /gpt-5\.4/i, reasoning: true },
          { match: /gpt-5\.3-codex$/i, contextWindow: 200000, maxTokens: 8192 },
        ],
      },
    },
  },
}

Runtime helpers

import {
  fetchModels,
  clearModelCache,
  refreshModels,
} from '@riskihajar/opencode-omniroute-auth/runtime';

Debugging and self-test

Check discovered models

To inspect the OmniRoute provider model registry with debug logs enabled:

source ~/.zshrc && OMNIROUTE_PLUGIN_DEBUG=1 opencode models omniroute --print-logs --log-level DEBUG

Useful things to verify in the output:

  • Hydrated model codex/gpt-5.4: attachment=true input.image=true toolcall=true
  • no later hydration pass flipping the same model back to attachment=false
  • omniroute/cu/default and omniroute/cursor/default should resolve without Responses parser errors

Spawn OpenCode for a quick self-test

To reproduce image handling from the CLI:

source ~/.zshrc && OMNIROUTE_PLUGIN_DEBUG=1 opencode run --model omniroute/codex/gpt-5.4 "[Image 1] ini gambar apa?" --print-logs --log-level DEBUG

What to look for:

  • if OpenCode still blocks image input before request serialization, your payload will degrade into injected input_text error content
  • if image serialization works, the user payload should contain type: "input_image"

Common verification flow

  1. restart OpenCode completely after plugin changes
  2. run opencode models omniroute --print-logs --log-level DEBUG
  3. confirm the target model is hydrated with attachment=true
  4. drop or paste an image into a fresh session
  5. inspect the payload and confirm input_image is present

Development

npm install
npm test

Release philosophy

This package is maintained as a pragmatic OmniRoute-focused distribution.

That means the priority order is:

  1. make it work in real OpenCode setups
  2. preserve OmniRoute-specific UX/features
  3. keep the public package easy to consume

If upstream/fork-main behavior is too generic to solve OmniRoute edge cases quickly, this package will continue shipping targeted fixes independently.

License

MIT