* chore(deps): drain the Dependabot queue — 7 of 10 alerts Lockfile-only bumps; no manifest touched, so nothing changes for consumers. Root package-lock.json: hono 4.13.0 -> 4.13.7 (#215 #216 #217, medium, patched 4.13.5) csv-parse 7.0.1 -> 7.0.2 (#213, medium) joi 18.2.3 -> 18.2.8 (#211 #212, low, patched 18.2.4/18.2.5) @omniroute/opencode-plugin: toml 4.1.1 -> 4.3.0 (#209, HIGH, patched 4.1.2) @omniroute/opencode-plugin-v2: esbuild 0.28.1 -> 0.28.2 (#210, low) — the direct copy only; see below. The plugin-v2 diff looks large but is one package: esbuild ships 27 platform binaries, each carrying version + resolved + integrity. Three alerts stay open, deliberately: #218 extract-zip (HIGH) and #214 adm-zip (medium) have NO published patch. Both are dev-scope. Closing them needs an upstream release or a decision to replace the dependency — neither belongs in a lockfile bump. #210 esbuild is only half-closed. `node_modules/esbuild` is on 0.28.2, but `tsup` pins `esbuild: ^0.27.0`, so its nested copy stays at 0.27.7 — inside the vulnerable range (>= 0.27.3, < 0.28.1). Updating tsup does not move it (8.5.1 is already current). Forcing it would take an `overrides` entry pushing a major of esbuild inside the bundler, which is exactly the change that breaks a build silently, for a LOW dev-only alert. Left for an upstream tsup release. check:lockfile passes on all three, including the workspace lock/manifest consistency check. check:tracked-artifacts OK. * chore(deps): bump js-yaml to 4.3.2 (root + electron) Two more HIGH alerts arrived after the first sweep: #220 js-yaml (root package-lock.json) >= 4.0.0, < 4.3.2 #219 js-yaml (electron/package-lock.json) >= 4.0.0, < 4.3.2 The root's own js-yaml was already on 5.4.1; the vulnerable copies were the ones nested under @yarnpkg/parsers, lockfile-lint, xmlbuilder2 (root) and the direct dependency in electron. All now 4.3.2. Four version lines, nothing else. #221 smol-toml (HIGH, <= 1.7.0) is NOT closed here. The root is on 1.8.0; the vulnerable 1.6.1 sits under @openai/codex-security, which pins it as an EXACT version rather than a range, so `npm update` cannot move it. Bumping codex-security itself (0.1.24 -> 0.1.26) does not help — 0.1.26 pins the same 1.6.1 — so that bump was reverted rather than carried along for no benefit. Closing #221 needs an upstream codex-security release or an `overrides` entry, the same trade already declined for #210/tsup: forcing a transitive pin from outside is how a build breaks silently. Note that @openai/codex-security is also the package carrying the unpatched extract-zip (#218), so one upstream release would likely clear both. * chore(deps): override smol-toml to 1.8.0 and raise the js-yaml floor Closes #221 (smol-toml, HIGH, DoS via malformed TOML, vulnerable <= 1.7.0). @openai/codex-security pins smol-toml at 1.6.1 as an EXACT version, so no `npm update` reaches it. This repo already uses `overrides` as its standard tool for exactly that situation — the block carries 20+ entries, including the scoped-by-parent form and the `qs`/`fast-uri`/`ip-address` entries that back earlier security bumps — so a scoped override is the idiomatic fix here, not a new mechanism: "@openai/codex-security": { "smol-toml": "^1.8.0" } The nested copy deduplicates to the root's existing 1.8.0, which two other consumers (the root itself and knip) already run, so the version is proven in this tree. The whole lockfile diff is the 14 lines of the removed 1.6.1 entry. Also raised the `@yarnpkg/parsers` js-yaml floor from ^4.3.1 to ^4.3.2, so the override documents the patched version rather than permitting the vulnerable one it was written against. Not fixed, and not fixable by version — verified against the npm registry rather than trusting the advisory metadata: #218 extract-zip — latest published IS 2.0.1, the vulnerable version. Dev scope, via @openai/codex-security. No release to move to. #214 adm-zip — latest published IS 0.6.0, the top of the vulnerable range (>= 0.5.9, <= 0.6.0). RUNTIME scope, via onnxruntime-node's ^0.5.16, and the repo already overrides adm-zip to ^0.6.0. No release to move to. Both need an upstream fix or a decision to replace the dependency; neither is a lockfile change. adm-zip being runtime rather than dev makes it the one worth tracking. #210 esbuild stays open too. A flat `overrides: { esbuild: ^0.28.2 }` in opencode-plugin-v2 does close it — npm then reports 0 vulnerabilities — but it requires regenerating that lockfile from scratch: 823 lines, 96 packages moved, for a LOW dev-only alert, and a major esbuild bump inside tsup cannot be validated here without a real install of that package. Tried, measured, reverted. Left for an upstream tsup release. check:lockfile OK on all lockfiles including the workspace consistency check; check:tracked-artifacts OK; prettier clean.
@omniroute/opencode-plugin-v2
OpenCode v2 plugin (define({ id, setup }), Promise API) that publishes the live OmniRoute catalog — models from /v1/models, combos from /api/combos (least-common-denominator join), auto-combos from /api/combos/auto, enrichment (names + pricing), and usable-provider filtering — into the v2 catalog.transform, with key + env auth via integration.transform.
Companion to @omniroute/opencode-plugin (OpenCode v1, same repo). The two packages are independent: this one carries its own catalog-mapping logic and the v1 plugin is left untouched.
Install
npm install @omniroute/opencode-plugin-v2
opencode.json:
{
"plugins": [
{
"package": "@omniroute/opencode-plugin-v2",
"options": {
"providerId": "omniroute",
"baseURL": "http://localhost:20128"
}
}
]
}
Credentials
The plugin needs a gateway key to read the catalog, and looks for one in this order:
- The credential you connected in OpenCode. The plugin registers an
integration, so
opencode auth(or the Connect action in the model picker) can store a key for it. Nothing is written toopencode.json— this is the recommended route. apiKeyin the plugin options, when you want a per-project override. Remember that this puts the key in a config file you may be committing.OMNIROUTE_API_KEYin the environment.
If none of the three yields a key, the catalog is empty and the plugin says so once at startup rather than leaving you with a silent empty model list.
The management token is a different key
Combos, provider health and enrichment (display names, pricing, free-tier
budgets) come from the gateway's /api/* endpoints, which most deployments
gate behind a management token rather than the inference key. Set it
explicitly:
"options": {
"baseURL": "http://localhost:20128",
"managementReadToken": "<management read token>"
}
Left unset, managementReadToken falls back to apiKey for backwards
compatibility. When a gateway rejects that fallback, the catalog still
publishes — but with raw model ids instead of display names, no canonical
alias dedupe, no pricing and no combos. The plugin warns once per endpoint
when this happens, naming the endpoint and the consequence, so the degraded
catalog is never a mystery.
Options
| Key | Default | Notes |
|---|---|---|
providerId |
"omniroute" |
Provider id and integration id; models publish under <providerId>/… |
baseURL |
required | OmniRoute gateway root (no /v1 suffix needed) |
apiKey |
connected credential, then OMNIROUTE_API_KEY |
Chat key for /v1/* — see Credentials |
managementReadToken |
falls back to apiKey |
Management key for /api/* (combos, providers, enrichment) — usually not the same key |
displayName |
"OmniRoute" |
Provider display name |
timeoutMs |
10000 |
Per-endpoint fetch timeout (auto-combos use 5s) |
modelCacheTtlMs |
300000 |
Catalog cache TTL; disk snapshot warms cold starts |
timeouts |
per-endpoint override | { models, combos, autoCombos, enrichment } in ms; falls back to timeoutMs |
enrichment |
true |
Fetch names + pricing (/api/pricing*, /api/free-tier/summary) |
providerTag |
true |
Prefix a display name with the upstream provider it routes to |
geminiSanitization |
true |
Strip $schema/additionalProperties from tool schemas sent to Gemini models ($ref tools are forwarded untouched) |
usableOnly |
false |
Filter to healthy provisioned providers (/api/providers) |
visibleModels / hiddenModels |
[] |
Exact-or-suffix allowlists, deny wins |
apiFormat.allowAnthropic |
false |
Route allowlisted ids to the Anthropic API block |
apiFormat.anthropicModels |
[] |
Full model ids routed to Anthropic |
apiFormat.anthropicPrefixes |
v1 defaults | Deprecated, warns once — prefer anthropicModels |
logLevel / startupDebug |
warn / false |
Logger verbosity |
Tool calling on Gemini models
Gemini answers 400 INVALID_ARGUMENT — for the whole request, not just the
offending tool — when a tool declaration carries $schema or
additionalProperties. Anything that emits standard JSON Schema therefore
breaks tool calling as soon as the chain routes to Gemini.
The plugin strips those keywords from tool schemas bound for a Gemini model of
this provider, and leaves every other request untouched. A tool carrying a
$ref is forwarded untouched instead of stripped: removing the reference
would widen the schema to "accept anything". Set
"geminiSanitization": false to turn it off.
Migrating from the v1 plugin
The v2 plugin publishes provider id X bare. The v1 plugin published opencode-X (native-adapter gate). Sessions pinned to opencode-X/... must re-select the model under X/....
License
MIT