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
Combo dispatch and the combo test button used to crash with
'TypeError: e.startsWith is not a function' when a step's modelStr
failed to resolve (regression after #2338 added per-account LKGP
routing for local/Docker providers). The TypeScript annotation says
the field is always a string, but malformed combo rows leaked through
to the dispatch path.
Two defensive boundary guards:
1. open-sse/services/combo.ts LKGP fallback findIndex — type-check
target.modelStr before calling startsWith.
2. src/app/api/combos/test/route.ts testComboTarget — coerce
target.modelStr at the entry, surface a clean 'Combo step is
missing a model id' error instead of crashing.
Regression test parses the source and asserts no unguarded
target.modelStr.<string-method> usages remain in combo.ts, so a
future refactor that reintroduces the pattern fails loudly.
Adds an import flow that lets users bring an existing Codex auth.json
into OmniRoute without a fresh OAuth login. Both a file-upload tab and
a paste-JSON tab are supported.
- `codexAuthImport.ts`: pure parser + createConnectionFromAuthFile
(conflict detection, overwriteExisting, JWT email/exp extraction)
- `POST /api/providers/codex-auth/import`: Zod-validated endpoint with
audit log (`provider.credentials.imported`)
- `importCodexAuthSchema` in schemas.ts (discriminated union json/text,
256 KB cap on paste source)
- `<ImportCodexAuthModal>` in providers/[id]/page.tsx with upload/paste
tabs, email auto-detection, name/email/overwrite fields
- "Import auth" toolbar button shown only on the Codex provider page
- 29 unit tests (17 parser + 12 schema) — all passing
Export filename change:
- Drop the redundant `codex-` prefix; embed the account email so multiple
exported files can coexist in the same downloads folder.
- Email is extracted from the id_token JWT `email` claim, with fallback
to connection.email and finally to the sanitized connection label.
- sanitizeFileNamePart now preserves @ so addresses survive intact
(e.g. `auth-diego@example.com.json`).
Apply Local refinement:
- ApplyCodexAuthModal: confirmation modal showing the resolved target
path, the side-by-side .bak location, and the centralized backup
trail. User must tick a confirmation checkbox before Apply enables.
- writeCodexAuthFileToLocalCli now writes a side-by-side
`auth-<timestamp>.bak` inside the .codex/ directory before replacing
the live file, in addition to the existing centralized backup. Both
inputs to the .bak path are server-controlled (dirname from the
static CLI_TOOLS table; basename from a server-generated ISO
timestamp), so no user input touches path APIs.
- apply-local route now emits a `provider.credentials.applied` audit
event with the resolved authPath and savedBakPath, and routes all
errors through sanitizeErrorMessage() per the security guide.
Tests: tests/unit/codexAuthFile.test.ts covers sanitization, JWT email
extraction, filename format for both branches (email/label), and the
ISO-timestamp .bak basename safety.
Scope: this is PR1 of the import/export work tracked under
_tasks/features-v3.8.0/importexport/. PR2 (import single) and PR3
(import bulk) will follow.
Resolves conflicts in 9 files to bring 181 commits from release/v3.8.0 into
the dashboard refactor branch ahead of merging back to release.
Layout strategy: our pages overhaul (tabs→pages, restructured sidebar,
removed redundant headers, OpenCode Free no-auth card) is the source of
truth. Release's functional additions are adapted into our layout.
Conflict resolution:
- package.json/package-lock.json: take release's deps (axios bump, CLI v4
deps, tls-client-node/wreq-js move to optionalDependencies); re-add our
@xyflow/react addition; regenerate lockfile.
- src/shared/constants/sidebarVisibility.ts: keep our 9-section restructure
— release's new IDs (limits, media, cli-tools, agents, cloud-agents,
memory, skills, agent-skills, context-*) are all already present in our
groups.
- src/i18n/messages/en.json: auto-merge picked up all release's new keys
(autoCatalog*, quotaCutoffs*, systemTransforms*, schema-coercion, vision);
only naming conflict was OmniSkills/AgentSkills — kept ours (no space).
- src/app/(dashboard)/dashboard/HomePageClient.tsx: kept our Provider
Topology card; ported release's TierCoverageWidget (placed before
topology).
- src/app/(dashboard)/dashboard/settings/page.tsx: kept our redirect to
/settings/general (we moved tabs to separate pages); release's sticky
tab CSS change is moot in our structure.
- src/app/(dashboard)/dashboard/skills/page.tsx: rerere applied — release
hardcoded "OmniSkills" h1 was already removed by our header-cleanup
refactor.
- src/app/(dashboard)/dashboard/agent-skills/page.tsx: both branches
created this file independently with identical data source; kept our
Tailwind-themed 2-column grid (release's version used inline styles).
- src/app/(dashboard)/dashboard/batch/page.tsx: kept our single-tab
structure (FilesListTab moved to /batch/files page); ported release's
onRefresh prop addition.
- src/app/(dashboard)/dashboard/batch/files/page.tsx (not in conflict but
updated): added batches fetch + batches prop to preserve release's
feature of showing related batches in the file detail modal.
Pre-existing typecheck errors in open-sse/services/contextManager.ts
(lines 141, 154, 167) come from release/v3.8.0 and are not introduced by
this merge.
The existing /api/combos GET requires a management token, which broke
read-only integrations (opencode-omniroute-auth plugin and similar) that
need to enrich combo capabilities from a normal Bearer API key. Those
clients got 403 AUTH_001 'Invalid management token' even though the same
API key could list models via /v1/models.
This adds GET /v1/combos with the same auth model as /v1/models:
- Accepts valid Bearer API key OR dashboard session cookie.
- Falls back to anonymous when REQUIRE_API_KEY=false (single-user local).
- Projects ONLY public metadata: name, strategy, description, model id,
providerId, comboName (for combo-refs). Internal routing details
(connectionId, weights, labels, sortOrder, config) are stripped.
/api/combos (management writes) is unchanged.
- tailscaleTunnel: add 'serve status' fallback for older/permissioned CLI builds
- tailscaleTunnel: use settings.tailscaleEnabled as fallback when live funnel detection fails
- tailscaleTunnel: use path.format() to avoid CWE-22 false positive on sibling binary path
- api/network/info: new endpoint returning LAN IPs and Tailscale interface IP
- endpoint page: show machine LAN IP chips in Local Server row for network access
- endpoint page: show Tailscale 100.x IP URL in expanded Tailscale panel
- endpoint page: fix Tailscale Stop Funnel icon (vpn_lock_off -> vpn_key_off, valid glyph)
Remove every reference to the competing open-source project from docs,
comparisons, i18n mirrors, comments, and source files so the branch
history does not expose competitive intelligence.
Five issues raised in the v3.8.0 release review, all release-blocking:
P1 — open-sse/services/tokenRefresh.ts
Read Windsurf Firebase API key from WINDSURF_CONFIG.firebaseApiKey
(resolvePublicCred wrapper) instead of process.env directly. Without
this, the literal removal from .env.example silently broke browser-flow
Windsurf/Devin token refresh.
P1 — open-sse/translator/request/openai-to-kiro.ts
Mark synthetic "(empty)" turns injected for assistant-first chats as
non-enumerable __synthetic and skip them when deriving conversationId
via uuidv5. Prevents unrelated chats from colliding on the same upstream
Kiro/AWS Builder ID context.
P2 — open-sse/utils/publicCreds.ts
Harden decodePublicCred against raw credential overrides outside
RAW_VALUE_PATTERN: strict-base64 alphabet check + printable-plain check
on the decoded result. Buffer.from(v, "base64") is lenient and was
silently mangling unrecognized raw values.
P2 — src/sse/services/auth.ts
Gate the x-api-key fallback on the anthropic-version header. Without
this scoping, local-mode requests with placeholder x-api-key from
non-Anthropic clients were rejected as Invalid API key even with
REQUIRE_API_KEY=false.
P2 — src/app/api/providers/[id]/test/route.ts
Move Qoder OAuth+PAT disambiguation BEFORE the CLI-runtime early-return
that was making the new message branch unreachable for the target
scenario from #2247.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Prevent raw exception messages from leaking stack frames or absolute
paths in the console logs and token health endpoints.
Also harden the i18n mirror move script by replacing shell-based git
commands with execFileSync and a safer fallback for untracked files.
Ensure provider-level delete all removes synced available model lists so imported models do not reappear after clearing managed models.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
When a Qoder connection lands in OAuth/CLI-flavored mode but the user
has pasted a Personal Access Token, the provider test route surfaces
"Local CLI runtime is not installed" plus a cascading 401 from
DashScope. Neither error tells the user "you picked the wrong auth
mode, switch to API Key".
The runtime check now detects this state (Qoder + non-apikey authType +
a token present on the connection or providerSpecificData) and surfaces
a single actionable message: "Qoder OAuth/Local CLI mode is selected
but the Qoder CLI is not detected. If you have a Personal Access Token,
switch this connection to API Key auth instead."
Non-Qoder providers and Qoder in real OAuth/CLI mode without a token
still get the original generic message.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Reorganizes /docs into 8 subfolders (architecture, guides, reference, frameworks,
routing, security, compression, ops). Resolves two conflicts:
- scripts/docs/gen-provider-reference.ts: combined FASE 1's new __dirname-based
ROOT (two levels up from scripts/docs/) with FASE 3's new output path
(docs/reference/PROVIDER_REFERENCE.md).
- scripts/check-env-doc-sync.mjs: deleted by FASE 1, modified by FASE 3; FASE 1's
delete wins (file is at scripts/check/ now). The FASE 3 intent (point to
docs/reference/ENVIRONMENT.md) was applied to the strict checker at the new path.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- src/app/docs/lib/docs-auto-generated.ts is regenerated from docs/<sub>/ with
fileName values like "architecture/ARCHITECTURE.md". The dynamic slug page
joins these against process.cwd()/docs so resolution still works.
- src/app/api/openapi/spec/route.ts now looks for the spec at
docs/reference/openapi.yaml first, with the flat-path fallback retained for
older bundles.
- tests updated: integration-wiring expects docs/reference/{API_REFERENCE.md,
openapi.yaml}; docs-site-overhaul reflects the new 8-section nav titles
(Architecture, Guides, Reference, Frameworks, Routing, Security, Compression,
Ops) and the new section for setup-guide ("Guides").
- open-sse/mcp-server/README.md picks up the rewritten docs/ reference.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Refresh the v3.8.0 documentation set across API, setup, Docker,
environment, Fly.io, VM deployment, and troubleshooting guides.
Document the updated budget payload, management-auth requirements,
WebSocket bridge secret, Compose profiles, production deployment
details, and expanded Node support. Also update the OpenAPI catalog,
raise coverage thresholds to match the current baseline, and align
cloud-agent task route typing and schemas with the stricter runtime
behavior.
Centralize optional API key capability checks so dashboard forms and
provider schemas share the same rules, including keyless Pollinations
support and cloud-agent batch testing mode.
Also align auto-combo config on `routerStrategy`, keep legacy combo
reads compatible, preserve MCP routes as management APIs, narrow
`require-login` public access to readonly/bootstrap flows, and make
cloud-agent CORS reflect the request origin for credentialed requests.
Protect cloud agent task routes and model cooldown management endpoints
with management-session auth and consistent CORS handling.
Also align cloud agent API serialization with the dashboard, reuse the
provider connection schema for CLI key storage, and accept active OAuth
tokens when building virtual auto-combo candidates.
Integrated into release/v3.8.0 after syncing the contributor branch, removing unrelated workflow/package-lock changes, and validating Command Code provider, auth, validation, and Responses coverage locally.
Integrated into release/v3.8.0 after syncing the contributor branch, removing unrelated workflow/docker/package-lock changes, and validating provider-scoped models coverage locally.
Integrated into release/v3.8.0 — adds target-based metadata aggregation for combo entries in /v1/models using least-common-denominator approach (context_length, max_output_tokens, capabilities, modalities).
Integrated into release/v3.8.0 — allows optional-key providers (SearXNG, Petals, self-hosted chat, OpenAI/Anthropic-compatible) to pass connection test by centralizing the check in providerAllowsOptionalApiKey().
## OAuth (browser PKCE)
- **`poll-callback`**: generalise from codex-only to all
`PKCE_CALLBACK_PROVIDERS` (codex, windsurf, devin-cli); state slot
is now dynamic (`__codexCallbackState` vs `__windsurfCallbackState`)
- **`OAuthModal`**: replace the codex-specific callback-server block
with a shared `PKCE_CALLBACK_SERVER_PROVIDERS` branch covering all
three providers; same start-callback-server + poll-callback polling
loop, same 2-second interval, same 5-minute timeout
- **`OAuthModal` redirect URI fallback**: windsurf/devin-cli now use
`http://localhost:{port}/auth/callback` (correct path) instead of
the generic `/callback` on non-true-localhost
- **`/auth/callback` page**: new Next.js route (`src/app/auth/callback`)
that re-exports `/callback/page`; Windsurf redirects to `/auth/callback`
so the popup auto-completes without manual URL paste (postMessage +
BroadcastChannel + localStorage)
## API-token (WINDSURF_API_KEY / paste-token)
- **`route.ts` — `import-token` action**: new POST action for
`IMPORT_TOKEN_PROVIDERS` (windsurf, devin-cli); calls
`provider.mapTokens({ accessToken: token })` skipping the HTTP
exchange and creating the connection directly
- **`oauthImportTokenSchema`**: Zod schema `{ token, connectionId? }`
- **`OAuthModal` — "Paste API Key" tab**: tab-switcher UI for
windsurf/devin-cli; sends `POST /import-token`; errors shown inline
## Security: remove hardcoded Firebase API key (GH secret alert)
- `AIzaSyBpLTEGSt59AUPKxBb7lIWjSE2ZXQH7mgU` removed from both
`oauth.ts` and `tokenRefresh.ts`; code now reads only
`process.env.WINDSURF_FIREBASE_API_KEY`
- Added `WINDSURF_FIREBASE_API_KEY` to `.env.example` with the
public key value and an explanation that it is a client-side
credential embedded in the Windsurf app (not a secret)
- `tokenRefresh.ts`: graceful `return null` with warn log when
key is absent (import tokens are long-lived and skip refresh anyway)
## Docs
- Sync 40 i18n CHANGELOG mirrors to v3.8.0 root content
Generated with [Devin](https://cli.devin.ai/docs)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>