OpenCode's Zen provider changed its slug from 'opencode-zen' to 'opencode',
breaking OmniRoute's provider resolution when users reference models with the
new prefix (e.g. 'opencode/deepseek-v4-flash-free').
Changes:
1. open-sse/services/model.ts: Add manual ALIAS_TO_PROVIDER_ID entry
mapping 'opencode' → 'opencode-zen' so parseModel() resolves
correctly for model strings using the new slug.
2. open-sse/executors/index.ts: Register 'opencode' as an OpencodeExecutor
alias for 'opencode-zen' so getExecutor() returns the correct executor.
3. open-sse/config/providerRegistry.ts: Update opencode-zen model list to
match the live API at https://opencode.ai/zen/v1/models:
- Add deepseek-v4-flash-free (the model users reported as broken)
- Add all 30+ models from the API (Claude, GPT, Gemini, Grok, GLM,
MiniMax, Kimi, Qwen series)
- Apply targetFormat: 'claude' to qwen3.5-plus (same SSE bug as qwen3.6)
- Remove ling-2.6-1t-free and trinity-large-preview-free (no longer in API)
- Enable passthroughModels so new models work without code deploys
4. @omniroute/opencode-provider/src/index.ts: Remove broken reference to
undefined OMNIROUTE_DEFAULT_MODEL_CONTEXT_LENGTHS constant.
5. tests/unit/opencode-executor.test.ts: Add tests for opencode alias,
deepseek-v4-flash-free routing, and model registry presence.
Error message sanitization (Hard Rule #12):
- claude-auth/export, codex-auth/export, gemini-cli-auth/export routes: replace
raw err.message with sanitizeErrorMessage() from open-sse/utils/error.ts
- imageGeneration, musicGeneration, videoGeneration handlers: import
sanitizeErrorMessage and replace all err.message in return values
- veoaifree-web executor: replace raw upstream response data in errResp() calls
with static strings
OAuth callback page (callback/page.tsx):
- Remove useSearchParams/Suspense dependency that caused hydration failures in
popup windows navigating back from Google OAuth (COOP header severs opener)
- Use window.location.search directly in useEffect with three send methods:
postMessage, BroadcastChannel, localStorage
- Fix postMessage target from "*" to window.location.origin (semgrep finding)
- Move setCurrentUrl call to manual-only branch to avoid unnecessary renders
copilot-web executor:
- Move accessToken from WebSocket URL query string to Authorization header
(avoids credential exposure in server logs)
- Add MAX_POOL_SIZE=100 cap to sessionPool with LRU eviction of oldest entry
CodeQL ReDoS fixes (js/polynomial-redos #233-240):
- Replace while(s.endsWith("/")) s=s.slice(0,-1) pattern (O(n²) allocations)
with index-based loop (O(n) time, single final slice) in:
bin/cli/api.mjs, all 6 cli-helper config generators, opencode-provider
Gemini OAuth:
- mapTokens: add idToken field to fix "missing id_token" export error
- Replace replace(/\/+$/, "") with explicit while-endsWith loop to avoid
polynomial backtracking on inputs with repeated trailing slashes
(CodeQL js/polynomial-redos #233-240, 8 alerts):
- @omniroute/opencode-provider/src/index.ts (normalizeBaseURL)
- bin/cli/api.mjs (stripTrailingSlash)
- src/lib/cli-helper/config-generator/{claude,cline,codex,continue,
kilocode,opencode}.ts (6 generators with identical pattern)
- tests/live/deepseek-web-live.test.ts: assert hostname via URL parsing
instead of String.includes() so the check is exact-match rather than
substring (CodeQL js/incomplete-url-substring-sanitization #241).
Alert #242 (Array.prototype.includes against fixed needle constant
OPENWEBUI_PARAGRAPH_ANCHORS) dismissed as CodeQL false-positive — not a
URL sanitization callsite.
The 1.0.0 release of the package was broken end-to-end:
1. index.js re-exported from "./index.ts" — Node can't import .ts at runtime,
so any consumer who `npm install`ed the package got ERR_UNKNOWN_FILE_EXTENSION.
2. The emitted provider shape did not match the OpenCode schema
(https://opencode.ai/config.json). It used a custom `{id, name, npm, options, auth}`
instead of the schema's `{npm: "@ai-sdk/openai-compatible", name, options, models}`.
3. README told users to pass `baseURL: "http://localhost:20128/v1"` but the code
appended `/v1` again — every request would 404 at `/v1/v1/...`.
4. No build step, no LICENSE file, no repository/author/engines fields, no tests.
This rewrite:
- Moves source under `src/`, adds a tsup build emitting CJS + ESM + .d.ts.
- `createOmniRouteProvider` now returns a schema-valid entry with
`npm: "@ai-sdk/openai-compatible"` + `models: Record<string, { name }>`.
- Adds `buildOmniRouteOpenCodeConfig` for full-document scaffolding.
- `normalizeBaseURL` deduplicates trailing `/` and `/v1`, accepts both forms,
and rejects malformed URLs and empty inputs.
- 13 unit tests covering URL normalisation, input validation, default model
catalog, custom models + labels, dedup/trim behaviour, and JSON round-trip.
- Adds LICENSE, full package.json (repository, engines, scripts, exports),
.gitignore, .npmignore, tsconfig.json, and a comprehensive README.
- Resets version to 0.1.0 to signal the pre-1.0 reset (1.0.0 was never on npm).
Documentation:
- New `docs/frameworks/OPENCODE.md` covering both integration paths (CLI vs npm),
URL normalisation, auth modes, troubleshooting, and runtime flow.
- README.md links the package and points to the new doc.
- CHANGELOG entry under Unreleased > Changed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>