Commit Graph

2888 Commits

Author SHA1 Message Date
diegosouzapw
582e89840d feat(cli): fase 5.5 — comando policy com CRUD e evaluate (exit 0/4) 2026-05-15 02:08:55 -03:00
diegosouzapw
d1880f1d4a feat(cli): fase 5.3+5.4 — a2a invoke JSON-RPC e tasks list/get/cancel/watch/stream/logs 2026-05-15 02:08:12 -03:00
diegosouzapw
3cfba85461 feat(cli): fase 5.1 — mcp call com stream e mcp scopes 2026-05-15 02:05:06 -03:00
diegosouzapw
23b1bd1ffe feat(cli): fase 4.4 — comando webhooks com CRUD, eventos e dispatch de teste 2026-05-15 01:53:09 -03:00
diegosouzapw
b8707dbbb4 feat(cli): fase 4.3 — comando eval com suites, runs e scorecard ASCII 2026-05-15 01:52:34 -03:00
diegosouzapw
76362c4efe feat(cli): fase 4.2 — comando cloud para agentes codex/devin/jules 2026-05-15 01:51:55 -03:00
diegosouzapw
8c48abc40c feat(cli): fase 4.1 — comando oauth com fluxos browser/import/social/device 2026-05-15 01:51:07 -03:00
diegosouzapw
28629bf7f0 feat(cli): adicionar omniroute audit (Fase 3.4)
5 subcomandos: tail, search, export, stats, get.
Mescla compliance e MCP por timestamp. --follow faz polling 2s.
--source filtra entre compliance|mcp|all. Mascaramento de actor.
2026-05-15 01:30:18 -03:00
diegosouzapw
2468ebf8f7 feat(cli): adicionar omniroute skills e marketplace (Fases 3.2 e 3.3)
skills: list, get, install, enable, disable, delete, execute, executions, skillssh.
marketplace: search, info, install, categories, featured.
Suporta --from-file, --from-url, --input-file, --type, --enabled, --status.
2026-05-15 01:24:36 -03:00
diegosouzapw
33ad5012c6 feat(cli): adicionar omniroute memory (Fase 3.1)
7 subcomandos: search, add, clear, list, get, delete, health.
Suporta --type, --api-key, --older-than (parser 30d/6m/1y), --token-budget.
Strings i18n em en.json e pt-BR.json.
2026-05-15 01:18:58 -03:00
diegosouzapw
ba14064ea4 feat(cli): adicionar omniroute providers metrics (Fase 2.6)
Extende o grupo `providers` com dois subcomandos:
- `providers metrics`: lista métricas de performance de todos os provedores
  (latência avg/P95, success rate, custo, breaker state, erros).
  Suporta --provider, --connection-id, --period, --sort-by, --limit,
  --watch (refresh 5s) e --compare (filtro multi-provider).
- `providers metric <id> <metric>`: retorna valor escalar de uma métrica
  específica para uma conexão.

Fonte: GET /api/provider-metrics — normaliza tanto formato objeto
{metrics: {[provider]: {}}} quanto array de providers.
2026-05-15 01:10:10 -03:00
diegosouzapw
85489a0295 feat(cli): adicionar grupo usage com 7 subcomandos (Fase 2.5)
Implementa `omniroute usage` com subcomandos:
- analytics: agregados por provedor com filtro --period/--provider
- budget list|get|set|reset: gerenciamento de budgets de custo
- quota: estado de quota por provedor com --check
- logs: call-logs com --search, --since, --api-key e --follow (tail 2s)
- utilization: métricas de uso por API key
- history: histórico de requisições
- proxy-logs: logs em nível de proxy

API keys mascaradas em outputs human. Todos os subcomandos suportam
--output json/table/csv/jsonl via emit().
2026-05-15 00:59:21 -03:00
diegosouzapw
993cd32829 feat(cli): adicionar comando cost com breakdown de custos (Fase 2.4)
Implementa `omniroute cost` que consulta /api/usage/analytics com:
- --period <range>: 1d|7d|30d|90d|ytd|all (padrão: 30d)
- --since/--until: faixa de datas ISO (substitui --period)
- --group-by: provider|model|api-key|combo|day (padrão: provider)
- --api-key: filtrar por chave específica
- --limit: top N resultados
- Total em stderr ao final (modo tabela)
- Ordenação por custo decrescente
2026-05-15 00:52:15 -03:00
diegosouzapw
18e5bf26a6 feat(cli): adicionar comando simulate para dry-run de routing (Fase 2.3)
Implementa `omniroute simulate [prompt]` que consulta /api/combos,
/api/monitoring/health e /api/usage/quota para mostrar qual caminho
de routing seria escolhido sem executar chamada upstream. Suporta:
- Tabela com provider, model, probabilidade, custo estimado, breaker e quota
- --explain: imprime árvore de fallback e faixa de custo no stderr
- --output json: retorna array de targets completo
- --file <path>: carrega body JSON para estimar tokens
- --combo <name>: filtra por combo específico
- Tratamento gracioso quando servidor está offline (exit 3)
2026-05-15 00:45:35 -03:00
diegosouzapw
7c128b0f4e feat(cli): adicionar comando stream com inspeção SSE (Fase 2.2)
Implementa `omniroute stream [prompt]` com suporte a:
- --raw: imprime linhas SSE brutas sem parsing
- --debug: timing por chunk no stderr com timestamp relativo
- --save <path>: persiste eventos em arquivo .jsonl
- --output json: retorna chunks + métricas (TTFT, totalMs, tokens/s)
- --responses-api: usa /v1/responses e lê campo delta
- SIGINT gracioso via reader.cancel()
- Métricas de TTFT e tokens/s no stderr ao final
2026-05-15 00:36:41 -03:00
diegosouzapw
685954c0dd feat(cli): adicionar comando chat one-shot (Fase 2.1)
Implementa `omniroute chat [prompt]` com suporte a --file, --stdin, --system,
--model, --max-tokens, --temperature, --top-p, --reasoning-effort,
--thinking-budget, --combo, --responses-api, --stream e --no-history.

Respostas impressas no stdout; latência e token count no stderr (não interfere
em pipes). Histórico salvo em ~/.omniroute/cli-history.jsonl. Streaming via
SSE com print incremental de deltas.
2026-05-15 00:28:36 -03:00
diegosouzapw
a5fa94bdcb feat(cli): crash recovery com backoff exponencial e PID granular (Fase 1.9)
Adiciona ServerSupervisor (bin/cli/runtime/processSupervisor.mjs) que reinicia o
servidor com backoff exponencial (1s, 2s, 4s... cap 10s) em caso de crash.
Após maxRestarts falhas em 30s exibe crash log e encerra. Detecta MITM como
causa do crash via heurística e desabilita automaticamente.

PID management agora é granular por subprocesso (~/.omniroute/{service}/.pid)
suportando server, mitm e tunnel/cloudflared|tailscale. `stop` e
`killAllSubprocesses` encerram todos os serviços registrados.

Novas opções em `serve`: --log (passa stdout/stderr inline), --no-recovery
(comportamento legado sem supervisor), --max-restarts <n> (padrão 2).
2026-05-15 00:18:53 -03:00
diegosouzapw
6b63aa3948 feat(cli): deletar bin/cli-commands.mjs monolito (Fase 1.8)
Remove o monolito bin/cli-commands.mjs (2853 linhas) e helpers redundantes
(bin/cli/args.mjs, tests/unit/cli-args.test.ts). Todos os subcomandos já foram
migrados individualmente para bin/cli/commands/ nas Fases 1.1–1.7. Atualiza
pack-artifact-policy para referenciar bin/cli/program.mjs no lugar de
bin/cli-commands.mjs e bin/cli/index.mjs. Atualiza docs e CHANGELOG.
2026-05-15 00:06:14 -03:00
diegosouzapw
0bb1ae7240 feat(cli): padronizar saída emit() — cli-table3/csv-stringify/schema (Fase 1.7) 2026-05-14 23:54:49 -03:00
diegosouzapw
9da0e704a7 feat(cli): consolidar CLI_TOOLS — listCliTools/getCliTool + setup --list (Fase 1.6) 2026-05-14 23:48:12 -03:00
diegosouzapw
ab911265ed feat(cli): banir SQLite direto — withRuntime + src/lib/db/* modules (Fase 1.5) 2026-05-14 23:41:02 -03:00
diegosouzapw
162e2f4b98 feat(cli): migrar backup/restore/health/quota/cache/mcp/a2a/tunnel/env/test/completion (Fase 1.4) 2026-05-14 23:24:52 -03:00
diegosouzapw
22d27ca273 feat(cli): migrar serve/stop/restart/dashboard/keys/models/combo (Fase 1.3)
Extrai 7 grupos de comandos do monolito bin/cli-commands.mjs (2853 linhas)
para módulos individuais em bin/cli/commands/, registrados via Commander.

- stop.mjs: SIGTERM/SIGKILL via process.kill(); fallback por porta usa execFile
  com array de args (evita injeção de shell / Semgrep CWE-78)
- restart.mjs: delega para runStopCommand + runServe
- dashboard.mjs: alias "open", fallback nativo por plataforma via execFile
- keys.mjs: server-first (POST /api/v1/providers/keys) → DB fallback; valida
  provider via loadAvailableProviders(); suporte a --stdin
- models.mjs: GET /api/models → fallback /api/v1/models; filtro por provider e --search
- combo.mjs: list/switch/create/delete; switch server-first → DB fallback via key_value;
  TODO(1.5) marcados para substituir SQL cru por src/lib/db/combos.ts
- serve.mjs: escreve PID via writePidFile() no spawn; limpa no shutdown; suporte daemon

utils/pid.mjs e i18n keys (en.json + pt-BR.json) já criados em iteração anterior.

Testes: cli-keys-command.test.ts atualizado para novos runners;
        cli-serve-stop-command.test.ts, cli-combo-command.test.ts,
        cli-models-command.test.ts adicionados (23 testes, 0 falhas).
2026-05-14 23:11:30 -03:00
diegosouzapw
4ed8e4e673 feat(cli): migrar comandos modulares para Commander (Fase 1.2)
Migra os 8 comandos restantes (doctor, setup, providers, config, status,
logs, update, provider) de parseArgs manual para a API do Commander.
Cada arquivo exporta register<Nome>(program) e run*Command(opts = {}).
Deleta bin/cli/index.mjs (substituído por commands/registry.mjs).
Remove dependência de bin/cli/args.mjs em todos os comandos migrados.
Corrige CWE-310 em doctor.mjs: authTagLength explícito no createDecipheriv.
2026-05-14 22:38:59 -03:00
diegosouzapw
31031422d3 feat(cli): adotar Commander.js como framework CLI (Fase 1.1)
- Instala commander@^14.0.0 com suporte nativo a ESM
- Cria bin/cli/program.mjs: programa raiz com opções globais
  (--output, --quiet, --no-color, --timeout, --api-key, --base-url)
- Cria bin/cli/commands/registry.mjs: adaptadores legados que delegam
  para os run*Command existentes sem quebrar compatibilidade
- Cria bin/cli/commands/serve.mjs: ação padrão (isDefault: true) com
  lógica de spawn extraída de omniroute.mjs, imports de runtime lazy
- Cria bin/cli/commands/reset-encrypted-columns.mjs: bypass de recuperação
- Refatora bin/omniroute.mjs: ~500 → ~90 linhas, delega ao Commander
- Adiciona strings i18n program.* e serve.description/port/no_open/daemon
  em en.json e pt-BR.json
- Adiciona 21 testes em tests/unit/cli-program.test.ts
2026-05-14 22:09:53 -03:00
diegosouzapw
2e494f8f07 feat(cli): Fase 0.3 — helpers base + convenções (api, i18n, output, runtime)
- bin/cli/CONVENTIONS.md: fonte normativa de flags, exit codes, output,
  retry/backoff, i18n, secrets, auditoria de ações destrutivas
- bin/cli/api.mjs: apiFetch() com retry/backoff, Retry-After, ApiError,
  statusToExitCode, isServerUp; computeBackoff/shouldRetryStatus exportados
- bin/cli/runtime.mjs: withRuntime/withHttp/withDb — server-first / DB-fallback;
  ServerOfflineError com exitCode 3
- bin/cli/i18n.mjs: t() com Map achatado (sem bracket em prototype), interpolação
  {vars}, setLocale/detectLocale/resetForTests; hardened contra __proto__ traversal
- bin/cli/output.mjs: emit() (table/json/jsonl/csv), EXIT_CODES, maskSecret,
  printSuccess/printError/printWarning/exitWith; output → stdout, diagnóstico → stderr
- bin/cli/locales/en.json + pt-BR.json: strings base (setup/doctor/providers/
  keys/combo/serve/backup/update/health/mcp/tunnel)
- bin/cli/README.md: mapa da estrutura e guia de uso dos helpers
- tests/unit/cli-exit-codes.test.ts: 10 casos — EXIT_CODES, statusToExitCode,
  backoff exponencial, jitter ±25%, t() i18n com pt-BR e anti-__proto__
- .env.example + docs/reference/ENVIRONMENT.md: documentar 4 novas env vars CLI
  (OMNIROUTE_LANG, OMNIROUTE_CLI_TOKEN, OMNIROUTE_HTTP_TIMEOUT_MS, OMNIROUTE_VERBOSE)
- scripts/check/check-env-doc-sync.mjs: adicionar LC_MESSAGES ao allowlist de sistema
2026-05-14 21:42:57 -03:00
diegosouzapw
cfc83e2fd8 docs(cli): remove duplicate docs/CLI-TOOLS.md + lint anti-regression
Phase 0.1 — single source of truth for CLI Tools documentation.

`docs/CLI-TOOLS.md` was a 492-line copy frozen at v3.0.0-rc.16 (13 tools,
outdated provider tables). The current source of truth is
`docs/reference/CLI-TOOLS.md` (v3.8.0, 17 tools, referenced by CLAUDE.md
and every cross-doc link in docs/).

Changes:
- delete docs/CLI-TOOLS.md (no remaining references; all docs already
  pointed at docs/reference/CLI-TOOLS.md)
- scripts/check/check-docs-sync.mjs: add anti-regression check that
  fails if a legacy superseded doc reappears

Verified: \`npm run check:docs-sync\` passes; rg shows zero remaining
references to the legacy path outside _references/ and _tasks/.

Refs: _tasks/features-v3.8.0/cli/fase-0-preparacao/0.1-limpar-docs-duplicada.md
2026-05-14 20:27:40 -03:00
diegosouzapw
4fe2ef8887 docs(opencode): announce @omniroute/opencode-provider on npm
- README: dedicated OpenCode integration section with npm/downloads/bundle
  size badges, install command, two-path usage example (CLI + npm), and
  the resulting opencode.json shape.
- CHANGELOG: note the 0.1.0 npm publish under Unreleased > Changed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 18:18:58 -03:00
diegosouzapw
4b1e57443a refactor(@omniroute/opencode-provider): rewrite for schema correctness + publishability
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>
2026-05-14 16:58:33 -03:00
diegosouzapw
d9b85704e4 docs(changelog): add PR #2261 managed model cleanup fix (@InkshadeWoods) 2026-05-14 15:48:31 -03:00
diegosouzapw
dd06e64207 Merge PR #2261: fix: align managed model cleanup for imported models (thanks @InkshadeWoods)
Adds deleteSyncedAvailableModelsForProvider() for full provider-level
cleanup, fixes delete-alias button visibility (source=alias only),
compatible models section gets proper 3-way delete logic.
2026-05-14 15:47:56 -03:00
diegosouzapw
d9c2c13851 fix(security): address P1/P2 findings from release review
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>
2026-05-14 15:23:46 -03:00
diegosouzapw
f3f1f9f36e fix(api): sanitize error responses in management routes
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.
2026-05-14 15:23:46 -03:00
Owen
67f713d3a5 Merge PR #2231: fix(deepseek): preserve reasoning_content through full pipeline for DeepSeek V4 models (thanks @kang-heewon)
Squash merge as part of the v3.8.0 release housekeeping. Closes #2231.
2026-05-14 15:23:24 -03:00
墨林ObsidianGrove
4a31a795b9 test: cover provider synced model deletion
Add regression coverage for clearing provider-scoped synced available models without affecting other providers.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-15 02:05:32 +08:00
墨林ObsidianGrove
83f242fa4d fix: clear synced provider models on delete all
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>
2026-05-15 01:39:31 +08:00
墨林ObsidianGrove
2f794d92a4 fix: hide delete action for imported models
Prevent synced imported and fallback provider models from showing row-level delete actions, while keeping custom/manual and alias-only deletion behavior intact.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-15 01:31:08 +08:00
diegosouzapw
eff7de2284 docs(security): note CodeQL custom-sanitizer limitation as dismissal precedent
CodeQL js/stack-trace-exposure does not recognize sanitizers reached
through a custom helper indirection — flagging callsites like
open-sse/utils/error.ts::errorResponse and
open-sse/executors/cursor.ts::buildErrorResponse even though both route
through sanitizeErrorMessage().

Record the dismissal precedent (alerts #224 and #231, May 2026):
- Add a "Known CodeQL limitation" section in
  docs/security/ERROR_SANITIZATION.md documenting how to handle future
  occurrences (verify callchain → verify test coverage → dismiss with
  reference, do NOT duplicate the pattern inline).
- Extend CLAUDE.md Hard Rule #14 with the precedent so the next
  engineer doesn't try to "fix" the false positive by weakening the
  shared sanitizer.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 14:10:29 -03:00
diegosouzapw
bbf3ba0138 docs(changelog): add 4 merged PRs (#2254, #2253, #2251, #2250) to Unreleased
- fix(executor/claude-code): non-enumerable tool remap metadata (@Rikonorus)
- fix(streaming): strip SSE compression headers (@Rikonorus)
- fix(kiro): harden translator for API compliance (@8mbe, closes #2213)
- fix(models): sync managed model aliases with visibility (@InkshadeWoods)
2026-05-14 13:47:03 -03:00
diegosouzapw
d2b413c9ab Merge PR #2251: fix(kiro): harden OpenAI-to-Kiro translator for API compliance (thanks @8mbe)
Closes #2213

Conflict resolution: kept both images field (HEAD) and origin field (PR),
kept toolsAttached return value (HEAD) while incorporating all 8mbe
improvements (schema normalization, synthetic user guard, orphaned tool
results, alternating role enforcement). All 16 tests pass.
2026-05-14 13:45:24 -03:00
diegosouzapw
ed1993d5bf Merge PR #2250: fix: sync managed model aliases with visibility (thanks @InkshadeWoods) 2026-05-14 13:42:14 -03:00
diegosouzapw
1d21eb1686 Merge PR #2253: fix: strip streaming compression headers (thanks @Rikonorus) 2026-05-14 13:41:41 -03:00
diegosouzapw
e1a6ecf238 Merge PR #2254: fix: keep Claude tool remap metadata off wire (thanks @Rikonorus) 2026-05-14 13:40:51 -03:00
diegosouzapw
57a80b6c1a fix(providers/blackbox-web): BLACKBOX_WEB_VALIDATED_TOKEN env override (#2252)
Blackbox's `/api/chat` now rejects requests whose `validated` field
doesn't match the frontend `tk` token (exported from app.blackbox.ai's
Next.js bundle), returning HTTP 403 even when the session cookie is
valid and the subscription is active. The previous executor sent a
random UUID, which works only until Blackbox enforces the check.

This change:

- Adds `resolveBlackboxValidatedToken()` that returns
  `BLACKBOX_WEB_VALIDATED_TOKEN` when set, otherwise falls back to the
  legacy random UUID (no regression for users who already work).
- Detects 403 responses whose body indicates a token-specific failure
  ("invalid validated token", "validation token", etc.) and replaces
  the generic "cookie expired" message with explicit guidance to set
  BLACKBOX_WEB_VALIDATED_TOKEN. The cookie-expired path is preserved
  for non-token 401/403.
- Documents the env var in `.env.example` and
  `docs/reference/ENVIRONMENT.md` (env-doc-sync check passes).

Deliberately NOT included: runtime scraping of Blackbox's Next.js
chunks to auto-extract `tk`. That coupling to their bundle hash would
silently break on every frontend deploy — the env override is the
stable path for operators who have already resolved the token.

Reported by @kazimshah39 with detailed root-cause analysis.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 13:00:15 -03:00
diegosouzapw
7210a73e1f fix(dashboard/api-manager): use getProviderDisplayName for owned_by labels (#2021)
Custom OpenAI-/Anthropic-compatible providers ship with synthetic IDs
like "openai-compatible-chat-<uuid>". ApiManagerPageClient was grouping
models by raw `model.owned_by`, so the user saw the full synthetic id
in the model picker.

Wrap `model.owned_by` with the existing centralized
`getProviderDisplayName` helper (already used by Endpoint, Health, and
Combos pages). The helper detects the dynamic-compatible pattern and
renders it as "Compatible (openai)" / "Compatible (anthropic)" — a
small but meaningful improvement that takes the dashboard one step
closer to issue #260's "no raw IDs in the UI" goal.

Showing the user-entered node name ("Poe") instead of the generic
"Compatible (openai)" label remains a follow-up (requires fetching
/api/provider-nodes here and matching by id/prefix); tracked separately.

Reported by @pulyankote with a complete surface-by-surface table.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 12:58:26 -03:00
diegosouzapw
f63f29830f fix(providers/qoder): disambiguate OAuth/CLI vs API-key error surface (#2247)
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>
2026-05-14 11:46:07 -03:00
diegosouzapw
e84e1b41bf fix(ui): clarify Claude extra-usage toggle notification text (#2157)
The toggle is labeled "Block Claude Extra Usage" but the success
notification simply said "Claude extra-usage blocked / allowed". Users
who interpreted the toggle as "Allow Extra Usage" then read the message
as inverted ("I enabled it and it tells me it's blocked").

New text spells out the toggle→effect relationship:
- ON  → "Claude extra-usage blocking enabled (extra usage will be blocked)"
- OFF → "Claude extra-usage blocking disabled (extra usage is allowed)"

No functional change. Reported by @uwuclxdy.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 11:43:43 -03:00
diegosouzapw
5ce332d2ee fix(translator): exclude cache_creation_input_tokens from prompt_tokens (#2215)
When OmniRoute translates Claude responses to the OpenAI format,
prompt_tokens was summing input + cache_read + cache_creation. Anthropic
pads short prompts up to a 1024-token minimum to create a cache, so:

  Request: {"messages":[{"role":"user","content":"hi"}]}
  Claude usage: input=8, cache_read=4, cache_creation=2000
  Dashboard "Total In": 8
  HTTP response prompt_tokens: 2008 (8 + 4 + 2000)

That 250x inflation broke downstream billing systems (Sub2API, NewAPI,
OneAPI) that trust prompt_tokens as the source of truth.

This change:
- prompt_tokens = input_tokens + cache_read_input_tokens (matches what
  the dashboard reports as "Total In", preserves issue #1426's intent
  that cache reads are billable input)
- cache_creation_tokens stays visible in
  prompt_tokens_details.cache_creation_tokens for auditing — it just no
  longer inflates the headline number

Reported by @downdawn with a complete root-cause trace.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 11:41:27 -03:00
diegosouzapw
7ab68365ba fix(auth): accept x-api-key header in extractApiKey (#2225)
Anthropic-native clients (Claude Code, @anthropic-ai/sdk) authenticate
via x-api-key per the Messages API contract. extractApiKey only read
Authorization: Bearer, so:

- usage_history.api_key_id was NULL for all x-api-key traffic (~50% of
  real-world traffic invisible in Costs/Analytics)
- api_keys.last_used_at never updated for keys delivered via x-api-key
- per-key policies (allowedModels, budget, rateLimits, accessSchedule,
  expiresAt) were bypassed for every Anthropic-native client

The fallback honors x-api-key (case-insensitive) when no Authorization:
Bearer is present. Bearer still wins when both are set, preserving
back-compat for clients that already send both. Reported by
@Forcerecon with a complete repro and validation plan.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 11:39:35 -03:00
diegosouzapw
242c50cb0c fix(security): rewrite sanitizeErrorMessage path matcher to be linear
The PATH_REGEX introduced in 1a39c31f used a greedy character class
([\w\-./\\]+) that can backtrack quadratically on inputs like
"///////..." — flagged by CodeQL js/polynomial-redos (#232).

Replace it with simple whitespace tokenization + a length-bounded
prefix-check helper. Same observable behaviour (paths replaced with
<path>), but linear time regardless of input shape. Adds a 4096-char
input cap as defence in depth.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 11:15:35 -03:00