Integrated into release/v3.8.0 — fixes DeepSeek 400 errors by using cached reasoning_content before falling back to empty string for tool_call messages
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
- Add addFirstProvider, addFirstProviderDesc, learnMore to providers namespace in en.json
(keys existed only in common namespace, causing raw key display on fresh installs)
- Add primary 'Add Provider' button to empty state that reveals provider grid
(only 'Learn more' external link existed, leaving users with no way to add a provider)
- Remove || fallback strings now that i18n keys are correctly placed
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.
- 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.
PR #2314's category filter chips and free-only toggle on the providers
page reference these keys under the 'providers' namespace, but the keys
exist only under 'common'. Add them under 'providers' so the chips render
without 'MISSING_MESSAGE' console errors.
The blanket .claude/ rule would block future additions to .claude/commands/
which is intentionally tracked. Replace with explicit list of runtime
artifacts: scheduled_tasks.lock, scheduled_tasks/, sessions/, state.json.
The lockfile is local Claude Code session state that was committed by
accident in 24b2e77a (#2265). Untrack it and add a narrow .gitignore rule
covering only runtime artifacts (lock, scheduled_tasks, sessions, state)
so shared command definitions at .claude/commands/ stay tracked.