{"_id":"@auron-labs/opencode-omniroute-auth","name":"@auron-labs/opencode-omniroute-auth","dist-tags":{"latest":"0.2.0"},"versions":{"0.2.0":{"name":"@auron-labs/opencode-omniroute-auth","version":"0.2.0","description":"OpenCode authentication plugin for OmniRoute API with /connect command and dynamic model fetching","license":"MIT","type":"module","sideEffects":false,"main":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"import":"./dist/index.js","types":"./dist/index.d.ts"},"./runtime":{"import":"./dist/runtime.js","types":"./dist/runtime.d.ts"}},"scripts":{"build":"tsc -p tsconfig.json","dev":"tsc --watch","clean":"rm -rf dist","test":"bun run build && node --test test/*.test.mjs","smoketest":"bun run build && node scripts/smoketest.mjs","check:exports":"node --input-type=module -e \"import('./dist/index.js').then((root)=>{if(typeof root.default!=='object'||typeof root.default.server!=='function')throw new Error('Default export must be {id,server}');if(typeof root.OmniRouteAuthPlugin!=='function')throw new Error('OmniRouteAuthPlugin must be a function');})\"","prepublishOnly":"bun run clean && bun run build && bun run check:exports"},"packageManager":"bun@1.2.23","publishConfig":{"access":"public"},"keywords":["opencode","plugin","auth","omniroute","authentication","api-key","connect","models"],"repository":{"type":"git","url":"git+https://github.com/auron-labs/opencode-plugins.git","directory":"packages/opencode-omniroute-auth"},"homepage":"https://github.com/auron-labs/opencode-plugins/tree/main/packages/opencode-omniroute-auth","bugs":{"url":"https://github.com/auron-labs/opencode-plugins/issues"},"dependencies":{},"peerDependencies":{"@opencode-ai/plugin":"*"},"devDependencies":{"@types/node":"^20.0.0","typescript":"^5.0.0"},"engines":{"node":">=20.0.0"},"gitHead":"4157a4a6b508679f2448cc3b70fa92f5a82b4cee","_id":"@auron-labs/opencode-omniroute-auth@0.2.0","_nodeVersion":"24.16.0","_npmVersion":"11.18.0","dist":{"integrity":"sha512-KR+m12ct8ZI0t8vnzqdxRkz5ZStyb6MGY1dMPppL7RY1ndoXr0Sfa9mHp9Z9F90/Stof47n7gdlIEEo1TQJTsg==","shasum":"ea081e65f2aca9f0e6ea910324986a8787572a8f","tarball":"https://registry.npmjs.org/@auron-labs/opencode-omniroute-auth/-/opencode-omniroute-auth-0.2.0.tgz","fileCount":43,"unpackedSize":224779,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIBb5KgYbRMMjrxuOz0wH3NsNASluG+c5BPM1C//EPYoNAiBlwouHIjMdzpTOVWl3tk+IL47pUmZheTzf3Io7HNTqnA=="}]},"_npmUser":{"name":"aflorey","email":"aaron@buckhamduffy.com"},"directories":{},"maintainers":[{"name":"aflorey","email":"aaron@buckhamduffy.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/opencode-omniroute-auth_0.2.0_1782895985974_0.5368064685056564"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-01T08:53:05.838Z","0.2.0":"2026-07-01T08:53:06.201Z","modified":"2026-07-01T08:53:06.410Z"},"maintainers":[{"name":"aflorey","email":"aaron@buckhamduffy.com"}],"description":"OpenCode authentication plugin for OmniRoute API with /connect command and dynamic model fetching","homepage":"https://github.com/auron-labs/opencode-plugins/tree/main/packages/opencode-omniroute-auth","keywords":["opencode","plugin","auth","omniroute","authentication","api-key","connect","models"],"repository":{"type":"git","url":"git+https://github.com/auron-labs/opencode-plugins.git","directory":"packages/opencode-omniroute-auth"},"bugs":{"url":"https://github.com/auron-labs/opencode-plugins/issues"},"license":"MIT","readme":"# OpenCode OmniRoute Auth Plugin\n\n🔌 Authentication plugin for [OpenCode](https://opencode.ai) to connect to [OmniRoute](https://omniroute.ai) API.\n\n## Features\n\n- ✅ **Simple `/connect` Command** - No manual configuration needed\n- ✅ **API Key Authentication** - Simple and secure API key-based auth\n- ✅ **Dynamic Model Fetching** - Automatically fetches available models from `/v1/models` endpoint\n- ✅ **Provider Auto-Registration** - Registers an `omniroute` provider via plugin hooks\n- ✅ **Model Caching** - Intelligent caching with TTL for better performance\n- ✅ **Fallback Models** - Default models when API is unavailable\n- ✅ **Model Metadata Normalization** - Reads all OmniRoute field variants (camelCase, snake_case, capabilities object) with proper precedence\n- ✅ **Provider Alias Deduplication** - Automatically deduplicates alias/canonical model entries (e.g., `cx/gpt-5.5` → `codex/gpt-5.5`)\n- ✅ **Combo Model Capability Enrichment** - Automatically calculates lowest common capabilities for OmniRoute combo models\n- ✅ **models.dev Enrichment** - Enriches model metadata from models.dev API with provider alias resolution\n- ✅ **Subscription Provider Fallback** - Falls back to public providers for subscription-based models\n- ✅ **Model Variant Support** - Automatically strips reasoning effort suffixes (e.g., `gpt-5.5-xhigh` → `gpt-5.5`) for lookup\n- ✅ **Secure Logging** - Sanitized log output with async file I/O to prevent event loop blocking\n\n## Installation\n\n```bash\nbun add @auron-labs/opencode-omniroute-auth\n```\n\n## Quick Start\n\n### 1. Add plugin to opencode config\n```json\n{\n  \"plugin\": [\n    \"@auron-labs/opencode-omniroute-auth\"\n  ]\n}\n```\n\n### 2. Connect to OmniRoute\n\nSimply run the `/connect` command in OpenCode:\n\n```\n/connect omniroute\n```\n\nThe plugin will prompt you for your **API key**.\n\n### 3. Done! 🎉\n\nThe plugin automatically:\n- Fetches available models from `/v1/models`\n- Configures OpenCode to use OmniRoute\n- Stores your credentials securely\n\nNo manual configuration file editing required!\n\n## Usage\n\nOnce connected, OpenCode will automatically use OmniRoute for AI requests:\n\n```bash\n# The plugin is now active and ready to use\n# All AI requests will be routed through OmniRoute\n```\n\n### Refresh Models\n\nBy default, the plugin refreshes the model list whenever provider options are reloaded (`refreshOnList: true`).\n\nYou can disable refreshes by setting `provider.omniroute.options.refreshOnList` to `false` and clear the cache programmatically:\n\n```typescript\nimport { clearModelCache } from '@auron-labs/opencode-omniroute-auth/runtime';\n\nclearModelCache();\n```\n\n## Configuration (Optional)\n\nWhile the plugin works out-of-the-box with `/connect`, you can also configure it manually in your OpenCode config:\n\n```json\n{\n  \"plugin\": [\n    \"@auron-labs/opencode-omniroute-auth\"\n  ],\n  \"provider\": {\n    \"omniroute\": {\n      \"options\": {\n        \"baseURL\": \"http://localhost:20128/v1\",\n        \"apiMode\": \"chat\",\n        \"refreshOnList\": true,\n        \"modelCacheTtl\": 300000,\n        \"modelList\": {\n          \"cleanNames\": true,\n          \"exclude\": [\n            \"openrouter/z-ai/glm-5.2\"\n          ]\n        }\n      }\n    }\n  }\n}\n```\n\nUse `/connect omniroute` to store your API key in `~/.local/share/opencode/auth.json`.\n\n### Configuration Options\n\n| Option | Type | Required | Description |\n|--------|------|----------|-------------|\n| `plugin` | string[] | No | Plugin packages to load (use `@auron-labs/opencode-omniroute-auth` when installed from Bun or npm) |\n| `provider.omniroute.options.baseURL` | string | No | OmniRoute API base URL (default: `http://localhost:20128/v1`) |\n| `provider.omniroute.options.apiMode` | `'chat' \\| 'responses'` | No | Provider API mode (default: `chat`) |\n| `provider.omniroute.options.modelCacheTtl` | number | No | Model cache TTL in milliseconds (default: 5 minutes) |\n| `provider.omniroute.options.refreshOnList` | boolean | No | Whether to refresh models when provider options load (default: true) |\n| `provider.omniroute.options.modelsDev` | object | No | Enrich model metadata from models.dev on refresh (default: enabled) |\n| `provider.omniroute.options.modelMetadata` | object \\| array | No | Override/add metadata for custom/virtual models (works well in `opencode.js`) |\n| `provider.omniroute.options.modelList` | object | No | Control dedupe, clean model display names, rename ids, override display aliases, filter models, and optionally sort by name |\n\n### Model Metadata Enrichment (models.dev)\n\nOmniRoute may not expose model context/output limits in `/v1/models`. When enabled, this plugin attempts to\nenrich `contextWindow` and `maxTokens` by matching your OmniRoute models against `models.dev`.\n\nYou can disable enrichment or override defaults:\n\n```js\n{\n  provider: {\n    omniroute: {\n      options: {\n        modelsDev: {\n          enabled: true,\n          url: 'https://models.dev/api.json',\n          timeoutMs: 1000,\n          cacheTtl: 86400000,\n          providerAliases: {\n            cx: 'openai',\n          },\n        },\n      },\n    },\n  },\n}\n```\n\n### Custom / Virtual Model Overrides (config blocks)\n\nFor custom/virtual models (or when matching is imperfect), you can provide metadata overrides.\n\nIn `opencode.js` you can use RegExp matchers:\n\n```js\n{\n  provider: {\n    omniroute: {\n      options: {\n        modelMetadata: [\n          { match: /gpt-5\\.3-codex$/i, contextWindow: 200000, maxTokens: 8192 },\n          { match: 'omniroute/virtual/my-custom-model', addIfMissing: true, contextWindow: 50000 },\n        ],\n      },\n    },\n  },\n}\n```\n\nIn JSON configs, use an object keyed by model id:\n\n```json\n{\n  \"provider\": {\n    \"omniroute\": {\n      \"options\": {\n        \"modelMetadata\": {\n          \"virtual/my-custom-model\": { \"contextWindow\": 50000, \"maxTokens\": 2048 }\n        }\n      }\n    }\n  }\n}\n```\n\n### Model List Cleanup And Filtering\n\nIf OmniRoute returns duplicate aliases or raw provider slugs, you can clean up the emitted model list without changing the model ids used for requests.\n\nDefaults:\n- `dedupe: 'primary'`\n- `cleanNames: false`\n- `sort`: unset\n- `include`, `exclude`, `aliases`, and `rename`: unset\n\n```js\n{\n  provider: {\n    omniroute: {\n      options: {\n        modelList: {\n          dedupe: 'primary',\n          cleanNames: true,\n          include: [/^openrouter\\//, 'mimo/mimo-v2.5-pro'],\n          exclude: [/glm-5\\.2$/],\n          aliases: {\n            openrouter: 'OpenRouter',\n          },\n          rename: {\n            'mimo/mimo-v2.5-pro': 'Xiaomi Mimo: MiMo-V2.5-Pro',\n          },\n          sort: 'name',\n        },\n      },\n    },\n  },\n}\n```\n\n- Model ids are never rewritten for requests; these options only affect the emitted list and display names.\n- `cleanNames` formats display names like `openrouter/z-ai/glm-5.2` -> `Openrouter: Z-AI GLM-5.2`\n- `dedupe` defaults to `'primary'`; set `false` to keep duplicate alias entries from `/v1/models`\n- `aliases` overrides built-in display tokens while cleaning names\n- `include` keeps only models matching one of the strings or RegExp entries\n- `exclude` removes models matching one of the strings or RegExp entries\n- `rename` overrides the final display name for exact model ids\n- `sort: 'name'` uses human-friendly numeric sorting (`Test-2` before `Test-10`)\n\n#### Model List Options\n\n| Field | Type | Default | Description |\n|-------|------|---------|-------------|\n| `dedupe` | `false \\| 'primary' \\| true` | `'primary'` | `true` and `'primary'` both keep the primary/root entry when duplicates share the same underlying model. `false` keeps duplicates. |\n| `cleanNames` | `boolean` | `false` | Generates display names from model ids and provider metadata. |\n| `include` | `Array<string \\| RegExp>` | unset | Keep only models matching at least one entry. |\n| `exclude` | `Array<string \\| RegExp>` | unset | Remove models matching any entry. |\n| `aliases` | `Record<string, string>` | unset | Override built-in display token cleanup, for example `{ openrouter: 'OpenRouter' }`. |\n| `rename` | `Record<string, string>` | unset | Final exact-id display name overrides after cleanup. |\n| `sort` | `'name'` | unset | Sort the final emitted list by human-friendly model name. |\n\nMatching rules:\n- String entries in `include` and `exclude` are exact matches.\n- RegExp entries can match the model `id`, original `name`, cleaned name, or renamed final name.\n\nCommon examples:\n\nKeep the current primary dedupe, clean names, and sort by display name:\n\n```js\nmodelList: {\n  dedupe: 'primary',\n  cleanNames: true,\n  sort: 'name',\n}\n```\n\nKeep duplicate aliases from `/v1/models`:\n\n```js\nmodelList: {\n  dedupe: false,\n}\n```\n\nOnly show OpenRouter models except one noisy entry:\n\n```js\nmodelList: {\n  cleanNames: true,\n  include: [/^openrouter\\//],\n  exclude: ['openrouter/z-ai/glm-5.2'],\n}\n```\n\nForce your own branding for specific providers or families:\n\n```js\nmodelList: {\n  cleanNames: true,\n  aliases: {\n    openrouter: 'OpenRouter',\n    'z-ai': 'Z.AI',\n  },\n}\n```\n\n### Combo Model Capability Enrichment\n\nOmniRoute supports \"combo models\" - virtual models that route to multiple underlying models with fallback strategies. This plugin automatically detects combo models and calculates their capabilities using a **lowest common denominator** approach:\n\n- **Context Window**: Minimum of all underlying models\n- **Max Tokens**: Minimum of all underlying models  \n- **Vision Support**: Only if ALL underlying models support vision\n- **Tool Support**: Only if ALL underlying models support tools\n\nThis ensures safe operation by never exceeding the capabilities of any single model in the combo.\n\n**How it works:**\n1. The plugin fetches combo definitions from OmniRoute's `/api/combos` endpoint\n2. For each combo model, it resolves the underlying models\n3. It looks up each underlying model's capabilities from `models.dev`\n4. It calculates the lowest common capabilities across all resolvable models\n5. These calculated capabilities are applied to the combo model\n\n**Example:**\nThe \"Designer\" combo might route to:\n- `kmc/kimi-k2.5` (context: 256000, tools: yes)\n- `cx/gpt-5.1-codex-mini` (context: 204800, tools: yes)\n- `gemini/models/gemini-3-flash-preview` (context: 1048576, tools: yes)\n\nCalculated capabilities:\n- Context: **204800** (minimum)\n- Max Tokens: **32768** (minimum)\n- Tools: **true** (all support tools)\n\nNote: Some underlying models may not be found in `models.dev` (e.g., custom models). In such cases, they are excluded from capability calculation, and a warning is logged.\n\n### API Mode\n\n### API Mode\n\nThe plugin supports two provider API modes:\n\n- `chat` (default) - best compatibility with existing OpenAI-compatible chat workflows.\n- `responses` - enables Responses API mode when your OmniRoute/OpenCode setup supports it.\n\nExample:\n\n```json\n{\n  \"provider\": {\n    \"omniroute\": {\n      \"options\": {\n        \"apiMode\": \"responses\"\n      }\n    }\n  }\n}\n```\n\nIf an unsupported value is provided, the plugin falls back to `chat`.\n\n## Dynamic Model Fetching\n\nThis plugin automatically fetches available models from OmniRoute's `/v1/models` endpoint. This ensures you always have access to the latest models without manual configuration.\n\n### How It Works\n\n1. On first request, the plugin fetches models from `/v1/models`\n2. By default, models are refreshed every time you open the model list (`refreshOnList: true`)\n3. If `refreshOnList` is disabled, models are cached for 5 minutes (configurable via `modelCacheTtl`)\n4. If the API is unavailable, fallback models are used\n\n## Default Models\n\nWhen the `/v1/models` endpoint is unavailable, the plugin provides these fallback models:\n\n- `gpt-4o` - GPT-4o model with full capabilities\n- `gpt-4o-mini` - Fast and cost-effective\n- `claude-3-5-sonnet` - Claude 3.5 Sonnet\n- `llama-3-1-405b` - Llama 3.1 405B\n\n## API\n\n### Types\n\n```typescript\nimport type {\n  OmniRouteApiMode,\n  OmniRouteConfig,\n  OmniRouteModel,\n  OmniRouteModelListConfig,\n  OmniRouteModelMetadataConfig,\n  OmniRouteModelsDevConfig,\n} from \"@auron-labs/opencode-omniroute-auth\";\n\ninterface OmniRouteConfig {\n  baseUrl: string;\n  apiKey: string;\n  apiMode: OmniRouteApiMode;\n  defaultModels?: OmniRouteModel[];\n  modelCacheTtl?: number;\n  refreshOnList?: boolean;\n  modelsDev?: OmniRouteModelsDevConfig;\n  modelList?: OmniRouteModelListConfig;\n  modelMetadata?: OmniRouteModelMetadataConfig;\n}\n\ntype OmniRouteApiMode = 'chat' | 'responses';\n\ninterface OmniRouteModelListConfig {\n  dedupe?: false | true | 'primary';\n  cleanNames?: boolean;\n  include?: Array<string | RegExp>;\n  exclude?: Array<string | RegExp>;\n  aliases?: Record<string, string>;\n  rename?: Record<string, string>;\n  sort?: 'name';\n}\n\ninterface OmniRouteModel {\n  id: string;\n  name: string;\n  description?: string;\n  owned_by?: string;\n  root?: string;\n  parent?: string | null;\n  contextWindow?: number;\n  maxTokens?: number;\n  supportsStreaming?: boolean;\n  supportsVision?: boolean;\n  supportsTools?: boolean;\n  supportsTemperature?: boolean;\n  supportsReasoning?: boolean;\n  supportsAttachment?: boolean;\n  // OmniRoute native fields (normalized automatically)\n  context_length?: number;\n  max_input_tokens?: number;\n  max_output_tokens?: number;\n  capabilities?: {\n    vision?: boolean;\n    tool_calling?: boolean;\n    reasoning?: boolean;\n    thinking?: boolean;\n    attachment?: boolean;\n    temperature?: boolean;\n  };\n  pricing?: {\n    input?: number;\n    output?: number;\n  };\n}\n```\n\n### Functions\n\n```typescript\nimport {\n  fetchModels,\n  clearModelCache,\n  refreshModels,\n  // Combo model utilities\n  clearComboCache,\n  fetchComboData,\n  resolveUnderlyingModels,\n  calculateModelCapabilities,\n} from '@auron-labs/opencode-omniroute-auth/runtime';\n\n// Fetch models manually (with automatic normalization and enrichment)\nconst models = await fetchModels(config, apiKey);\n\n// Clear model cache (also clears combo cache)\nclearModelCache();\n\n// Force refresh models\nconst freshModels = await refreshModels(config, apiKey);\n\n// Combo model utilities\nconst combos = await fetchComboData(config);\nconst underlyingModels = await resolveUnderlyingModels('Designer', config);\nconst capabilities = await calculateModelCapabilities(model, config, modelsDevIndex);\n```\n\n## Development\n\n```bash\n# Install dependencies\nnpm install\n\n# Build\nnpm run build\n\n# Watch mode\nnpm run dev\n\n# Clean\nnpm run clean\n```\n\n## Troubleshooting\n\n### Connection Failed\n\nIf you see \"Connection failed\" when running `/connect omniroute`:\n\n1. **Check your configured base URL** - Ensure `provider.omniroute.options.baseURL` points to your OmniRoute endpoint\n2. **Verify your API key** - Ensure your API key starts with `sk-` and is valid\n3. **Check OmniRoute is running** - Ensure your OmniRoute instance is accessible\n\n### Models Not Loading\n\nIf models aren't loading:\n\n1. Check your OmniRoute `/v1/models` endpoint is accessible\n2. Ensure `provider.omniroute.options.baseURL` points to your OmniRoute endpoint\n3. Re-run `/connect omniroute` to refresh your API key\n4. If you use the package programmatically, call `clearModelCache()` from `@auron-labs/opencode-omniroute-auth/runtime`\n5. Check the OpenCode logs for error messages\n\n### Plugin Not Loading Outside This Repo\n\nIf the plugin loads only through a local shim (for example from `.opencode/plugins`) but not from npm in `opencode.json`:\n\n1. Ensure you are using `@auron-labs/opencode-omniroute-auth@1.0.1` or newer\n2. Confirm your config includes `\"plugin\": [\"@auron-labs/opencode-omniroute-auth\"]`\n3. Restart OpenCode so npm plugins are reloaded\n4. Check plugin install cache/logs under `~/.cache/opencode/node_modules`\n\nIf needed, clear and reinstall plugin dependencies, then restart OpenCode.\n\n## License\n\nMIT\n\n## Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request.\n\n## Support\n\nFor support, please open an issue on GitHub or contact OmniRoute support.\n\n## Star History\n\n[![Star History Chart](https://api.star-history.com/svg?repos=Alph4d0g/opencode-omniroute-auth&type=Date)](https://star-history.com/#Alph4d0g/opencode-omniroute-auth&Date)\n","readmeFilename":"README.md","_rev":"1-3805e23e5ca52a4b40e69b067ef7856c"}