mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-26 09:52:11 +03:00
* chore(release): open v3.8.45 development cycle
* chore(release): parallel-cycle flow — sync-next-cycle script + Hard Rule #21 semantics (#6203)
Integrated into release/v3.8.45
* perf(test): tsx/esm loader + tsx 4.23 + órfãos recuperados + CI via npm scripts (plano testes+CI, Pacote 1) (#6214)
* perf(test): tsx/esm loader, tsx 4.23 bump, orphan tests recovered, CI runs npm scripts
Pacote 1 (quick wins) do plano mestre testes+CI:
- Swap --import tsx -> --import tsx/esm on the 19 test scripts: the repo is pure
ESM and the CJS hook costs ~1.3s PER test process (2,462 processes/run).
Measured: bootstrap 2.3s -> 1.1s; real-suite A/B (tests/unit/db, 12 files)
22.2s -> 14.1s wall (-36%), 82/82 pass. Non-test scripts keep the full hook.
- Bump tsx ^4.22.3 -> ^4.23.0 (fix for privatenumber/tsx#809 startup regression;
helps module resolution on big graphs — hook cost unchanged, honest note).
- Recover 22 ORPHAN test files (tests/unit/feature-triage/*.test.mjs, 53 cases,
53/53 pass) that matched no glob and ran in NO CI job; drop the dead
'executors' dir from the braces glob.
- Single source of truth for the unit-suite invocation: new test:unit:ci:shard
(shard via TEST_SHARD env) called by ci.yml test-unit/node24/node26/coverage
and quality.yml fast-unit — closing two silent drifts: CI was NOT importing
setupPolyfill.ts, and the fast path glob OMITTED tests/unit/memory + usage.
quality.yml TIA step + ci.yml test-integration get the tsx/esm swap only.
Validation: full unit suite 21,153 tests / 21,135 pass / 13 skip (5 fails =
known load-flake family, 10/10 green rerun isolated); vitest 237/237; smoke
db+feature-triage 135/135. Record run 2 = ci.yml workflow_dispatch on the
stacked pacote-2 branch (clean runners).
* fix(test): dashboard UI tests keep full tsx hook; recover 15 more .mjs orphans; extend discovery gate to .mjs
Follow-up do dispatch de validacao (run 28720431562), que pegou 2 problemas reais:
1. tests/unit/dashboard/** (11 arquivos, 102 casos) importam componentes React cujo
grafo puxa @lobehub/icons — o build es/ dele faz require() interno de arquivos com
sintaxe ESM, que so funciona com o patch CJS do tsx (sem ele: SyntaxError
'Unexpected token export' no CI; local vira crawl de ~60s/arquivo). Esses 11
arquivos rodam agora numa 2a invocacao com --import tsx COMPLETO (mesmo shard),
e o resto da suite mantem tsx/esm (-50% bootstrap). Validado: 102/102.
2. check:test-discovery falhou porque ancorava textualmente os globs nos workflows —
COLLECTORS atualizado p/ o modelo fonte-unica (ancora = nome do script
test:unit:ci:shard nos workflows) + varredura ESTENDIDA a .test.mjs, que era o
ponto cego que deixou os orfaos apodrecerem. A extensao revelou +15 orfaos .mjs
(top-level + db/) alem dos 22 de feature-triage — TODOS religados via glob
tests/unit/**/*.test.mjs (171/171 pass). Um deles (encryption-error-handling)
codificava o contrato PRE-hardening (decrypt falho retornava ciphertext cru —
vazamento); alinhado ao contrato shipped (null + log) com comentario.
Gate: [test-discovery] OK — 2892 arquivos, 22 collectors, 60 orfaos congelados
(divida rastreada, shrink-only).
* ci: dedup heavy pipeline — compat to nightly, coverage folded into unit shards, i18n single job, draft-skip (#6215)
Pacote 2+3-ci do plano mestre testes+CI (aprovado 2026-07-04). O CI pesado rodava a
suite unit 4x por sync da release-PR (95 jobs, 208 min-maquina) e o ciclo v3.8.44
disparou 123 desses runs (88 cancelados, 0 uteis) porque a release-PR viva fica
aberta o ciclo inteiro.
- D2: matrizes Node 24/26 (build + 8 jobs de teste, ~28% do custo por run) saem do
per-sync e viram .github/workflows/nightly-compat.yml (diario, fail-fast off,
resolve a release ativa como o nightly-release-green, abre issue de tracking em
falha). ci.yml/ci-summary limpos das referencias.
- D3: a matrix Coverage Shard x8 (~18% do custo) e eliminada — o job test-unit roda
os MESMOS shards sob c8/NODE_V8_COVERAGE e sobe os artifacts coverage-shard-N; o
job de merge (test-coverage) so repontou needs (padrao do CI do nodejs/node).
timeout test-unit 15->25min pelo overhead de instrumentacao.
- D4: a matrix i18n de ~40 jobs de <1min (saturava sozinha os 20 slots de
concorrencia da conta Free) vira 1 job que itera os idiomas com ::group:: por
idioma e artifact unico com resultados nomeados por idioma (antes 40 result.txt
colidiam no merge-multiple do ci-summary).
- P3: jobs pesados pulam pull_requests DRAFT (predicado em 10 jobs-raiz; o resto
pula pela cadeia de needs; ci-summary segue rodando como sinal unico) — a skill
/generate-release ja abre a release-PR viva como draft e flipa ready no 0a.0a
(commit eb04fc5 no repo .agents/skills).
- C5 (CodeQL schedule) NAO incluido: bloqueado na acao do dono Settings -> CodeQL
Default->Advanced (documentado no proprio codeql.yml).
Validacao: js-yaml parse ok; check:workflows zizmor 156 < baseline 159 (ratchet
verde); validacao de execucao = workflow_dispatch deste ci.yml neste branch ate
package-artifact + electron-package-smoke verdes (registrada no PR).
* feat(quality): no-new-warnings por PR — ESLint bulk suppressions + lint-guard fork-condicional (Pacote 4) (#6218)
* feat(quality): no-new-warnings per PR via native ESLint bulk suppressions
Pacote 4 do plano mestre testes+CI (aprovado 2026-07-04). O ratchet de
eslintWarnings so rodava no CI pesado (release-PR) -> o drift acumulava invisivel
e explodia na release (+41/+37/+88 por ciclo, rebaselinado as cegas — historico
no proprio quality-baseline.json). Modelo novo (SonarSource Clean-as-You-Code +
ESLint bulk suppressions nativo >=9.24):
- config/quality/eslint-suppressions.json congela a divida existente por
arquivo+regra: 476 arquivos / 4.273 violacoes.
- npm run lint + lint-staged (pre-commit) + novo job lint-guard no quality.yml
rodam suppressions-aware: violacao NOVA fica vermelha NO PR que a introduz
(bulk suppressions ainda eleva estouros de baseline por arquivo a error).
- 3 regras warn promovidas a error em src/** (react-hooks/exhaustive-deps,
@next/next/no-img-element, import/no-anonymous-default-export) — divida
existente congelada, ocorrencia nova = erro imediato.
- collect-metrics mede sob o baseline congelado -> a metrica eslintWarnings
vira 'divida liquida nova' (~0 em regime); baseline apertado 4279->0 no mesmo
PR (exigencia do require-tighten). Aperto do ESTOQUE congelado: npx eslint .
--prune-suppressions na reconciliacao da release.
- Principio Zero: lint-guard usa continue-on-error para PR de FORK (report-only;
a campanha /green-prs aplica o fix via co-autoria) — bloqueante so para
branches internas, a origem real do drift.
Validacao: negativo (any novo em tests/) exit 1; negativo (img em src/, regra
promovida) exit 1; positivo escopado exit 0; baseline gerado por --suppress-all
no tip (tree inteiro passa por construcao); YAML js-yaml ok.
* fix(quality): clear the 6 residual warnings so lint-guard runs clean at --max-warnings 0
The committed baseline still let 6 warnings through the lint-guard gate:
5 now-unused inline eslint-disable directives (the file-level suppressions
made them redundant — removed via eslint --fix, suppressions regenerated to
absorb the re-exposed occurrences) and 1 anonymous default export in
tests/load/k6-soak.js (outside the src/** severity-override scope — named
the k6 scenario function instead).
Verified on the clean tree: lint-guard exit=0; any-canary (new 'const x: any'
in open-sse) exit=1 — the gate bites on NEW violations while the 4,273
frozen ones stay suppressed (476 files).
* fix(ci): lint-guard continue-on-error must be boolean on non-PR events
github.event.pull_request is undefined on workflow_dispatch — the bare property
expression made the job fail at plan time (run 28722888456: 4 jobs green, run red,
lint-guard never materialized). Guard with event_name check so the expression is
always boolean: PR de fork = report-only (Principio Zero), resto = blocking.
* docs(changelog): v3.8.45 bullets for the tests+quality+CI pipeline overhaul (#6214, #6215, #6218)
i18n CHANGELOG mirrors intentionally left to the release reconciliation
(release:sync-changelog-i18n), per cycle practice.
* fix(api): stabilize relay SSRF-guard binding for minified builds (#6149) (#6224)
* fix(mcp): forward extra context through static tool loops (#6178) (#6228)
* fix(services): 9Router embed route + pre-spawn port probe (#6205) (#6227)
* fix(backend): system-first memory injection for strict providers (#6135) (#6225)
* fix(auth): clear error for stale-key decryption failures (#6148) (#6226)
* fix(backend): record reasoning source for zero-metered reasoning models (#6187) (#6229)
* fix(providers): refresh stale NVIDIA NIM model registry (#6108) (#6223)
* fix(backend): distinct max_input_tokens for GPT-family models (#6191) (#6230)
* fix(oauth): extract keychain-import-only guard to restore file-size freeze (base-red) (#6158)
`src/app/api/oauth/[provider]/[action]/route.ts` grew to 959 lines, past its
frozen cap of 924 (`check:file-size` → Fast Quality Gates red on release/v3.8.44).
The growth came from #6054 (graceful 400 for keychain-import-only providers / zed):
a doc block, two Sets (KEYCHAIN_IMPORT_ONLY_PROVIDERS, OAUTH_FLOW_ACTIONS) and a
keychainImportOnlyResponse() helper, plus two duplicated guard blocks in GET/POST.
That is a cohesive, self-contained leaf, so extract it to a new
`keychainImportOnly.ts` exposing `keychainImportOnlyGuard(provider, action)`
(returns the 400 NextResponse or null). The two route callsites collapse to a
2-line guard each. route.ts: 959 -> 918 (< 924, freeze restored). No behavior
change.
Tests (Rule #8/#18):
- Existing tests/unit/oauth-keychain-import-only-6041.test.ts (route-level GET/POST
zed 400) still pass unchanged — behavior preserved.
- New tests/unit/oauth-keychain-import-only-guard.test.ts pins the extracted guard
in isolation (zed+flow -> 400, normal provider -> null, zed+non-flow -> null).
* fix(dashboard): stop model-test error freezing the page (React #31 object toast) (#6161)
Clicking 'test' on a provider model (e.g. a ClinePass flash model) could freeze
the entire dashboard. Root cause: POST /api/models/test returned an OBJECT in
`error` on the Zod-validation and invalid-JSON paths (`validation.error.format()`
/ a details object). The client does `notify.error(data.error)`, and
NotificationToast renders the message directly as a React child — an object throws
React #31 ('Objects are not valid as a React child'), crashing the tree = frozen
page instead of a toast.
Fixed in three layers (defense in depth):
1. Server (root cause): /api/models/test now returns a STRING `error` on every
path — flattens Zod issues to text, returns 'Invalid JSON body' for bad JSON.
2. Client: onTestModel funnels the response through extractApiErrorMessage() so any
object-shaped error is coerced to a string before notify.error.
3. Toast: NotificationToast coerces title/message via toToastText() — a resilient
catch-all so no future caller can freeze the page with a non-string.
Tests (Rule #18, both node:test / blocking suite):
- tests/unit/models-test-error-shape.test.ts — asserts STRING error on Zod-fail,
missing-field, and invalid-JSON (fails on the pre-fix route: 3/3 red -> green).
- tests/unit/notification-toast-coercion.test.ts — toToastText coercion matrix.
* fix(dashboard): remove the always-on Auto-Routing (combo) banner from the home page (#6164)
The blue "Auto-Routing Active — OmniRoute is automatically routing requests
using combo-based strategies" banner was rendered unconditionally on the home
page (`/home`, the default dashboard landing) — it did NOT reflect whether
auto-routing was actually active, and reappeared on every fresh browser / private
window / cleared localStorage (dismissal is stored per-browser). It added noise
to the landing page without conveying live state.
Remove it: drop the <AutoRoutingBanner /> usage + import from home/page.tsx and
delete the now-unused component and its test.
* fix(cline): force upstream streaming for Cline/ClinePass (streaming-only API) (#6165)
* fix(cline): force upstream streaming for Cline/ClinePass (streaming-only API)
Cline's API (api.cline.bot) only implements streaming (streamText). A
non-streaming request returns HTTP 500 "generateText is not implemented" (Claude
models) or HTTP 502 "empty response" (others). Live-verified on the VPS:
stream:true → works (STREAM_OK), stream:false → fails. This is why testing a Cline
model in the dashboard (the test button sends stream:false) failed.
Fix (reuses the existing isClaudeCodeCompatible mechanism, no new handler):
- Flag `cline` and `clinepass` registry entries with `forceStream: true`.
- In chatCore, OR `providerRequiresStreaming` into `upstreamStream` (line 1591)
so the upstream request always streams for these providers, while the client's
original `stream` intent still drives the response format. The existing
non-streaming branch (parseNonStreamingResponseBody) already accumulates the
upstream SSE and converts it back to JSON for stream:false clients — the same
path Claude-Code-compatible providers already use.
Tests (Rule #18): tests/unit/cline-force-stream.test.ts pins the registry flags +
resolveStreamFlag forcing behavior. Live VPS before/after recorded on the PR.
* fix(sse): cline forceStream must stream upstream only, keep client JSON
The #2081 wiring fed providerRequiresStreaming into resolveStreamFlag,
forcing the client-facing stream flag to true for forceStream providers.
That skips the if(!stream) branch that drains a forced upstream SSE and
converts it back to JSON, so a stream:false caller (model-test button,
plain JSON API) got STREAM_EARLY_EOF instead of a JSON body.
Keep providerRequiresStreaming only on upstreamStream (force upstream to
stream); leave the client-facing stream as the client sent it, so
readNonStreamingResponseBody accumulates the SSE into JSON. The promised
handleForcedSSEToJson (#2081 comment) was never implemented — this uses
the existing non-streaming SSE-buffering path (same as isClaudeCodeCompatible).
Live-verified on VPS: cline stream:true worked, stream:false failed.
* fix(providers): correct Kiro model catalog to real upstream ids (#6170)
* fix(providers): correct Kiro model catalog to real upstream ids
Kiro's API (generateAssistantResponse) returns 400 "Invalid model. Please
select a different model" for any id it does not recognize. The registry
exposed fabricated ids (copied from OmniRoute's own Anthropic catalog) that
Kiro never serves, so every call to them 400'd. Live-verified on the VPS:
Removed (400 Invalid model):
- auto-kiro (no "auto" model id — was sent verbatim upstream)
- claude-fable-5 (Kiro offers no Fable)
- claude-opus-4.8/4.7/4.6 (Kiro offers no Opus)
Corrected:
- claude-sonnet-4.6 -> claude-sonnet-4.5 (Kiro's Sonnet is 4.5; 4.5 -> 200)
Kept:
- claude-sonnet-5 (real Kiro model, plan-gated per account)
- claude-haiku-4.5, deepseek-3.2, glm-5, minimax-m2.5/m2.1,
qwen3-coder-next (all proven 200 on the VPS)
Aligns the free-model catalog and drops the orphaned auto-kiro price key.
Regression guard: tests/unit/kiro-catalog-real-models.test.ts (3/3).
Kiro cluster #6112/#6113/#6099.
* test(providers): align stale Kiro-catalog tests to the corrected upstream ids
The fabricated Kiro ids removed in the parent commit (claude-fable-5,
claude-opus-4.8/4.7/4.6, claude-sonnet-4.6) were still asserted as present by
three pre-existing tests, which encoded the bug:
- catalog-updates-v3x: now asserts Kiro does NOT expose Fable 5 / Opus (kept the
legit cc exposure) and guards the real claude-sonnet-4.5 pricing.
- model-family-fallback-notation: the dot-notation example moves from kiro/ to
anthropic/ (which genuinely serves Opus/Fable in dot notation) — coverage kept.
- provider-models-route: the Kiro local-catalog assertion now expects the real
Sonnet 5 / Sonnet 4.5 set and negatively guards the fabricated ids.
Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>
* fix(sse): surface ChatGPT-web image silent-drop as an accurate error (#6208)
When ChatGPT Web generates an image as an image_asset_pointer but the pointer
fails to resolve to a downloadable URL (unknown asset scheme, download 403/
expired, oversize), resolveImagePointers returned [] — indistinguishable from
'no image produced' — so the image-generation handler reported the misleading
502 'completed without returning image markdown'. The image genuinely existed
upstream; OmniRoute dropped it silently.
Fix: the executor flags x_image_resolution_failed when a pointer existed but
none resolved (and logs the unresolved asset scheme for follow-up), and the
handler surfaces a truthful 'generated but not retrievable' 502 instead of
'no image markdown'. Adds executorFactory DI for unit testing.
TDD: tests/unit/chatgpt-web-image-silentdrop.test.ts (red -> green), plus the
existing chatgpt-web / image-generation-handler suites stay green.
Reported via community triage (mesh escalated backlog).
* fix(dashboard): providers page data-timeout guard + live-ws standalone wiring (#6211)
* fix(dashboard): providers page data-timeout guard + live-ws standalone wiring
Captura de trabalho em progresso: timeout de dados na página de providers,
ajuste em ProviderLimits e instrumentation-node, com testes novos
(providers-page-data-timeout, live-ws-standalone-wiring).
* chore(quality): rebaseline ProviderLimits/index.tsx file-size (+6, #6211 data-timeout guard)
Cohesive fix growth from PR #6211's data-timeout guard on the quota page's two
first-paint fetches (1121->1127). The fast-path PR->release skips check:file-size,
so the bump lands with the PR. Justification recorded in file-size-baseline.json.
* fix(translator): strip reasoning param for nvidia z-ai/glm-5.2 (#6181)
* fix(translator): strip reasoning param for nvidia z-ai/glm-5.2
NVIDIA NIM OpenAI-compatible wrapper rejects the reasoning body field
and returns HTTP 400 "Unsupported parameter(s): `reasoning`".
Add a StripRule scoped to provider=nvidia + model /z-ai\/glm-5\.2/i.
Mirrors PR #6102 drop pattern (minimax-m2.7 thinking).
* docs(translator): tighten nvidia glm-5.2 strip-rule comment
* fix(translator): anchor glm-5.2 strip rule with word boundary
* fix: add nvidia to PROVIDER_TOOL_LIMITS (1536) to prevent tool truncation (#6177)
NVIDIA NIM API (nvidia/* models) silently truncates the tool list to 128
(the default MAX_TOOLS_LIMIT) because nvidia is not in PROVIDER_TOOL_LIMITS.
Tools beyond index 127 are dropped, causing agents to lose access to
critical tools like task, read, or high-index MCP tools.
Verified that NVIDIA NIM API supports up to 1536 tools by direct testing.
End-to-end confirmed: 198 tools sent, model successfully called tools at
indices 193, 195, and 197 (previously dropped by truncation to 128).
Follows the same pattern as #5563 (grok-cli: 200), integrated in v3.8.43.
* feat(provider): add Claude 5 Sonnet to Claude Web provider (#6200) (#6209)
* feat(provider): add Claude 5 Sonnet to Claude Web provider (#6200)
* test(providers): guard claude-web claude-sonnet-5 registry entry (#6209)
Adds the missing regression test the PR-test-policy gate requires: asserts the
claude-web registry exposes claude-sonnet-5 (Claude 5 Sonnet web) alongside the
existing 4.6 Sonnet / 4.5 Haiku entries. Fails on the release base (no entry).
Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>
---------
Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com>
* fix(cli): detect POSIX auto-set HOSTNAME via os.hostname() to fix bind address (#6194) (#6195)
POSIX shells (bash/zsh) always set HOSTNAME to the machine name. The
.env loader uses first-wins semantics, so HOSTNAME=0.0.0.0 in .env is
silently ignored. This causes the server to bind to the LAN hostname
instead of 0.0.0.0, breaking localhost access and all internal
self-requests (ModelSync, HealthCheck, cloud sync).
The fix compares process.env.HOSTNAME against os.hostname(): when they
match, it's the POSIX auto-set signature and HOSTNAME is ignored.
OMNIROUTE_SERVER_HOST takes precedence as the dedicated escape hatch.
Backward compatibility is preserved: users who set HOSTNAME to a value
that doesn't match the machine name (e.g. Windows CMD/PowerShell users
with HOSTNAME in .env) will still have their value honoured.
Closes #6194
* feat(sse): surface Kiro adaptive-thinking reasoning as reasoning_content (#6213)
Kiro/CodeWhisperer streams Claude's reasoning as native `reasoningContentEvent`
frames when adaptive thinking is enabled, but the Kiro executor had no handler
for them, so `reasoning_effort` requests returned no reasoning. Wire it end to
end:
- translator (openai-to-kiro): enable Kiro thinking when the request carries
`reasoning_effort`, Anthropic `output_config.effort`, or a `thinking` block
(`{type:"enabled",budget_tokens}` mapped to a level; `{type:"adaptive"}`
defaults to `high`, matching Anthropic's documented default). Prepends the
Kiro `<thinking_mode>`/`<max_thinking_length>` prompt directive and sets
top-level `additionalModelRequestFields` ({output_config.effort,
thinking:{type:"adaptive"}, max_tokens}). Gated on `supportsReasoning`; drops
non-default temperature/top_p (rejected by adaptive-only Claude models).
- executor transformRequest: forward `additionalModelRequestFields` to AWS
(previously dropped by the strict top-level allowlist).
- executor stream loop: parse `reasoningContentEvent` (and reasoningText
variants) into the OpenAI reasoning_content channel.
Verified against the live CodeWhisperer stream: reasoningContentEvent frames are
returned, and larger effort/budget measurably deepens reasoning up to the model
cap. Unit tests cover the effort sources, forwarding, temp/top_p stripping, and
native reasoning-frame parsing.
* fix(chatcore): exempt opencode client from the default 128-tool truncation (#6193)
* fix(chatcore): exempt opencode client from the default 128-tool truncation
The default MAX_TOOLS_LIMIT (128) cap made truncateToolList blind-slice
tools.slice(0, 128), dropping opencode's built-in task tool and part of
its MCP tools when the inbound list exceeded 128 — so models routed
through OmniRoute could not launch subagents or reach all their tools.
Detect the opencode client (any x-opencode-* header, or 'opencode' in
the user-agent) and bypass ONLY the speculative 128 default. A known
provider ceiling (proactive PROVIDER_TOOL_LIMITS or a detected limit)
always wins and still truncates, even for opencode, so upstreams with
real hard limits (e.g. grok-cli 200) keep their 400-avoidance guard.
Non-opencode clients are unchanged.
- requestFormat.ts: add isOpencodeClient(headers, userAgent) + expose it
on resolveChatCoreRequestFormat.
- toolLimitDetector.ts: add getKnownToolLimit(); getEffectiveToolLimit
becomes getKnownToolLimit(provider) ?? DEFAULT_LIMIT (byte-identical
for existing callers).
- upstreamBody.ts: truncateToolList takes bypassDefaultToolLimit and
encodes the precedence; fix cosmetic debug-log count.
- chatCore.ts: thread the flag into prepareUpstreamBody.
- tests: extend tool-limit-detector unit tests.
* refactor(tools): accept nullable provider in tool-limit resolvers
Address PR review: widen getKnownToolLimit / getEffectiveToolLimit to
(provider: string | null | undefined) to match the call sites in
truncateToolList, and add unit assertions covering null/undefined
providers (getKnownToolLimit -> null, getEffectiveToolLimit -> 128).
---------
Co-authored-by: DKotsyuba <16292493+DKotsyuba@users.noreply.github.com>
Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com>
* fix(providers): refresh GitHub Copilot catalog (#6154)
* fix(providers): refresh github copilot catalog
Limit GitHub Copilot discovery to the curated supported model set and keep the provider cooldown panel client-safe by moving countdown formatting out of localDb.
* chore(quality): rebaseline providerPageHelpers.ts file-size (+13, #6154 copilot catalog)
The GitHub Copilot catalog refresh grows the provider-page model-section helper
(1021->1034). Fast-path PR->release skips check:file-size, so the bump lands with
the PR. Justification recorded in file-size-baseline.json.
Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>
---------
Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>
* chore(quality): rebaseline kiro-translator file-size debt from #6213
The #6213 kiro adaptive-thinking feature grew openai-to-kiro.ts (853->890) and its
test (1093->1234); the fast-path PR->release does not gate check:file-size on merge,
so the growth accumulated on the release tip. Rebaselined to keep the tip green.
Justification recorded in file-size-baseline.json.
* fix(doctor): resolve two false-positive WARNs (#6162) (#6163)
* fix(doctor): resolve two false-positive WARNs (#6162)
The `omniroute doctor` command reported two warnings on healthy installs
even though the underlying checks actually passed. Both came from the
doctor probing state that already worked; they looked like bugs but users
couldn't tell without manual digging.
Issue 1 — Server liveness HTTP 401
/api/health and /api/health/degradation both require the management
token. Doctor called them without auth → 401 → WARN, even when the
Next.js server was clearly alive and listening.
Fix: probe the configured health endpoint first; on 401/403, fall
back to a publicly served static asset (/favicon.ico) to confirm the
server is alive. WARN now only fires when both probes fail.
Issue 2 — CLI Tools '@/shared' import
tool-detector.ts (and 3 other cli-helper files) import @/shared/...
aliases that resolve via tsconfig.json paths. The CLI ships raw TS
source (no compile step) and runs through tsx, but tsx does not honor
tsconfig paths at runtime, and tsconfig-paths only hooks CJS
Module._resolveFilename while doctor uses ESM `import()`.
Fix: replace @/shared/... with relative imports in the 4 cli-helper
files. This is the same pattern these files already use for ./config-
generator/* imports. No new dependency, no architectural change, and
the fix doesn't regress Next.js itself which keeps using @/shared.
Verified on v3.8.43 (Node v24.17, Windows 11):
Before: 7 ok, 2 warning(s), 0 failure(s)
After: 8 ok, N warning(s), 0 failure(s)
where N accurately reflects which CLI tools are installed and
configured for OmniRoute (e.g. Hermes Agent installed but not
pointed at 20128 → 2 real warnings, not 1 false-positive).
Refs #6162
* fix(doctor): derive fallback URL from primary URL via new URL()
Per Gemini code-assist review feedback: the previous fallback constructed
the /favicon.ico URL from defaults (127.0.0.1:PORT) which ignored custom
host/port/protocol configurations supplied via:
- OMNIROUTE_DOCTOR_LIVENESS_URL
- OMNIROUTE_DOCTOR_HOST
- --liveness-url / --host CLI flags
Parse the primary URL with new URL() to preserve protocol, host, port, and
subpaths. The previous default-based fallback remains as a catch-all for
invalid primary URLs.
* test(doctor): add regression tests for #6162 fixes
Two new test files lock the fix and satisfy the PR Test Policy gate
("production code change without tests"):
- tests/unit/cli-helper-tool-detector-paths-6162.test.ts
Locks the @/shared → relative imports fix across all 4 cli-helper
files. Asserts (a) no @/shared alias remains in the cli-helper
sources, and (b) each file is importable at runtime via tsx/ESM,
which would have thrown "Cannot find package '@/shared'" before
the fix.
- tests/unit/cli-doctor-liveness-fallback-6162.test.ts
Locks the /favicon.ico fallback in doctor.mjs. Asserts the
fallback probe exists, derives its URL from the primary URL via
new URL() (per Gemini review feedback), and that the buggy
'Server responded with HTTP 401' WARN path is gone.
Both tests use only node:test + node:assert/strict so they slot into
the existing 'test' and 'test:unit' scripts with no extra config.
* test(doctor): fix primary.ok regex in fallback test
The earlier regex /primary\.ok\s*\?/ required a '?' immediately after,
but the actual doctor.mjs code uses a multi-line if-block:
if (primary.ok) {
return ok(...);
}
Use /\bprimary\.ok\b/ instead so the assertion matches the existing
branching.
---------
Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com>
* fix(doubao-web): switch provider to Dola global (#6235)
* fix(doubao-web): switch provider to Dola global
* fix(doubao-web): use .dola.com cookie domain for s_v_web_id + rebaseline test
The Dola switch left s_v_web_id with a host-only "www.dola.com" domain, which fails
the token-source contract (domain must start with "." or "http") — the sibling
sessionid/ttwid cookies and the canonical cookieDomain already use ".dola.com", which
also matches www.dola.com. Also rebaselines web-cookie-providers-new.test.ts (850->890)
for the provider-switch regression cases.
Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>
---------
Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com>
* fix(providers): register zed in OAuth PROVIDERS to fix Unknown provider error (#6041) (#6078)
Registers a minimal import_token entry for the existing Zed IDE keychain-import
provider so getProvider("zed") no longer throws "Unknown provider: zed" when the
UI probes the OAuth capability endpoint; generateAuthData returns { supported: false }.
Test runner fix: the regression test imported from "vitest" but lives in tests/unit/
(node:test territory, outside the vitest include globs) — it ran in no runner. Converted
to node:test + node:assert so it actually executes (8/8 green).
Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>
* fix(oauth): align zed in OAUTH_PROVIDER_IDS + config enum after #6078 merge
#6078 registered zed in the OAuth PROVIDERS registry but did not add it to the
constants PROVIDERS id map nor the oauth-providers-config enumeration test, leaving
that test red on the release tip (getProvider enumeration vs EXPECTED mismatch).
Adds ZED to the id constants + zed to EXPECTED_PROVIDER_KEYS/EXPECTED_CONFIG_BY_PROVIDER.
* fix(mitm): strip colons from macOS cert fingerprint before keychain match (#6134) (#6204)
fix(mitm): strip colons from macOS cert fingerprint before keychain match (#6134). Extracted testable macCertOutputHasFingerprint helper + regression guard. Thanks @rianonehub. Integrated into release/v3.8.45.
* docs(architecture): sync stale DB-layer counts (45+/55 → 95+/110+) in REPOSITORY_MAP, db-schema diagram and llm.txt (+42 i18n mirrors) (#6167)
docs(architecture): sync stale DB-layer counts (45+/55 → 95+/110+) across REPOSITORY_MAP, db-schema diagram, llm.txt + 42 i18n mirrors (#6167). Docs-only; check:docs-all passes locally on the reconstruction. Reds are pre-existing base-red drift on release/v3.8.45 (dast-smoke #6228; executor-kiro.test.ts eslint anys; changelog/package.json version drift) — none introduced by this PR. Integrated into release/v3.8.45.
* fix(api): count tool_use/tool_result/thinking blocks in count_tokens estimate (port from 9router#2337) (#6221)
fix(api): count tool_use/tool_result/thinking blocks in count_tokens estimate (#6221, port from 9router#2337). TDD-covered (6/6); typed the test casts to clear no-new-eslint. Reds are pre-existing base-red drift on release/v3.8.45 (dast-smoke #6228; executor-kiro.test.ts eslint anys; changelog/package.json version drift) — none introduced by this PR. Thanks @luweiCN. Integrated into release/v3.8.45.
* fix(antigravity): strip trailing assistant prefill turn for Vertex Claude models (#6114)
fix(antigravity): strip trailing assistant prefill for Vertex Claude models (#6114). TDD-covered (6/6), merged on TDD strength per owner. Reds are pre-existing base-red drift on release/v3.8.45. Thanks @anki1kr. Integrated into release/v3.8.45.
* fix(security): require management auth for mutable cloud routes (#6233) (#6233)
fix(security): require management auth for mutable cloud routes (#6233). Verified: 3 PR tests + full authz/route-guard suite 241/241 green. Thanks @vittoroliveira-dev. Integrated into release/v3.8.45.
* fix(dashboard): use connection.id (UUID) not connection.provider (category) in onboarding wizard href (issue #6144) (#6166)
refactor(dashboard): extract tested buildProviderDetailsHref helper for onboarding wizard (#6166). Behavioral #6144 fix already on tip via #6145; this lands the tested-helper hardening. Thanks @KooshaPari. Integrated into release/v3.8.45.
* feat(rankings): add 'Configured Only' filter to Free Provider Rankings page (#6245)
feat(rankings): add 'Configured Only' filter to Free Provider Rankings (#6245, closes #6150). 9/9 test green. Thanks @Iammilansoni. Integrated into release/v3.8.45.
* fix(i18n): add 118 missing Italian translations (#6212)
i18n(it): add 118 Italian translations (#6212). Audited net-additive (0 keys dropped, valid JSON). Thanks @serverless83. Integrated into release/v3.8.45.
* test(dashboard): realign #6145 onboarding-href guard to the #6166 helper refactor (#6270)
Realign the #6145 onboarding-href guard to the #6166 helper refactor (buildProviderDetailsHref). Test-only; unblocks the fast-path unit job across the open PR queue. Base-reds only (dast-smoke #6228, docs version-drift, executor-kiro anys). Integrated into release/v3.8.45.
* feat(providers): add Yuanbao (web) cookie-session provider (#6196) (#6256)
feat(providers): add Yuanbao (web) cookie-session provider (#6196). TDD-covered; base-reds only (dast-smoke #6228, docs version-drift, executor-kiro anys — #6145 guard fixed on tip via #6270). Integrated into release/v3.8.45.
* feat(providers): route built-in agentrouter through dynamic CC wire image (#6056) (#6255)
feat(providers): route built-in agentrouter through dynamic CC wire image (#6056). TDD-covered (agentrouter-cc-wire-image.test.ts). Base-reds only. Integrated into release/v3.8.45.
* feat(providers): bulk-add API keys for Cloudflare Workers AI (#6174) (#6254)
feat(providers): bulk-add API keys for Cloudflare Workers AI (#6174). Per-entry providerSpecificData (fixes shared-object reuse); TDD guard bulk-api-key-parser-cloudflare.test.ts. Base-reds only. Integrated into release/v3.8.45. (thanks @muflifadla38)
* feat(dashboard): routing/settings UX clarity — share %, Cloud Sync rename, base-URL override (#6147) (#6253)
feat(dashboard): routing/settings UX clarity (#6147) — effective share %, Cloud Sync→Remote Settings Sync rename, opt-in advanced base-URL override. TDD guard routing-settings-ux-6147.test.ts (6/6). Base-reds only. Integrated into release/v3.8.45.
* feat(combo): add option to disable session stickiness (#6168) (#6252)
feat(combo): add option to disable session stickiness (#6168) — per-combo/global, precedence config→settings→false (preserves #3825). TDD guard combo-disable-session-stickiness.test.ts (8/8). Base-reds only. Integrated into release/v3.8.45. (thanks @RCrushMe)
* feat(docker): OMNIROUTE_NO_SUDO env flag for root-less MITM cert trust (#6122) (#6249)
feat(docker): OMNIROUTE_NO_SUDO env flag for root-less MITM cert trust (#6122). resolveSudoSpawn strips sudo when set; argv-array spawn preserved (Hard Rule #13). TDD guard mitm-systemCommands-no-sudo.test.ts (5/5). Base-reds only. Integrated into release/v3.8.45. (thanks @powellnorma)
* feat(providers): add Requesty as an OpenAI-compatible gateway provider (#6120) (#6250)
feat(providers): add Requesty as an OpenAI-compatible gateway provider (#6120). Fixed the APIKEY_PROVIDERS count guard (167→168) the feature had missed. TDD guard requesty-provider.test.ts (4/4) + providers-constants-split (4/4). Base-reds only. Integrated into release/v3.8.45. (thanks @chirag127)
* fix(providers): remove deprecated MiMo v2 entries (#6248)
chore(providers): remove deprecated MiMo V2 catalog entries (superseded by V2.5); realign provider-catalog tests. Merged — thank you, @backryun! Base-reds only. Integrated into release/v3.8.45.
* fix(github-skills): add missing import, add unit tests, fix settings JSON parse (#6186)
feat(skills): GitHub skill-discovery subsystem — search/score/scan/import agent skills from GitHub, MCP tools (scope-gated) + /api/github-skills route (host-pinned api.github.com, sanitized errors). Merged — thank you, @Moseyuh333! Pre-merge fixes: passed toolDef.scopes so tools gate correctly under scope enforcement, routed the GET error path through sanitizeErrorMessage+500 (Hard Rule #12), and realigned the agent-skills count guards (catalog/routes/generator/mcp) for the new catalog entry. Base-reds only. Integrated into release/v3.8.45.
* Fix/5976 continued (#6216)
fix(combo): 5 streaming-path fixes (#5976) — locked-stream 500, error-frame-only-if-no-content, Gemini MALFORMED_RESPONSE→content_filter failover, correlationId substring, per-model-500 lockout skip + request-logger UI. Merged — thank you, @hartmark! Maintainer follow-up: releaseQualityClone cancels the abandoned quality-check tee branch (per-request memory) + regression test. All 41 combo/streaming quality tests green. Base-reds only. Integrated into release/v3.8.45.
* feat(dashboard): filter Free Provider Rankings by configured/available (#6150) (#6251)
feat(dashboard): configured-only / available-only filters on Free Provider Rankings (#6150) — server-side query params + tested lib helper; supersedes the client-side #6245 toggle with an available-only dimension. Lib logic 11/11 green; UI validated live on VPS. Base-reds only. Integrated into release/v3.8.45.
* ci: unblock test jobs from the Build gate (start at minute 0) (#6275)
test-unit x8, test-vitest, test-integration x2 and test-security all had
needs: build but never download the next-build artifact — the dependency
only serialized ~20min of Build wall-clock in front of every test run.
Switch them to needs: changes with the same skip condition Build uses
(docs-only PRs and drafts still skip), so the test chain
(test-unit -> test-coverage -> quality-gate -> sonarqube) now runs in
parallel with Build instead of after it.
Jobs that genuinely consume the artifact keep needs: build unchanged:
test-e2e x9, package-artifact, electron-package-smoke.
Expected effect on a full ci.yml run: critical path drops from
build + tests (~30min+) to max(build, tests) — roughly 15-20min saved
per run, no extra runner minutes beyond starting the same jobs earlier.
* ci(build): switch Next.js production build to Turbopack (1.9x faster) (#6273)
Next 16 ships Turbopack as the stable production bundler. Benchmarked on a
32-core box against the same tree: webpack 1035s -> Turbopack 539s (1.92x).
The build script already supports the switch via OMNIROUTE_USE_TURBOPACK=1
(scripts/build/build-next-isolated.mjs) and next.config.mjs already mirrors
the resolveAlias stubs in the turbopack block, so this only flips the CI env.
The webpack .build/next/cache actions/cache step is removed in the same
commit: Turbopack does not use the webpack cache dir (its persistent FS
cache is experimental and NOT enabled), so restoring ~0.5 GB per run would
be pure wasted download. Revert restores webpack + its cache together.
Validation: standalone output smoke-tested (server.js boots, health 200,
dashboard 307); the 428 build warnings are the known benign 'overly broad
file pattern' static-analysis notices for dynamic fs usage (covered at
runtime by outputFileTracingIncludes). Downstream e2e x9, package-artifact
and electron-package-smoke consume this artifact, so a green CI run here
validates the Turbopack artifact end-to-end. nightly-compat and npm-publish
stay on webpack until this PR proves out.
* feat(build): make Turbopack the default bundler for dev and build (#6283)
Turbopack (stable in Next 16) becomes the code default in the three entry
points that previously required an explicit OMNIROUTE_USE_TURBOPACK=1:
- scripts/build/build-next-isolated.mjs (production build)
- scripts/dev/run-next.mjs (dev server)
- scripts/dev/run-next-playwright.mjs (playwright dev runner)
OMNIROUTE_USE_TURBOPACK=0 remains the webpack escape hatch (Windows /
native-binding / bundler-compat issues), and only the documented '0'
opts out — junk values keep the default.
Benchmarked on this codebase (same tree, Next 16.2.9): webpack 1035s vs
Turbopack 539s on a 32-core box; ~20min vs 6min59s on ubuntu-latest.
Artifact validated end-to-end (standalone smoke + e2e/package-artifact/
electron-package-smoke CI jobs, Docker amd64+arm64 builds clean with the
v3.8.27 ImportTracer panic gone on 16.2.9).
TDD: tests/unit/build-bundler-default-turbopack.test.ts (new) +
run-next-playwright.test.ts extended with the unset-env default case;
both red before the flip, green after. ENVIRONMENT.md updated.
* feat(docker): build the image with Turbopack (v3.8.27 panic gone on Next 16.2.9) (#6285)
Flips the builder stage to OMNIROUTE_USE_TURBOPACK=1 and rewrites the stale
panic comment: the v3.8.27-era TurbopackInternalError ('entered unreachable
code: there must be a path to a root' in ImportTracer::get_traces) no longer
reproduces on Next 16.2.9.
Validation (2026-07-05, this exact Dockerfile, default
OMNIROUTE_BUILD_MEMORY_MB=4096, no overrides):
- amd64: docker build 659s, exit 0, zero panic/OOM strings in the full log,
container smoke-tested (/api/monitoring/health 200)
- arm64 (qemu): exit 0, zero panic strings
Webpack stays available as the escape hatch:
--build-arg / -e OMNIROUTE_USE_TURBOPACK=0. The V8 heap ceiling is kept:
Turbopack's compile is native Rust, but prerender/export still runs on V8.
* ci: opt-in self-hosted VPS runners for the release window (anti-queue) (#6284)
Adds the on-demand self-hosted runner plumbing for /generate-release:
- scripts/vps/release-runner-up.sh: starts the runner VM on Proxmox, waits
for >=1 'omni-release' runner to report online via the GitHub API, then
flips the USE_VPS_RUNNER repo variable to true. Any failure/timeout sets
it back to false and exits 1 so the caller falls back to hosted runners.
- scripts/vps/release-runner-down.sh: flips USE_VPS_RUNNER=false FIRST
(so no job gets scheduled onto a dying runner), then gracefully shuts
the VM down. Idempotent.
- ci.yml: build, test-unit x8 and test-vitest pick their runner
dynamically. Self-hosted is used ONLY when USE_VPS_RUNNER == 'true'
AND the event is own-origin (push/dispatch, or a PR whose head repo is
this repository). Fork PRs and the var's default/absent state always
fall back to ubuntu-latest.
Why: the Free plan caps hosted concurrency at 20 jobs; a release run
saturates it and queues. The benchmarked VPS (32-core, 4 runners) matches
hosted per-job times (unit ~8.5min/shard, build 9min turbo) but eliminates
the queue, which is the real release bottleneck (~30-50min on a busy day).
Scope is conservative: only the three job families benchmarked on the VPS;
e2e/electron/integration stay hosted (playwright/xvfb provisioning not
validated on the runner workspace yet).
* docs(changelog): restore v3.8.45/v3.8.44 sections eaten by the #6193 merge auto-resolve + CI-perf campaign bullets (#6273 #6275 #6283 #6284 #6285)
* fix(dashboard): null-guard connection in EditConnectionModal base-URL override (#6147) (#6287)
fix(dashboard): null-guard connection in EditConnectionModal (#6147) — fixes 'Cannot read properties of null (reading authType)' crash on every provider-detail page entry. TDD: connModals.test.tsx null-mount 9/9. Base-reds only. Integrated into release/v3.8.45.
* chore(release-green): clear test-masking + docs-all HARD reds for the v3.8.45 pre-flight
- test-masking: allowlist the 4 verified-legitimate assert reductions of the
cycle (#6248 MiMo V2 removal, #6170 Kiro catalog correction, #6154 Copilot
catalog refresh) and register the #6164 AutoRoutingBanner test deletion with
a real replacement guard (tests/unit/home-no-autorouting-banner.test.ts —
asserts the banner stays out of home/page.tsx and the component stays deleted)
- docs-sync: executors count 68 -> 73 in ARCHITECTURE.md + CODEBASE_DOCUMENTATION.md
- env-doc-sync: document OMNIROUTE_NO_SUDO (#6249/#6122) in .env.example +
docs/reference/ENVIRONMENT.md
* fix(quality): clear the cycle's 11 net-new ESLint errors + make validate-release-green suppressions-aware
- executor-kiro/save-call-log/call-logs-correlation tests: replace 15 'as any'
casts with typed shapes (net-new no-explicit-any errors from #6213/#6216);
prune the now-empty suppression entries so the frozen baseline stays exact
- github-skills + usage/call-logs routes: raw toLowerCase().includes() search
replaced by matchesSearch() (no-restricted-syntax — Turkish-safe search,
behavior covered by tests/unit/call-logs-correlation-substring.test.ts and
tests/unit/github-collector.test.ts)
- validate-release-green.mjs: run ESLint with --suppressions-location (match
the npm run lint contract — frozen debt is not a release red) and raise the
lint timeout 15->30min (a full pass takes ~14min alone; the 15min ceiling
expired under concurrent suite load and surfaced as 'could not parse eslint
json')
* fix(skills): generate the missing omni-github-skills registry entry + align catalog count tests
PR #6186 added omni-github-skills to the agent-skills catalog (API 22 -> 23)
but did not run the generator, so skills/omni-github-skills/SKILL.md never
existed and 6 integration assertions split between the old (42/43) and new
counts. Generated via scripts/skills/generate-agent-skills.mjs --apply and
aligned agent-skills-discovery to the real totals (43 = 23 API + 20 CLI;
handlers return 44 with config-codex-cli). 30/30 discovery+content tests green.
* fix(combo): restrict the #6216 empty-stream failover to truly empty bodies (restores #3399/#3685 contracts)
The 'streaming no recognized content' branch added by #6216 marked ANY
stream that ended without content deltas as invalid — sweeping in two
regression-guarded pass-through contracts: an empty stream terminated by an
explicit 'data: [DONE]' (#3399 context-cache protection) and an incomplete
Claude lifecycle (ping only, no message_start; #3685 — stream-readiness
timeout territory, not failover). Both unit guards were red on the branch
and green on main (86/86 vs 84/86).
The branch now fires only for a truly EMPTY body (zero bytes — the Gemini
HTTP-200-empty case that motivated #6216), tracked via sawAnyBytes. New
guard: '#5976 truly EMPTY streaming body (zero bytes) -> invalid for combo
failover'. 87/87 across both files.
Also in this pre-flight batch:
- agentSkillTools-mcp: api.have upper bound 22 -> 23 (the #6186 catalog
addition updated the totals but missed this bound)
- delete tests/unit/free-provider-rankings-configured-filter.test.ts:
#6251 (server-side configuredOnly/availableOnly) superseded the #6245
client-side toggle it pinned; replacement declared in the test-masking
allowlist (tests/unit/freeProviderRankings-filters.test.ts, 11/11)
* chore(quality): prune stale ESLint suppressions (4,273 -> 4,233)
Entries whose violations no longer exist (cleaned by cycle merges and the
pre-flight fixes) made 'npm run lint' exit 2 with 'suppressions left that do
not occur anymore'. Regenerated via --prune-suppressions; net-new policy
unchanged.
* fix(proxy): #6246 stop the v3.8.44 proxy IP-leak + over-deactivation regression (#6296)
Merged into release/v3.8.45. Reds pré-existentes classificados: dast-smoke (infra), check:file-size (drift de baseline de god-files congelados), e 3 testes de contagem agentSkills stale (43→44 já corrigidos no tip pelo #6186 — somem no squash sobre o tip). Núcleo do fix #6246 (proxy IP-leak + over-deactivation).
* fix(proxy): make "Test All" read-only + add bulk enable/disable (#6246) (#6299)
Merged into release/v3.8.45. Delta do par #6246 (Test-All read-only + bulk enable/disable), reconciliado com o núcleo #6296 já mergeado. CHANGELOG restaurado (ambos bullets do #6246 coexistem). Reds pré-existentes: dast-smoke (infra) + file-size drift.
* fix(resilience): evict sticky affinity on pinned-account failover (#6219) (#6231)
Merged into release/v3.8.45. Sticky affinity failover (#6219). Sincronizado com tip; CHANGELOG restaurado (base bullets preservados, net +1). Reds pré-existentes: dast-smoke + file-size drift.
* fix(sse): drop commentary-phase text in Responses passthrough (#6199) (#6232)
Merged into release/v3.8.45. Responses commentary-phase filter (#6199). Sincronizado; CHANGELOG restaurado net +1. Reds: dast-smoke + file-size drift.
* fix: bug-fix sweep — log path, AgentBridge DNS, opencode-go headers, GitLab Duo tools, M365 EDU (#6197 #6127 #6198 #5997 #6220 #6210) (#6234)
Merged into release/v3.8.45. Bug-fix sweep (#6197 log path, #6127/#6198 AgentBridge DNS, #6210 M365 EDU, #6220 GitLab Duo tools, #5997 opencode-go headers). 27 testes verdes. CHANGELOG restaurado net +5. Reds: dast-smoke + file-size drift.
* fix(docker): add id= to BuildKit cache mounts for strict builders (#6291)
Merged into release/v3.8.45. Dockerfile-only: explicit id= on BuildKit cache mounts (fixes strict-frontend parse error). Reds pré-existentes (dast-smoke/file-size drift) não relacionados a mudança de Dockerfile. Thanks @karimalsalah.
* fix(sse): strip zero-width markers from streamed tool-call arguments (follow-up to #5857) (#6292)
Merged into release/v3.8.45. Strip zero-width markers from streamed tool-call arguments (#5857 follow-up). Test 50/50. CHANGELOG reconciled net +1. Reds pré-existentes (dast-smoke/file-size drift). Thanks @DKotsyuba.
* ci(quality): merge-integrity fast-gates + pre-flight hermetic mode (#6300)
Merged into release/v3.8.45. CI merge-integrity fast-gates (changelog-integrity + agent-skills-sync) + pre-flight hermetic mode. O gate novo detectou 11 SKILL.md gerados fora de sync (drift pré-existente no tip: omni-api-keys endpoints, omni-github-skills do #6186) — regenerados neste PR, Merge-integrity GREEN no CI. Reds restantes base-red (dast-smoke/file-size drift/live-data flaky). Testes novos 17/17.
* fix(a2a): finish the #6186 catalog-count update — 3 hardcoded 22s left in production
#6186 added omni-github-skills (API 22 -> 23) and updated computeCoverage's
total, but left the old count hardcoded in the A2A layer: listCapabilities
metadata reported coverage.api.total 22 (type literal + value) and
SkillCoverageSchema pinned z.literal(22) — so the schema would REJECT the
correct runtime value. Aligned all three to 23 + the unit fixtures
(listCapabilities-a2a, agentSkills-schemas, 46/46 with agentSkillTools-mcp).
* fix(quality): type the 7 net-new 'as any' casts from #6292 (Lint red on the release tip)
#6292 rewrote/added zero-width-marker tests with 7 new 'as any' result casts,
pushing the file past its frozen suppression (84) — and when violations
exceed the suppressed count ESLint reports ALL of them, so the ci.yml Lint
job went red with 90 errors. The 7 new sites get typed shapes (delta /
arguments / choices / output accessors — same pattern as fecf888fd); the
pre-existing debt stays frozen at the exact new count (83). 50/50 tests
green, file lint-clean under the suppressions baseline.
* fix(api): Zod-validate POST /api/github-skills + document new gate envs + pin merge-integrity actions
Three latent heavy-CI reds surfaced by the VPS validation dispatch (the fast
path never runs these gates):
- t06 route-validation: POST /api/github-skills destructured request.json()
blind — a non-array 'targets' would .map-crash. Now validateBody(zod)
with defaults preserved (Hard Rule #7). Guard:
tests/unit/github-skills-route-validation.test.ts (4/4).
- env-doc-sync: document OMNIROUTE_SKIP_SYSTEM_TRUST (#6310) and the
changelog-integrity gate envs CHANGELOG_BASE_REF/ALLOW_CHANGELOG_REMOVALS
(#6300) in .env.example + ENVIRONMENT.md.
- zizmor ratchet: the new merge-integrity job's checkout/setup-node uses were
unpinned (+1 finding, 160 > baseline 159); pinned by SHA -> 158 (< baseline).
* fix(quality): clear the 2 remaining heavy-gate reds on the release tip
- check:error-helper: githubSkillTools.ts (#6186 wave) built MCP install
error results with raw err.message — routed through sanitizeErrorMessage()
(Hard Rule #12; 19/19 agentSkillTools tests green)
- check:mutation-test-coverage: tests/unit/combo-provider-cooldown-sibling.test.ts
(#6216) was missing from stryker.conf tap.testFiles — added so its mutant
kills count on nightly-mutation
* fix(security): 405 method-first for /api/keys/{id}/devices (dast-smoke QUERY check)
Schemathesis's newer unsupported-methods check (unpinned tool drift) sends
QUERY /api/keys/{id}/devices and demands 405 Method Not Allowed; the path had
no HIGH_RISK_METHOD_RULES entry, so the auth layer answered 401 first. Add
the devices rule (GET-only) so undocumented methods get a clean method-first
405 — same pattern as the v3.8.44 TRACE fix. TDD:
tests/unit/dast-method-not-allowed.test.ts gains the devices QUERY case (4/4).
* fix(mitm): test suite and CI must never mutate the OS trust store (OMNIROUTE_SKIP_SYSTEM_TRUST) (#6310)
Incident 2026-07-05 on the self-hosted release runner (VM 113): the
integration test 'POST /cert: installs trust when cert exists' exercised the
REAL install path, wrote a 105-byte fake PEM (FakeMITMCertForTestingOnly)
into /usr/local/share/ca-certificates and update-ca-certificates baked the
invalid entry into ca-certificates.crt — breaking ALL system TLS on the VM
(curl error 77, apt cert failures, and the intermittent gzip-corrupted
next-build artifacts that failed 6/9 e2e shards in run 28754447912). Hosted
runners are ephemeral, so the same mutation went unnoticed for months.
- installCert/uninstallCert: skip the OS dispatch under
OMNIROUTE_SKIP_SYSTEM_TRUST=1 — AFTER the input checks, so the #4546
environment-skip contract (missing file throws -> structured skip) and the
already-installed/not-installed early returns are preserved.
- installTproxyCa/uninstallTproxyCa: same guard, only when no run dep is
injected (DI'd tests keep exercising the full command sequence with mocks).
- tests/_setup/isolateDataDir.ts sets the env for every node:test process;
ci.yml/quality.yml/nightly-release-green.yml set it workflow-wide (e2e runs
the real app outside the test setup).
TDD: tests/unit/system-trust-test-guard.test.ts (guard exported to every test
process; guarded install resolves on a real file without touching the OS;
missing-file contract preserved). 82/82 across the affected cert/tproxy/
agent-bridge suites.
* ci(vps): hermetic nightly pre-flight on the release runner (descoped: e2e/integration/electron stay hosted) (#6305)
* ci(vps): extend the dynamic omni-release runner to e2e/integration/electron + nightly pre-flight
Completes the #6284 rollout to the jobs where the VPS pays the most:
- test-e2e (9 shards, 15-20min/shard hosted — dominated by setup, not tests),
test-integration (2 shards) and electron-package-smoke now pick the
self-hosted omni-release runner under the same gate: vars.USE_VPS_RUNNER
== 'true' AND own-origin (fork PRs never reach self-hosted).
- nightly-release-green (the release pre-flight) becomes runner-dynamic too:
on the VPS it runs in a clean env — no operator OMNIROUTE_API_KEY, no
local noauth CLIs — eliminating the machine-specific false positives that
dominated the 2026-07-05 pre-flight; passes --hermetic (no-op until the
#6300 validator lands, then belt-and-suspenders).
Validation plan (per operator request): release-runner-up.sh -> full ci.yml
workflow_dispatch on this branch exercising e2e/integration/electron on the
VPS (proves playwright --with-deps + xvfb on the runner) -> down.sh -> VM
off verified.
* fix(mitm): test suite and CI must never mutate the OS trust store (OMNIROUTE_SKIP_SYSTEM_TRUST)
Incident 2026-07-05 on the self-hosted release runner (VM 113): the
integration test 'POST /cert: installs trust when cert exists' exercised the
REAL install path, wrote a 105-byte fake PEM (FakeMITMCertForTestingOnly)
into /usr/local/share/ca-certificates and update-ca-certificates baked the
invalid entry into ca-certificates.crt — breaking ALL system TLS on the VM
(curl error 77, apt cert failures, and the intermittent gzip-corrupted
next-build artifacts that failed 6/9 e2e shards in run 28754447912). Hosted
runners are ephemeral, so the same mutation went unnoticed for months.
- installCert/uninstallCert: skip the OS dispatch under
OMNIROUTE_SKIP_SYSTEM_TRUST=1 — AFTER the input checks, so the #4546
environment-skip contract (missing file throws -> structured skip) and the
already-installed/not-installed early returns are preserved.
- installTproxyCa/uninstallTproxyCa: same guard, only when no run dep is
injected (DI'd tests keep exercising the full command sequence with mocks).
- tests/_setup/isolateDataDir.ts sets the env for every node:test process;
ci.yml/quality.yml/nightly-release-green.yml set it workflow-wide (e2e runs
the real app outside the test setup).
TDD: tests/unit/system-trust-test-guard.test.ts (guard exported to every test
process; guarded install resolves on a real file without touching the OS;
missing-file contract preserved). 82/82 across the affected cert/tproxy/
agent-bridge suites.
* ci(vps): descope — e2e/integration/electron stay on hosted runners; keep the hermetic nightly pre-flight dynamic
Validation verdict (runs 28754447912 + 28757670732, VM 113):
- e2e cannot run >1 per VM: both jobs bind port 20128 ('already used') — needs
a per-job port in the playwright runner before any VM rollout.
- concurrent ~1GB artifact downloads truncate on the VM uplink (gzip 'invalid
compressed data' — with 2 runners the e2e shard passed; corruption returned
at 4) — actions/download-artifact has no integrity retry here.
- integration shard 2 exceeded its 15-min timeout twice on the VM.
The VPS remains a win for whole-machine jobs: build/unit/vitest (already
dynamic via #6284) and nightly-release-green (single job, clean env, hermetic
pre-flight) — which this PR keeps.
* chore(quality): v3.8.45 cycle-close file-size rebaseline (Phase 0 drift absorption)
13 files grown by the cycle's merged PRs (#6216 streaming+request-logger UI;
#6251/#6253 dashboard UX) — legitimate merged-feature growth absorbed by the
release captain per the Phase 0 drift policy; all entries stay frozen (cannot
grow further). Justification key: _rebaseline_2026_07_06_v3845_release_close.
* chore(quality): v3.8.45 cycle-close cognitive/cyclomatic rebaseline (Phase 0 drift absorption)
cognitive 867->877 (+10), cyclomatic 2028->2035 (+7) — inherited cycle drift
measured by check:release-green (hermetic) on the release tip; the captain's
pre-flight fixes are gate/test/workflow changes (complexity-neutral).
Justification keys: _rebaseline_2026_07_06_v3845_release_close.
* docs(changelog): v3.8.45 reconciliation — fold Unreleased into the version section, 30 missing bullets, contributors hall
Phase 0a reconciliation (/generate-release): every commit since v3.8.44 now
has a bullet or a Maintenance rollup (82 commits; the only ref-less residues
are the bump/recording commits); #6193 bullet gains its PR ref; #6298
diagnosis credited (@subhansh-dev, landed via #6234); [3.8.44] header dated;
'### 🙌 Contributors' table injected (27 external + maintainer) + 42 i18n
mirrors resynced.
* chore(release): v3.8.45 — 2026-07-06
* fix(resilience): 502/503/504 keep the connection-unavailability path — only the exact 500 skips lockout (#5976 contract)
The #6216 branch used 'status >= 500', so a 503 on a per-model-quota /
openai-compatible provider returned cooldownMs 0 — no model lockout AND no
connection cooldown — hot-looping the failing upstream and breaking the
resilience-http-e2e 'priority combo falls back on 503' guard on the release
PR (the request after a 503 hit the same primary again). #6216's OWN unit
contract pins the narrow behavior ('Gemini 503 should NOT skip cooldown',
combo-provider-cooldown-sibling.test.ts) but computes the condition inline
instead of exercising auth.ts. Code aligned to the tested contract:
status === 500 skips (intermittent, not model-specific); 502/503/504 keep
the pre-#6216 model-lockout path. Validated: the failing integration test
flips red -> green locally (19s, deterministic before).
* fix(security): crypto-backed randomNumericId in doubao-web (CodeQL js/insecure-randomness)
The synthetic Dola device/web id was built from Math.random; CodeQL flags it
as insecure randomness in a security context. Not a secret, but
crypto.getRandomValues costs the same and closes alert #692 at the source.
Doubao executor tests 14/14 green. Alerts #693-695 (incomplete-url-substring
in unit-test asserts) dismissed as false positives per the v3.8.35 precedent
(Hard Rule #14).
* chore(quality): shave the #5976 fix comment back under the auth.ts file-size freeze (2447)
---------
Co-authored-by: Danny S <36470572+kanztu@users.noreply.github.com>
Co-authored-by: Luis Alejandro Vega <LuisAlejandroVega@redesprivadasvirtuales.com>
Co-authored-by: Milan Soni <123074437+Iammilansoni@users.noreply.github.com>
Co-authored-by: R. Beltran <rbeltran8000@gmail.com>
Co-authored-by: VXNCXNX <93332837+VXNCXNX@users.noreply.github.com>
Co-authored-by: Denis Kotsyuba <kocubads96@gmail.com>
Co-authored-by: DKotsyuba <16292493+DKotsyuba@users.noreply.github.com>
Co-authored-by: backryun <bakryun0718@proton.me>
Co-authored-by: Aris <arissunandar399@gmail.com>
Co-authored-by: Ankit <177378174+anki1kr@users.noreply.github.com>
Co-authored-by: Rian Priskanova <rian@evercore.technology>
Co-authored-by: Vittor Guilherme Borges de Oliveira <vittoroliveira.dev@gmail.com>
Co-authored-by: KooshaPari <42529354+KooshaPari@users.noreply.github.com>
Co-authored-by: serverless83 <35410475+serverless83@users.noreply.github.com>
Co-authored-by: Moseyuh333 <148680980+Moseyuh333@users.noreply.github.com>
Co-authored-by: Markus Hartung <mail@hartmark.se>
Co-authored-by: HenryHaniHannoush <karimmalsalah@gmail.com>
7172 lines
200 KiB
YAML
7172 lines
200 KiB
YAML
openapi: 3.1.0
|
||
info:
|
||
title: OmniRoute API
|
||
version: 3.8.45
|
||
description: |
|
||
OmniRoute is a local-first AI API proxy router. It provides an OpenAI-compatible
|
||
endpoint that routes requests to multiple AI providers with load balancing,
|
||
failover, and usage tracking.
|
||
|
||
## Base URLs
|
||
- **Local**: `http://localhost:20128`
|
||
|
||
## Authentication
|
||
All proxy endpoints require a Bearer token (API key managed via the dashboard).
|
||
Management endpoints are protected when `requireLogin` is enabled.
|
||
contact:
|
||
name: OmniRoute
|
||
license:
|
||
name: MIT
|
||
|
||
servers:
|
||
- url: http://localhost:20128
|
||
description: Local development
|
||
|
||
tags:
|
||
- name: Playground
|
||
description: Playground Studio — preset management and prompt improvement
|
||
- name: Memory
|
||
description: Conversational memory management — CRUD, engine status, playground preview, summarization, reindex, and Qdrant settings (plan 21 — v3.8.6). All routes require management auth.
|
||
- name: Chat
|
||
description: OpenAI-compatible chat completions
|
||
- name: Messages
|
||
description: Anthropic-compatible messages
|
||
- name: Responses
|
||
description: OpenAI Responses API
|
||
- name: Embeddings
|
||
description: Text embedding generation
|
||
- name: Images
|
||
description: Image generation
|
||
- name: Audio
|
||
description: Audio speech and transcription
|
||
- name: Moderations
|
||
description: Content moderation
|
||
- name: Rerank
|
||
description: Document reranking
|
||
- name: Models
|
||
description: Available model listing
|
||
- name: Providers
|
||
description: Provider connection management
|
||
- name: Provider Nodes
|
||
description: Provider node configuration
|
||
- name: API Keys
|
||
description: API key management
|
||
- name: Combos
|
||
description: Routing combo management
|
||
- name: Settings
|
||
description: Application settings
|
||
- name: Compression
|
||
description: Prompt compression, RTK filters, Caveman rules, and compression combos
|
||
- name: Usage
|
||
description: Usage analytics and logs
|
||
- name: Translator
|
||
description: Format translation debug & testing
|
||
- name: CLI Tools
|
||
description: CLI tool configuration management
|
||
- name: Embedded Services
|
||
description: >-
|
||
Install, start, stop, and monitor locally-running embedded services (9Router, CLIProxyAPI).
|
||
All routes are LOCAL_ONLY — accessible from loopback only (hard rule #17).
|
||
- name: OAuth
|
||
description: OAuth flows for provider authentication
|
||
- name: System
|
||
description: System management (restart, shutdown, backup)
|
||
- name: Pricing
|
||
description: Model pricing configuration
|
||
- name: Cloud
|
||
description: Cloud worker authentication and sync
|
||
- name: Fallback
|
||
description: Fallback chain management
|
||
- name: Telemetry
|
||
description: Telemetry and token health monitoring
|
||
- name: Agent Skills
|
||
description: >-
|
||
Agent Skills catalog — 42 SKILL.md files (22 REST API + 20 CLI) for external agents,
|
||
MCP clients, and A2A orchestrators to discover OmniRoute capabilities.
|
||
- name: AgentBridge
|
||
description: >-
|
||
MITM proxy manager for 9 IDE agents (Antigravity, Kiro, Copilot, Codex, Cursor, Zed,
|
||
Claude Code, Open Code, Trae). Controls server lifecycle, DNS/model mappings, bypass list,
|
||
and cert management. All routes are LOCAL_ONLY + SPAWN_CAPABLE (hard rules #15, #17).
|
||
See docs/frameworks/AGENTBRIDGE.md.
|
||
- name: Traffic Inspector
|
||
description: >-
|
||
LLM-aware HTTPS traffic debugger with 4 capture modes (AgentBridge, Custom Hosts,
|
||
HTTP_PROXY :8080, System-wide). Provides real-time WebSocket stream, session recording,
|
||
HAR export, SSE merge, and conversation normalization.
|
||
All routes are LOCAL_ONLY + SPAWN_CAPABLE (hard rules #15, #17).
|
||
See docs/frameworks/TRAFFIC_INSPECTOR.md.
|
||
|
||
paths:
|
||
# --- Playground + Search Tools (plans 17+18) ---
|
||
/api/playground/improve-prompt:
|
||
post:
|
||
tags:
|
||
- Playground
|
||
summary: Improve prompt via LLM
|
||
description: |
|
||
Rewrites the supplied system prompt and/or user prompt using a meta-prompt
|
||
(inspired by Anthropic Console Prompt Improver). Internally calls
|
||
`/v1/chat/completions` with the model specified in the request body.
|
||
Quota is consumed from the caller's account.
|
||
security:
|
||
- BearerAuth: []
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required:
|
||
- model
|
||
properties:
|
||
system:
|
||
type: string
|
||
maxLength: 50000
|
||
description: System prompt to improve (at least one of system/prompt required)
|
||
prompt:
|
||
type: string
|
||
maxLength: 50000
|
||
description: User prompt to improve
|
||
model:
|
||
type: string
|
||
description: Model to use for the improvement call (e.g. openai/gpt-4o)
|
||
tone:
|
||
type: string
|
||
enum:
|
||
- concise
|
||
- detailed
|
||
default: concise
|
||
responses:
|
||
"200":
|
||
description: Improved prompt(s)
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
improvedSystem:
|
||
type: string
|
||
improvedPrompt:
|
||
type: string
|
||
tokensIn:
|
||
type: integer
|
||
tokensOut:
|
||
type: integer
|
||
"400":
|
||
$ref: "#/components/responses/BadRequest"
|
||
"401":
|
||
$ref: "#/components/responses/Unauthorized"
|
||
/api/playground/presets:
|
||
get:
|
||
tags:
|
||
- Playground
|
||
summary: List playground presets
|
||
description: Returns all saved playground presets ordered by creation date (newest first).
|
||
security:
|
||
- BearerAuth: []
|
||
responses:
|
||
"200":
|
||
description: Preset list
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
presets:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/PlaygroundPreset"
|
||
"401":
|
||
$ref: "#/components/responses/Unauthorized"
|
||
post:
|
||
tags:
|
||
- Playground
|
||
summary: Create playground preset
|
||
description: Saves the current playground configuration as a named preset in the database.
|
||
security:
|
||
- BearerAuth: []
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/PlaygroundPresetCreate"
|
||
responses:
|
||
"201":
|
||
description: Created preset
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/PlaygroundPreset"
|
||
"400":
|
||
$ref: "#/components/responses/BadRequest"
|
||
"401":
|
||
$ref: "#/components/responses/Unauthorized"
|
||
/api/playground/presets/{id}:
|
||
parameters:
|
||
- name: id
|
||
in: path
|
||
required: true
|
||
schema:
|
||
type: string
|
||
format: uuid
|
||
get:
|
||
tags:
|
||
- Playground
|
||
summary: Get playground preset
|
||
security:
|
||
- BearerAuth: []
|
||
responses:
|
||
"200":
|
||
description: Preset found
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/PlaygroundPreset"
|
||
"401":
|
||
$ref: "#/components/responses/Unauthorized"
|
||
"404":
|
||
description: Preset not found
|
||
put:
|
||
tags:
|
||
- Playground
|
||
summary: Update playground preset
|
||
security:
|
||
- BearerAuth: []
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/PlaygroundPresetCreate"
|
||
responses:
|
||
"200":
|
||
description: Updated preset
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/PlaygroundPreset"
|
||
"400":
|
||
$ref: "#/components/responses/BadRequest"
|
||
"401":
|
||
$ref: "#/components/responses/Unauthorized"
|
||
"404":
|
||
description: Preset not found
|
||
delete:
|
||
tags:
|
||
- Playground
|
||
summary: Delete playground preset
|
||
security:
|
||
- BearerAuth: []
|
||
responses:
|
||
"204":
|
||
description: Deleted
|
||
"401":
|
||
$ref: "#/components/responses/Unauthorized"
|
||
"404":
|
||
description: Preset not found
|
||
# --- Memory Engine (plan 21) ---
|
||
/api/memory:
|
||
get:
|
||
tags:
|
||
- Memory
|
||
summary: List memory entries
|
||
security:
|
||
- ManagementSessionAuth: []
|
||
parameters:
|
||
- name: apiKeyId
|
||
in: query
|
||
schema:
|
||
type: string
|
||
- name: type
|
||
in: query
|
||
schema:
|
||
type: string
|
||
enum:
|
||
- factual
|
||
- episodic
|
||
- procedural
|
||
- semantic
|
||
- name: sessionId
|
||
in: query
|
||
schema:
|
||
type: string
|
||
- name: q
|
||
in: query
|
||
schema:
|
||
type: string
|
||
- name: limit
|
||
in: query
|
||
schema:
|
||
type: integer
|
||
minimum: 1
|
||
maximum: 200
|
||
default: 50
|
||
- name: page
|
||
in: query
|
||
schema:
|
||
type: integer
|
||
minimum: 1
|
||
default: 1
|
||
- name: offset
|
||
in: query
|
||
schema:
|
||
type: integer
|
||
minimum: 0
|
||
responses:
|
||
"200":
|
||
description: Paginated list of memories with stats
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
data:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/MemoryEntry"
|
||
total:
|
||
type: integer
|
||
totalPages:
|
||
type: integer
|
||
stats:
|
||
type: object
|
||
properties:
|
||
total:
|
||
type: integer
|
||
tokensUsed:
|
||
type: integer
|
||
hitRate:
|
||
type: number
|
||
cacheStats:
|
||
type: object
|
||
properties:
|
||
hits:
|
||
type: integer
|
||
misses:
|
||
type: integer
|
||
"401":
|
||
$ref: "#/components/responses/Unauthorized"
|
||
post:
|
||
tags:
|
||
- Memory
|
||
summary: Create a memory entry
|
||
security:
|
||
- ManagementSessionAuth: []
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required:
|
||
- content
|
||
- key
|
||
properties:
|
||
content:
|
||
type: string
|
||
minLength: 1
|
||
key:
|
||
type: string
|
||
minLength: 1
|
||
type:
|
||
type: string
|
||
enum:
|
||
- factual
|
||
- episodic
|
||
- procedural
|
||
- semantic
|
||
default: factual
|
||
sessionId:
|
||
type: string
|
||
nullable: true
|
||
apiKeyId:
|
||
type: string
|
||
metadata:
|
||
type: object
|
||
additionalProperties: true
|
||
expiresAt:
|
||
type: string
|
||
format: date-time
|
||
nullable: true
|
||
responses:
|
||
"201":
|
||
description: Created memory entry
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/MemoryEntry"
|
||
"400":
|
||
description: Validation error
|
||
"401":
|
||
$ref: "#/components/responses/Unauthorized"
|
||
/api/memory/{id}:
|
||
parameters:
|
||
- name: id
|
||
in: path
|
||
required: true
|
||
schema:
|
||
type: string
|
||
description: Memory UUID
|
||
get:
|
||
tags:
|
||
- Memory
|
||
summary: Get a single memory entry
|
||
security:
|
||
- ManagementSessionAuth: []
|
||
responses:
|
||
"200":
|
||
description: Memory entry
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/MemoryEntry"
|
||
"401":
|
||
$ref: "#/components/responses/Unauthorized"
|
||
"404":
|
||
description: Memory not found
|
||
put:
|
||
tags:
|
||
- Memory
|
||
summary: Update a memory entry
|
||
description: Update `type`, `key`, `content`, and/or `metadata` of an existing memory. If an embedding source is available, the vector in `vec_memories` is also regenerated. Corresponds to `MemoryUpdatePutSchema`.
|
||
security:
|
||
- ManagementSessionAuth: []
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
type:
|
||
type: string
|
||
enum:
|
||
- factual
|
||
- episodic
|
||
- procedural
|
||
- semantic
|
||
key:
|
||
type: string
|
||
minLength: 1
|
||
content:
|
||
type: string
|
||
minLength: 1
|
||
metadata:
|
||
type: object
|
||
additionalProperties: true
|
||
additionalProperties: false
|
||
responses:
|
||
"200":
|
||
description: Updated memory entry
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/MemoryEntry"
|
||
"400":
|
||
description: Validation error
|
||
"401":
|
||
$ref: "#/components/responses/Unauthorized"
|
||
"404":
|
||
description: Memory not found
|
||
delete:
|
||
tags:
|
||
- Memory
|
||
summary: Delete a memory entry
|
||
description: Deletes the SQLite row, removes the vector from `vec_memories`, and best-effort deletes the point from Qdrant.
|
||
security:
|
||
- ManagementSessionAuth: []
|
||
responses:
|
||
"200":
|
||
description: Deleted
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
success:
|
||
type: boolean
|
||
"401":
|
||
$ref: "#/components/responses/Unauthorized"
|
||
"404":
|
||
description: Memory not found
|
||
/api/memory/health:
|
||
get:
|
||
tags:
|
||
- Memory
|
||
summary: Memory store health check
|
||
description: Round-trip create→list→delete to verify the store is alive.
|
||
security:
|
||
- ManagementSessionAuth: []
|
||
responses:
|
||
"200":
|
||
description: Health result
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
working:
|
||
type: boolean
|
||
latencyMs:
|
||
type: number
|
||
error:
|
||
type: string
|
||
nullable: true
|
||
"401":
|
||
$ref: "#/components/responses/Unauthorized"
|
||
/api/memory/retrieve-preview:
|
||
post:
|
||
tags:
|
||
- Memory
|
||
summary: Dry-run memory retrieval (Playground)
|
||
description: Simulates `retrieveMemories()` for a given query and returns the ranked results with score, tier, and token count. Does NOT modify any memory. Corresponds to `RetrievePreviewSchema`.
|
||
security:
|
||
- ManagementSessionAuth: []
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required:
|
||
- query
|
||
properties:
|
||
query:
|
||
type: string
|
||
minLength: 1
|
||
strategy:
|
||
type: string
|
||
enum:
|
||
- exact
|
||
- semantic
|
||
- hybrid
|
||
default: hybrid
|
||
maxTokens:
|
||
type: integer
|
||
minimum: 1
|
||
maximum: 16000
|
||
default: 2000
|
||
apiKeyId:
|
||
type: string
|
||
description: Optional — tests global pool when omitted
|
||
limit:
|
||
type: integer
|
||
minimum: 1
|
||
maximum: 100
|
||
default: 20
|
||
additionalProperties: false
|
||
responses:
|
||
"200":
|
||
description: Preview results
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
memories:
|
||
type: array
|
||
items:
|
||
type: object
|
||
properties:
|
||
id:
|
||
type: string
|
||
type:
|
||
type: string
|
||
enum:
|
||
- factual
|
||
- episodic
|
||
- procedural
|
||
- semantic
|
||
key:
|
||
type: string
|
||
content:
|
||
type: string
|
||
score:
|
||
type: number
|
||
tokens:
|
||
type: integer
|
||
tier:
|
||
type: string
|
||
enum:
|
||
- fts5
|
||
- vector
|
||
- hybrid-rrf
|
||
- qdrant
|
||
vecScore:
|
||
type: number
|
||
nullable: true
|
||
ftsScore:
|
||
type: number
|
||
nullable: true
|
||
resolution:
|
||
type: object
|
||
properties:
|
||
embeddingSource:
|
||
type: string
|
||
enum:
|
||
- remote
|
||
- static
|
||
- transformers
|
||
nullable: true
|
||
embeddingModel:
|
||
type: string
|
||
nullable: true
|
||
vectorStore:
|
||
type: string
|
||
enum:
|
||
- sqlite-vec
|
||
- qdrant
|
||
- none
|
||
strategyUsed:
|
||
type: string
|
||
enum:
|
||
- exact
|
||
- semantic
|
||
- hybrid
|
||
rerankApplied:
|
||
type: boolean
|
||
fallbackReason:
|
||
type: string
|
||
nullable: true
|
||
totalTokensUsed:
|
||
type: integer
|
||
budgetMaxTokens:
|
||
type: integer
|
||
"400":
|
||
description: Validation error
|
||
"401":
|
||
$ref: "#/components/responses/Unauthorized"
|
||
/api/memory/embedding-providers:
|
||
get:
|
||
tags:
|
||
- Memory
|
||
summary: List embedding providers
|
||
description: Returns all providers that have embedding-capable models, indicating which have an active API key configured.
|
||
security:
|
||
- ManagementSessionAuth: []
|
||
responses:
|
||
"200":
|
||
description: Provider list
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
providers:
|
||
type: array
|
||
items:
|
||
type: object
|
||
properties:
|
||
provider:
|
||
type: string
|
||
hasKey:
|
||
type: boolean
|
||
models:
|
||
type: array
|
||
items:
|
||
type: object
|
||
properties:
|
||
id:
|
||
type: string
|
||
description: "Format: provider/model"
|
||
name:
|
||
type: string
|
||
dimensions:
|
||
type: integer
|
||
nullable: true
|
||
"401":
|
||
$ref: "#/components/responses/Unauthorized"
|
||
/api/memory/engine-status:
|
||
get:
|
||
tags:
|
||
- Memory
|
||
summary: Memory engine status
|
||
description: Returns the full engine status including keyword tier availability, embedding resolution, vector store statistics (sqlite-vec), Qdrant health, and rerank configuration. Corresponds to `MemoryEngineStatusSchema`.
|
||
security:
|
||
- ManagementSessionAuth: []
|
||
responses:
|
||
"200":
|
||
description: Engine status
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
keyword:
|
||
type: object
|
||
properties:
|
||
available:
|
||
type: boolean
|
||
backend:
|
||
type: string
|
||
enum:
|
||
- FTS5
|
||
embedding:
|
||
type: object
|
||
properties:
|
||
source:
|
||
type: string
|
||
enum:
|
||
- remote
|
||
- static
|
||
- transformers
|
||
nullable: true
|
||
model:
|
||
type: string
|
||
nullable: true
|
||
dimensions:
|
||
type: integer
|
||
nullable: true
|
||
available:
|
||
type: boolean
|
||
reason:
|
||
type: string
|
||
cacheStats:
|
||
type: object
|
||
properties:
|
||
hits:
|
||
type: integer
|
||
misses:
|
||
type: integer
|
||
size:
|
||
type: integer
|
||
vectorStore:
|
||
type: object
|
||
properties:
|
||
backend:
|
||
type: string
|
||
enum:
|
||
- sqlite-vec
|
||
- qdrant
|
||
- none
|
||
available:
|
||
type: boolean
|
||
rowCount:
|
||
type: integer
|
||
needsReindex:
|
||
type: integer
|
||
reason:
|
||
type: string
|
||
qdrant:
|
||
type: object
|
||
properties:
|
||
enabled:
|
||
type: boolean
|
||
healthy:
|
||
type: boolean
|
||
nullable: true
|
||
latencyMs:
|
||
type: number
|
||
nullable: true
|
||
error:
|
||
type: string
|
||
nullable: true
|
||
rerank:
|
||
type: object
|
||
properties:
|
||
enabled:
|
||
type: boolean
|
||
provider:
|
||
type: string
|
||
nullable: true
|
||
model:
|
||
type: string
|
||
nullable: true
|
||
available:
|
||
type: boolean
|
||
reason:
|
||
type: string
|
||
"401":
|
||
$ref: "#/components/responses/Unauthorized"
|
||
/api/memory/summarize:
|
||
post:
|
||
tags:
|
||
- Memory
|
||
summary: Compact old memories
|
||
description: "Manually triggers memory compaction for memories older than `olderThanDays`. Use `dryRun: true` to preview candidates. Corresponds to `MemorySummarizeSchema`."
|
||
security:
|
||
- ManagementSessionAuth: []
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
olderThanDays:
|
||
type: integer
|
||
minimum: 1
|
||
maximum: 365
|
||
default: 30
|
||
apiKeyId:
|
||
type: string
|
||
description: Optional — compacts all keys when omitted
|
||
dryRun:
|
||
type: boolean
|
||
default: false
|
||
additionalProperties: false
|
||
responses:
|
||
"200":
|
||
description: Summarization result
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
candidates:
|
||
type: integer
|
||
tokensSaved:
|
||
type: integer
|
||
dryRun:
|
||
type: boolean
|
||
"400":
|
||
description: Validation error
|
||
"401":
|
||
$ref: "#/components/responses/Unauthorized"
|
||
/api/memory/reindex:
|
||
post:
|
||
tags:
|
||
- Memory
|
||
summary: Trigger vector reindex
|
||
description: "Starts background reindexing of memories with `needs_reindex = 1`. Use `force: true` to regenerate ALL vectors regardless of index status. Corresponds to `MemoryReindexSchema`."
|
||
security:
|
||
- ManagementSessionAuth: []
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
force:
|
||
type: boolean
|
||
default: false
|
||
description: When true, marks all memories needs_reindex=1 before running.
|
||
additionalProperties: false
|
||
responses:
|
||
"200":
|
||
description: Reindex started
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
started:
|
||
type: boolean
|
||
pending:
|
||
type: integer
|
||
description: Memories still pending after this batch
|
||
"400":
|
||
description: Validation error
|
||
"401":
|
||
$ref: "#/components/responses/Unauthorized"
|
||
/api/settings/memory:
|
||
get:
|
||
tags:
|
||
- Memory
|
||
- Settings
|
||
summary: Get memory settings
|
||
description: Returns the extended memory settings including 7 new fields added in plan 21 (embeddingSource, embeddingProviderModel, transformersEnabled, staticEnabled, rerankEnabled, rerankProviderModel, vectorStore).
|
||
security:
|
||
- ManagementSessionAuth: []
|
||
responses:
|
||
"200":
|
||
description: Extended memory settings
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/MemorySettingsExtended"
|
||
"401":
|
||
$ref: "#/components/responses/Unauthorized"
|
||
put:
|
||
tags:
|
||
- Memory
|
||
- Settings
|
||
summary: Update memory settings
|
||
description: "Update any subset of the extended memory settings. All fields are optional; only provided fields are updated. Schema: `MemorySettingsExtendedSchema`."
|
||
security:
|
||
- ManagementSessionAuth: []
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/MemorySettingsExtended"
|
||
responses:
|
||
"200":
|
||
description: Updated memory settings
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/MemorySettingsExtended"
|
||
"400":
|
||
description: Validation error
|
||
"401":
|
||
$ref: "#/components/responses/Unauthorized"
|
||
/api/settings/qdrant:
|
||
get:
|
||
tags:
|
||
- Memory
|
||
- Settings
|
||
summary: Get Qdrant settings
|
||
description: Returns current Qdrant configuration. The `apiKey` field is never returned raw — use `hasApiKey` / `apiKeyMasked` instead.
|
||
security:
|
||
- ManagementSessionAuth: []
|
||
responses:
|
||
"200":
|
||
description: Qdrant settings
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/QdrantSettings"
|
||
"401":
|
||
$ref: "#/components/responses/Unauthorized"
|
||
put:
|
||
tags:
|
||
- Memory
|
||
- Settings
|
||
summary: Update Qdrant settings
|
||
description: 'Update Qdrant configuration. Pass `apiKey: ""` to remove the stored key. Schema: `QdrantSettingsUpdateSchema`.'
|
||
security:
|
||
- ManagementSessionAuth: []
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
enabled:
|
||
type: boolean
|
||
host:
|
||
type: string
|
||
port:
|
||
type: integer
|
||
minimum: 1
|
||
maximum: 65535
|
||
collection:
|
||
type: string
|
||
minLength: 1
|
||
embeddingModel:
|
||
type: string
|
||
minLength: 1
|
||
apiKey:
|
||
type: string
|
||
description: Empty string removes the key
|
||
additionalProperties: false
|
||
responses:
|
||
"200":
|
||
description: Updated Qdrant settings
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/QdrantSettings"
|
||
"400":
|
||
description: Validation error
|
||
"401":
|
||
$ref: "#/components/responses/Unauthorized"
|
||
/api/settings/qdrant/health:
|
||
get:
|
||
tags:
|
||
- Memory
|
||
summary: Qdrant health probe
|
||
description: Performs a liveness check against the configured Qdrant instance. Returns latency and any connection error (sanitized — no stack traces).
|
||
security:
|
||
- ManagementSessionAuth: []
|
||
responses:
|
||
"200":
|
||
description: Health result
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/QdrantHealthResult"
|
||
"401":
|
||
$ref: "#/components/responses/Unauthorized"
|
||
/api/settings/qdrant/search:
|
||
post:
|
||
tags:
|
||
- Memory
|
||
summary: Qdrant semantic search test
|
||
description: "Performs a test semantic search against the Qdrant collection. Useful for validating that the integration works end-to-end. Schema: `QdrantSearchSchema`."
|
||
security:
|
||
- ManagementSessionAuth: []
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required:
|
||
- query
|
||
properties:
|
||
query:
|
||
type: string
|
||
minLength: 1
|
||
topK:
|
||
type: integer
|
||
minimum: 1
|
||
maximum: 50
|
||
default: 5
|
||
additionalProperties: false
|
||
responses:
|
||
"200":
|
||
description: Search results
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
results:
|
||
type: array
|
||
items:
|
||
type: object
|
||
"400":
|
||
description: Validation error
|
||
"401":
|
||
$ref: "#/components/responses/Unauthorized"
|
||
"503":
|
||
description: Qdrant unavailable (structured error, no stack trace)
|
||
/api/settings/qdrant/cleanup:
|
||
post:
|
||
tags:
|
||
- Memory
|
||
summary: Clean up expired Qdrant points
|
||
description: Removes Qdrant points for memories that have expired or exceeded the configured retention window.
|
||
security:
|
||
- ManagementSessionAuth: []
|
||
responses:
|
||
"200":
|
||
description: Cleanup result
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
deleted:
|
||
type: integer
|
||
checked:
|
||
type: integer
|
||
"401":
|
||
$ref: "#/components/responses/Unauthorized"
|
||
"503":
|
||
description: Qdrant unavailable (structured error, no stack trace)
|
||
/api/settings/qdrant/embedding-models:
|
||
get:
|
||
tags:
|
||
- Memory
|
||
summary: List Qdrant embedding models
|
||
description: Returns the list of embedding models available for use with Qdrant.
|
||
security:
|
||
- ManagementSessionAuth: []
|
||
responses:
|
||
"200":
|
||
description: Embedding models list
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
models:
|
||
type: array
|
||
items:
|
||
type: string
|
||
"401":
|
||
$ref: "#/components/responses/Unauthorized"
|
||
# ─── Proxy Endpoints ──────────────────────────────────────────
|
||
|
||
/api/v1/chat/completions:
|
||
post:
|
||
tags: [Chat]
|
||
summary: Create chat completion
|
||
description: OpenAI-compatible chat completions endpoint. Routes to configured providers.
|
||
security:
|
||
- BearerAuth: []
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ChatCompletionRequest"
|
||
responses:
|
||
"200":
|
||
description: Chat completion response (or SSE stream)
|
||
headers:
|
||
X-OmniRoute-Response-Cost:
|
||
schema:
|
||
type: string
|
||
description: Request cost in USD, fixed 10 decimals (e.g. `0.0001234500`; `0.0000000000` for free/unpriced).
|
||
X-OmniRoute-Tokens-In:
|
||
schema:
|
||
type: string
|
||
description: Input (prompt) token count.
|
||
X-OmniRoute-Tokens-Out:
|
||
schema:
|
||
type: string
|
||
description: Output (completion) token count.
|
||
X-OmniRoute-Model:
|
||
schema:
|
||
type: string
|
||
description: Resolved model.
|
||
X-OmniRoute-Provider:
|
||
schema:
|
||
type: string
|
||
description: Resolved provider alias.
|
||
X-OmniRoute-Latency-Ms:
|
||
schema:
|
||
type: string
|
||
description: Handler latency in milliseconds.
|
||
X-OmniRoute-Cache-Hit:
|
||
schema:
|
||
type: string
|
||
enum: ["true", "false"]
|
||
description: Whether the response was served from cache.
|
||
X-OmniRoute-Fallback-Attempts:
|
||
schema:
|
||
type: string
|
||
description: Number of fallback attempts (only present when > 0).
|
||
X-OmniRoute-Request-Id:
|
||
schema:
|
||
type: string
|
||
description: Request correlation id (present when known).
|
||
X-OmniRoute-Version:
|
||
schema:
|
||
type: string
|
||
description: OmniRoute build version (always present).
|
||
X-OmniRoute-Cost-Saved:
|
||
schema:
|
||
type: string
|
||
description: >-
|
||
On a semantic-cache HIT, the original (would-have-been) cost in USD that
|
||
the cache avoided (fixed 10 decimals). Present only on cache hits;
|
||
X-OmniRoute-Response-Cost is 0 for the same response (incremental cost).
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ChatCompletionResponse"
|
||
text/event-stream:
|
||
schema:
|
||
type: string
|
||
"401":
|
||
$ref: "#/components/responses/Unauthorized"
|
||
"502":
|
||
description: All upstream providers failed
|
||
|
||
/api/v1/ws:
|
||
get:
|
||
tags: [Chat]
|
||
summary: Chat completion over WebSocket (handshake + upgrade)
|
||
description: >-
|
||
OpenAI-compatible chat over a WebSocket connection. `GET` with
|
||
`?handshake=1` returns the connection descriptor (auth path, message
|
||
protocol and live-event channels) as JSON; a plain `GET` without an
|
||
Upgrade returns `426 Upgrade Required`. After upgrading, the client
|
||
exchanges JSON frames — `{type:"request", id, payload:{model, messages}}`
|
||
to start a completion and `{type:"cancel", id}` to abort it. A separate
|
||
live channel (default port `LIVE_WS_PORT=20129`, path `/live`) streams
|
||
dashboard events on the `requests`, `combo` and `credentials` topics with
|
||
a 15s heartbeat. Requires an API key.
|
||
security:
|
||
- BearerAuth: []
|
||
parameters:
|
||
- name: handshake
|
||
in: query
|
||
description: Set to `1` to receive the JSON connection descriptor instead of upgrading.
|
||
required: false
|
||
schema:
|
||
type: string
|
||
enum: ["1"]
|
||
responses:
|
||
"101":
|
||
description: WebSocket upgrade successful
|
||
"200":
|
||
description: Handshake descriptor (auth path, message protocol, live channels)
|
||
"401":
|
||
description: WebSocket auth required (no credential supplied)
|
||
"403":
|
||
description: Invalid WebSocket credential
|
||
"426":
|
||
description: Upgrade Required — connect via WebSocket or use `?handshake=1`
|
||
|
||
/api/v1/providers/{provider}/chat/completions:
|
||
post:
|
||
tags: [Chat]
|
||
summary: Create chat completion (provider-specific)
|
||
description: Routes to a specific provider by name.
|
||
security:
|
||
- BearerAuth: []
|
||
parameters:
|
||
- name: provider
|
||
in: path
|
||
required: true
|
||
schema:
|
||
type: string
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ChatCompletionRequest"
|
||
responses:
|
||
"200":
|
||
description: Chat completion response
|
||
"401":
|
||
$ref: "#/components/responses/Unauthorized"
|
||
|
||
/api/v1/api/chat:
|
||
post:
|
||
tags: [Chat]
|
||
summary: Ollama-compatible chat endpoint
|
||
description: Provides compatibility with Ollama's /api/chat format.
|
||
security:
|
||
- BearerAuth: []
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
responses:
|
||
"200":
|
||
description: Chat response (JSON or streaming)
|
||
|
||
/api/v1/messages:
|
||
post:
|
||
tags: [Messages]
|
||
summary: Create message (Anthropic-compatible)
|
||
description: Anthropic Messages API endpoint. Routes to Claude providers.
|
||
security:
|
||
- BearerAuth: []
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/MessagesRequest"
|
||
responses:
|
||
"200":
|
||
description: >-
|
||
Message response (or SSE stream). Non-streaming success responses
|
||
carry the `X-OmniRoute-*` cost-telemetry headers (see
|
||
`POST /api/v1/chat/completions`), including `X-OmniRoute-Request-Id`
|
||
and `X-OmniRoute-Version`.
|
||
"401":
|
||
$ref: "#/components/responses/Unauthorized"
|
||
|
||
/api/v1/messages/count_tokens:
|
||
post:
|
||
tags: [Messages]
|
||
summary: Count tokens for a message
|
||
security:
|
||
- BearerAuth: []
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
responses:
|
||
"200":
|
||
description: Token count
|
||
|
||
/api/v1/responses:
|
||
post:
|
||
tags: [Responses]
|
||
summary: Create response (OpenAI Responses API)
|
||
description: OpenAI Responses API endpoint.
|
||
security:
|
||
- BearerAuth: []
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
responses:
|
||
"200":
|
||
description: >-
|
||
Response object or SSE stream. Non-streaming success responses carry
|
||
the `X-OmniRoute-*` cost-telemetry headers (see
|
||
`POST /api/v1/chat/completions`), including `X-OmniRoute-Request-Id`
|
||
and `X-OmniRoute-Version`.
|
||
"401":
|
||
$ref: "#/components/responses/Unauthorized"
|
||
|
||
/api/v1/embeddings:
|
||
post:
|
||
tags: [Embeddings]
|
||
summary: Create embeddings
|
||
security:
|
||
- BearerAuth: []
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [input, model]
|
||
properties:
|
||
input:
|
||
oneOf:
|
||
- type: string
|
||
- type: array
|
||
items:
|
||
type: string
|
||
model:
|
||
type: string
|
||
responses:
|
||
"200":
|
||
description: >-
|
||
Embedding vectors. Success responses carry the `X-OmniRoute-*`
|
||
cost-telemetry headers (see `POST /api/v1/chat/completions`); media
|
||
cost is computed per modality when pricing is available, otherwise
|
||
`0` (fail-open).
|
||
|
||
/api/v1/providers/{provider}/embeddings:
|
||
post:
|
||
tags: [Embeddings]
|
||
summary: Create embeddings (provider-specific)
|
||
security:
|
||
- BearerAuth: []
|
||
parameters:
|
||
- name: provider
|
||
in: path
|
||
required: true
|
||
schema:
|
||
type: string
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
responses:
|
||
"200":
|
||
description: Embedding vectors
|
||
|
||
/api/v1/images/generations:
|
||
post:
|
||
tags: [Images]
|
||
summary: Generate images
|
||
security:
|
||
- BearerAuth: []
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [prompt]
|
||
properties:
|
||
prompt:
|
||
type: string
|
||
model:
|
||
type: string
|
||
n:
|
||
type: integer
|
||
default: 1
|
||
size:
|
||
type: string
|
||
default: 1024x1024
|
||
responses:
|
||
"200":
|
||
description: >-
|
||
Generated images. Success responses carry the `X-OmniRoute-*`
|
||
cost-telemetry headers (see `POST /api/v1/chat/completions`); image
|
||
cost is computed per image when pricing is available, otherwise `0`
|
||
(fail-open).
|
||
|
||
/api/v1/providers/{provider}/images/generations:
|
||
post:
|
||
tags: [Images]
|
||
summary: Generate images (provider-specific)
|
||
security:
|
||
- BearerAuth: []
|
||
parameters:
|
||
- name: provider
|
||
in: path
|
||
required: true
|
||
schema:
|
||
type: string
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
responses:
|
||
"200":
|
||
description: Generated images
|
||
|
||
/api/v1/audio/speech:
|
||
post:
|
||
tags: [Audio]
|
||
summary: Generate speech audio
|
||
description: Text-to-speech endpoint. Routes to configured TTS providers.
|
||
security:
|
||
- BearerAuth: []
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [input]
|
||
properties:
|
||
input:
|
||
type: string
|
||
model:
|
||
type: string
|
||
voice:
|
||
type: string
|
||
responses:
|
||
"200":
|
||
description: >-
|
||
Audio data. Success responses carry the `X-OmniRoute-*`
|
||
cost-telemetry headers (see `POST /api/v1/chat/completions`); speech
|
||
cost is computed per character when pricing is available, otherwise
|
||
`0` (fail-open).
|
||
|
||
/api/v1/audio/transcriptions:
|
||
post:
|
||
tags: [Audio]
|
||
summary: Transcribe audio
|
||
description: Audio-to-text transcription endpoint.
|
||
security:
|
||
- BearerAuth: []
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
multipart/form-data:
|
||
schema:
|
||
type: object
|
||
required: [file]
|
||
properties:
|
||
file:
|
||
type: string
|
||
format: binary
|
||
model:
|
||
type: string
|
||
responses:
|
||
"200":
|
||
description: >-
|
||
Transcription result. Success responses carry the `X-OmniRoute-*`
|
||
cost-telemetry headers (see `POST /api/v1/chat/completions`);
|
||
transcription cost is computed per second when pricing is available,
|
||
otherwise `0` (fail-open).
|
||
|
||
/api/v1/moderations:
|
||
post:
|
||
tags: [Moderations]
|
||
summary: Create moderation
|
||
description: Content moderation endpoint. Routes to configured moderation providers.
|
||
security:
|
||
- BearerAuth: []
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [input]
|
||
properties:
|
||
input:
|
||
oneOf:
|
||
- type: string
|
||
- type: array
|
||
items:
|
||
type: string
|
||
responses:
|
||
"200":
|
||
description: >-
|
||
Moderation result. Success responses carry the `X-OmniRoute-*`
|
||
cost-telemetry headers (see `POST /api/v1/chat/completions`);
|
||
moderations are always cost `0` (free).
|
||
|
||
/api/v1/rerank:
|
||
post:
|
||
tags: [Rerank]
|
||
summary: Rerank documents
|
||
description: Document reranking endpoint.
|
||
security:
|
||
- BearerAuth: []
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [query, documents]
|
||
properties:
|
||
query:
|
||
type: string
|
||
documents:
|
||
type: array
|
||
items:
|
||
type: string
|
||
model:
|
||
type: string
|
||
responses:
|
||
"200":
|
||
description: >-
|
||
Reranked documents. Success responses carry the `X-OmniRoute-*`
|
||
cost-telemetry headers (see `POST /api/v1/chat/completions`); rerank
|
||
cost is computed per search-unit when pricing is available,
|
||
otherwise `0` (fail-open).
|
||
|
||
/api/v1:
|
||
get:
|
||
tags: [System]
|
||
summary: API v1 root endpoint
|
||
description: Returns basic API info and status.
|
||
security:
|
||
- BearerAuth: []
|
||
responses:
|
||
"200":
|
||
description: API info
|
||
|
||
/api/v1/models:
|
||
get:
|
||
tags: [Models]
|
||
summary: List available models
|
||
description: Returns all models available across configured providers.
|
||
security:
|
||
- BearerAuth: []
|
||
responses:
|
||
"200":
|
||
description: Model list
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
object:
|
||
type: string
|
||
example: list
|
||
data:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/Model"
|
||
|
||
/api/v1/providers/{provider}/models:
|
||
get:
|
||
tags: [Models]
|
||
summary: List models for a specific provider
|
||
description: Returns only models for the selected provider with provider prefix removed from each model id.
|
||
security:
|
||
- BearerAuth: []
|
||
parameters:
|
||
- in: path
|
||
name: provider
|
||
required: true
|
||
schema:
|
||
type: string
|
||
description: Provider id or alias (for example `openai`, `claude`, `cc`).
|
||
responses:
|
||
"200":
|
||
description: Provider-scoped model list
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
object:
|
||
type: string
|
||
example: list
|
||
data:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/Model"
|
||
"400":
|
||
description: Unknown provider
|
||
|
||
/api/models:
|
||
get:
|
||
tags: [Models]
|
||
summary: List models (management)
|
||
responses:
|
||
"200":
|
||
description: Internal model list with aliases
|
||
|
||
/api/models/alias:
|
||
post:
|
||
tags: [Models]
|
||
summary: Create or update a model alias
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
responses:
|
||
"200":
|
||
description: Alias created/updated
|
||
|
||
/api/models/catalog:
|
||
get:
|
||
tags: [Models]
|
||
summary: Get full model catalog
|
||
responses:
|
||
"200":
|
||
description: Complete catalog with all providers
|
||
|
||
# ─── Management Endpoints ──────────────────────────────────────
|
||
|
||
/api/providers:
|
||
get:
|
||
tags: [Providers]
|
||
summary: List provider connections
|
||
responses:
|
||
"200":
|
||
description: Provider connection list
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
connections:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/ProviderConnection"
|
||
post:
|
||
tags: [Providers]
|
||
summary: Create provider connection
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ProviderConnectionCreate"
|
||
responses:
|
||
"201":
|
||
description: Created provider connection
|
||
|
||
/api/providers/{id}:
|
||
get:
|
||
tags: [Providers]
|
||
summary: Get provider connection
|
||
parameters:
|
||
- $ref: "#/components/parameters/ResourceId"
|
||
responses:
|
||
"200":
|
||
description: Provider connection details
|
||
"404":
|
||
description: Provider not found
|
||
patch:
|
||
tags: [Providers]
|
||
summary: Update provider connection
|
||
parameters:
|
||
- $ref: "#/components/parameters/ResourceId"
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ProviderConnectionCreate"
|
||
responses:
|
||
"200":
|
||
description: Updated provider
|
||
delete:
|
||
tags: [Providers]
|
||
summary: Delete provider connection
|
||
parameters:
|
||
- $ref: "#/components/parameters/ResourceId"
|
||
responses:
|
||
"200":
|
||
description: Provider deleted
|
||
|
||
/api/providers/{id}/test:
|
||
post:
|
||
tags: [Providers]
|
||
summary: Test provider connection
|
||
parameters:
|
||
- $ref: "#/components/parameters/ResourceId"
|
||
responses:
|
||
"200":
|
||
description: Test result
|
||
|
||
/api/providers/{id}/models:
|
||
get:
|
||
tags: [Providers]
|
||
summary: List models for a provider
|
||
parameters:
|
||
- $ref: "#/components/parameters/ResourceId"
|
||
responses:
|
||
"200":
|
||
description: Provider model list
|
||
|
||
/api/providers/test-batch:
|
||
post:
|
||
tags: [Providers]
|
||
summary: Test multiple providers at once
|
||
responses:
|
||
"200":
|
||
description: Batch test results
|
||
|
||
/api/providers/validate:
|
||
post:
|
||
tags: [Providers]
|
||
summary: Validate provider credentials
|
||
responses:
|
||
"200":
|
||
description: Validation result
|
||
|
||
/api/providers/client:
|
||
get:
|
||
tags: [Providers]
|
||
summary: Get client-side provider info
|
||
responses:
|
||
"200":
|
||
description: Provider info for frontend
|
||
|
||
/api/providers/agy-auth/import:
|
||
post:
|
||
tags: [Providers]
|
||
summary: Import an Antigravity CLI (agy) token file as an `agy` connection
|
||
responses:
|
||
"200":
|
||
description: Created or updated provider connection
|
||
|
||
/api/providers/agy-auth/import-bulk:
|
||
post:
|
||
tags: [Providers]
|
||
summary: Bulk-import multiple Antigravity CLI (agy) token files (up to 50)
|
||
responses:
|
||
"200":
|
||
description: Per-entry import results (success/failed counts)
|
||
|
||
/api/providers/agy-auth/zip-extract:
|
||
post:
|
||
tags: [Providers]
|
||
summary: Extract `.json` token files from an uploaded ZIP for agy bulk import
|
||
responses:
|
||
"200":
|
||
description: Extracted token-file entries
|
||
|
||
/api/providers/agy-auth/apply-local:
|
||
post:
|
||
tags: [Providers]
|
||
summary: Auto-detect and import the local Antigravity CLI (agy) login from disk
|
||
responses:
|
||
"200":
|
||
description: Created or updated provider connection
|
||
"404":
|
||
description: No local agy login found
|
||
|
||
/api/provider-nodes:
|
||
get:
|
||
tags: [Provider Nodes]
|
||
summary: List provider nodes
|
||
responses:
|
||
"200":
|
||
description: Provider node list
|
||
post:
|
||
tags: [Provider Nodes]
|
||
summary: Create provider node
|
||
responses:
|
||
"201":
|
||
description: Created node
|
||
|
||
/api/provider-nodes/{id}:
|
||
patch:
|
||
tags: [Provider Nodes]
|
||
summary: Update provider node
|
||
parameters:
|
||
- $ref: "#/components/parameters/ResourceId"
|
||
responses:
|
||
"200":
|
||
description: Updated node
|
||
delete:
|
||
tags: [Provider Nodes]
|
||
summary: Delete provider node
|
||
parameters:
|
||
- $ref: "#/components/parameters/ResourceId"
|
||
responses:
|
||
"200":
|
||
description: Node deleted
|
||
|
||
/api/provider-nodes/validate:
|
||
post:
|
||
tags: [Provider Nodes]
|
||
summary: Validate a provider node
|
||
responses:
|
||
"200":
|
||
description: Validation result
|
||
|
||
/api/provider-models:
|
||
get:
|
||
tags: [Provider Nodes]
|
||
summary: List provider models
|
||
responses:
|
||
"200":
|
||
description: Provider model list
|
||
|
||
/api/keys:
|
||
get:
|
||
tags: [API Keys]
|
||
summary: List API keys
|
||
responses:
|
||
"200":
|
||
description: API key list
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
keys:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/ApiKey"
|
||
"401":
|
||
description: Authentication required
|
||
post:
|
||
tags: [API Keys]
|
||
summary: Create API key
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [label]
|
||
properties:
|
||
label:
|
||
type: string
|
||
responses:
|
||
"201":
|
||
description: Created API key (includes full key value)
|
||
"401":
|
||
description: Authentication required
|
||
|
||
/api/keys/{id}:
|
||
get:
|
||
tags: [API Keys]
|
||
summary: Get API key
|
||
parameters:
|
||
- $ref: "#/components/parameters/ResourceId"
|
||
responses:
|
||
"200":
|
||
description: API key metadata
|
||
"401":
|
||
description: Authentication required
|
||
"404":
|
||
description: Key not found
|
||
patch:
|
||
tags: [API Keys]
|
||
summary: Update API key
|
||
parameters:
|
||
- $ref: "#/components/parameters/ResourceId"
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
additionalProperties: true
|
||
responses:
|
||
"200":
|
||
description: API key settings updated
|
||
"400":
|
||
description: Invalid update request
|
||
"401":
|
||
description: Authentication required
|
||
"404":
|
||
description: Key not found
|
||
delete:
|
||
tags: [API Keys]
|
||
summary: Delete API key
|
||
parameters:
|
||
- $ref: "#/components/parameters/ResourceId"
|
||
responses:
|
||
"200":
|
||
description: Key deleted
|
||
"401":
|
||
description: Authentication required
|
||
"404":
|
||
description: Key not found
|
||
|
||
/api/combos:
|
||
get:
|
||
tags: [Combos]
|
||
summary: List routing combos
|
||
responses:
|
||
"200":
|
||
description: Combo list
|
||
post:
|
||
tags: [Combos]
|
||
summary: Create routing combo
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ComboCreate"
|
||
responses:
|
||
"201":
|
||
description: Created combo
|
||
|
||
/api/combos/{id}:
|
||
patch:
|
||
tags: [Combos]
|
||
summary: Update combo
|
||
parameters:
|
||
- $ref: "#/components/parameters/ResourceId"
|
||
responses:
|
||
"200":
|
||
description: Updated combo
|
||
delete:
|
||
tags: [Combos]
|
||
summary: Delete combo
|
||
parameters:
|
||
- $ref: "#/components/parameters/ResourceId"
|
||
responses:
|
||
"200":
|
||
description: Combo deleted
|
||
|
||
/api/combos/metrics:
|
||
get:
|
||
tags: [Combos]
|
||
summary: Get combo metrics
|
||
responses:
|
||
"200":
|
||
description: Metrics for combos
|
||
|
||
/api/combos/test:
|
||
post:
|
||
tags: [Combos]
|
||
summary: Test a combo configuration
|
||
responses:
|
||
"200":
|
||
description: Test result
|
||
|
||
/api/settings:
|
||
get:
|
||
tags: [Settings]
|
||
summary: Get application settings
|
||
responses:
|
||
"200":
|
||
description: Current settings
|
||
patch:
|
||
tags: [Settings]
|
||
summary: Update settings
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
responses:
|
||
"200":
|
||
description: Updated settings
|
||
|
||
/api/settings/purge-request-history:
|
||
post:
|
||
tags: [Settings]
|
||
summary: Clear request log history
|
||
description: Deletes `call_logs`, legacy `request_detail_logs`, and local request artifact files under `DATA_DIR/call_logs`.
|
||
security:
|
||
- ManagementSessionAuth: []
|
||
responses:
|
||
"200":
|
||
description: Request history cleared
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
deleted:
|
||
type: integer
|
||
deletedArtifacts:
|
||
type: integer
|
||
deletedDetailedLogs:
|
||
type: integer
|
||
errors:
|
||
type: integer
|
||
"401":
|
||
description: Unauthorized
|
||
"500":
|
||
description: Cleanup failed or reported errors
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
deleted:
|
||
type: integer
|
||
deletedArtifacts:
|
||
type: integer
|
||
deletedDetailedLogs:
|
||
type: integer
|
||
errors:
|
||
type: integer
|
||
error:
|
||
type: object
|
||
|
||
/api/settings/compression:
|
||
get:
|
||
tags: [Compression]
|
||
summary: Get global compression settings
|
||
security:
|
||
- ManagementSessionAuth: []
|
||
responses:
|
||
"200":
|
||
description: Current compression settings
|
||
put:
|
||
tags: [Compression]
|
||
summary: Update global compression settings
|
||
security:
|
||
- ManagementSessionAuth: []
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
enabled:
|
||
type: boolean
|
||
defaultMode:
|
||
type: string
|
||
enum: [off, lite, standard, aggressive, ultra, rtk, stacked]
|
||
autoTriggerMode:
|
||
type: string
|
||
enum: [off, lite, standard, aggressive, ultra, rtk, stacked]
|
||
autoTriggerTokens:
|
||
type: integer
|
||
minimum: 0
|
||
rtkConfig:
|
||
type: object
|
||
additionalProperties: true
|
||
stackedPipeline:
|
||
type: array
|
||
items:
|
||
type: object
|
||
responses:
|
||
"200":
|
||
description: Updated compression settings
|
||
|
||
/api/settings/compression/mcp-accessibility:
|
||
get:
|
||
tags: [Compression]
|
||
summary: Get the MCP tool-output accessibility (trimming) config
|
||
security:
|
||
- ManagementSessionAuth: []
|
||
responses:
|
||
"200":
|
||
description: Current mcpAccessibility config
|
||
put:
|
||
tags: [Compression]
|
||
summary: Update the MCP tool-output accessibility (trimming) config
|
||
description: >-
|
||
Partial-merge update. Numeric floors (e.g. a maxTextChars below the truncation-tail
|
||
reserve) are folded back to the safe defaults server-side, so the response reflects the
|
||
effective config.
|
||
security:
|
||
- ManagementSessionAuth: []
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
enabled:
|
||
type: boolean
|
||
maxTextChars:
|
||
type: integer
|
||
minimum: 1
|
||
collapseThreshold:
|
||
type: integer
|
||
minimum: 1
|
||
collapseKeepHead:
|
||
type: integer
|
||
minimum: 0
|
||
collapseKeepTail:
|
||
type: integer
|
||
minimum: 0
|
||
minLengthToProcess:
|
||
type: integer
|
||
minimum: 1
|
||
responses:
|
||
"200":
|
||
description: Updated mcpAccessibility config (numeric floors applied)
|
||
|
||
/api/compression/preview:
|
||
post:
|
||
tags: [Compression]
|
||
summary: Preview compression for a message payload
|
||
security:
|
||
- BearerAuth: []
|
||
- ManagementSessionAuth: []
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [messages, mode]
|
||
properties:
|
||
mode:
|
||
type: string
|
||
enum: [off, lite, standard, aggressive, ultra, rtk, stacked]
|
||
messages:
|
||
type: array
|
||
items:
|
||
type: object
|
||
required: [role, content]
|
||
properties:
|
||
role:
|
||
type: string
|
||
content:
|
||
oneOf:
|
||
- type: string
|
||
- type: array
|
||
items: {}
|
||
config:
|
||
type: object
|
||
additionalProperties: true
|
||
responses:
|
||
"200":
|
||
description: Compression preview with diff, validation, and stats
|
||
|
||
/api/compression/language-packs:
|
||
get:
|
||
tags: [Compression]
|
||
summary: List Caveman compression language packs
|
||
security:
|
||
- BearerAuth: []
|
||
- ManagementSessionAuth: []
|
||
responses:
|
||
"200":
|
||
description: Available languages and rule-pack metadata
|
||
|
||
/api/compression/rules:
|
||
get:
|
||
tags: [Compression]
|
||
summary: List Caveman compression rule metadata
|
||
security:
|
||
- BearerAuth: []
|
||
- ManagementSessionAuth: []
|
||
responses:
|
||
"200":
|
||
description: Caveman rule metadata
|
||
|
||
/api/context/rtk/config:
|
||
get:
|
||
tags: [Compression]
|
||
summary: Get RTK compression settings
|
||
security:
|
||
- ManagementSessionAuth: []
|
||
responses:
|
||
"200":
|
||
description: Current RTK config
|
||
put:
|
||
tags: [Compression]
|
||
summary: Update RTK compression settings
|
||
security:
|
||
- ManagementSessionAuth: []
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
enabled:
|
||
type: boolean
|
||
intensity:
|
||
type: string
|
||
enum: [minimal, standard, aggressive]
|
||
customFiltersEnabled:
|
||
type: boolean
|
||
trustProjectFilters:
|
||
type: boolean
|
||
rawOutputRetention:
|
||
type: string
|
||
enum: [never, failures, always]
|
||
rawOutputMaxBytes:
|
||
type: integer
|
||
responses:
|
||
"200":
|
||
description: Updated RTK config
|
||
|
||
/api/context/rtk/filters:
|
||
get:
|
||
tags: [Compression]
|
||
summary: List RTK filters and load diagnostics
|
||
security:
|
||
- ManagementSessionAuth: []
|
||
responses:
|
||
"200":
|
||
description: RTK filter catalog and diagnostics
|
||
|
||
/api/context/rtk/test:
|
||
post:
|
||
tags: [Compression]
|
||
summary: Run RTK compression preview for text
|
||
security:
|
||
- ManagementSessionAuth: []
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [text]
|
||
properties:
|
||
text:
|
||
type: string
|
||
command:
|
||
type: string
|
||
config:
|
||
type: object
|
||
additionalProperties: true
|
||
responses:
|
||
"200":
|
||
description: Detection and RTK compression result
|
||
|
||
/api/context/rtk/raw-output/{id}:
|
||
get:
|
||
tags: [Compression]
|
||
summary: Read retained redacted RTK raw output
|
||
security:
|
||
- ManagementSessionAuth: []
|
||
parameters:
|
||
- in: path
|
||
name: id
|
||
required: true
|
||
schema:
|
||
type: string
|
||
pattern: "^[a-f0-9]{24}$"
|
||
responses:
|
||
"200":
|
||
description: Raw output text
|
||
"404":
|
||
description: Raw output not found
|
||
|
||
/api/settings/payload-rules:
|
||
get:
|
||
tags: [Settings]
|
||
summary: Get payload rules configuration
|
||
description: |
|
||
Returns the current payload rules used to mutate outgoing request payloads before they
|
||
are sent upstream.
|
||
|
||
Requires a dashboard management session cookie when management auth is enabled.
|
||
security:
|
||
- ManagementSessionAuth: []
|
||
responses:
|
||
"200":
|
||
description: Current payload rules configuration
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/PayloadRulesConfig"
|
||
"401":
|
||
$ref: "#/components/responses/ManagementAuthenticationRequired"
|
||
"403":
|
||
$ref: "#/components/responses/ManagementInvalidToken"
|
||
"500":
|
||
description: Failed to read payload rules configuration
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ApiErrorResponse"
|
||
put:
|
||
tags: [Settings]
|
||
summary: Update payload rules configuration
|
||
description: |
|
||
Persists and hot reloads payload rules. The legacy input field `default-raw` is accepted
|
||
on writes and normalized to `defaultRaw` in responses/runtime state.
|
||
|
||
Requires a dashboard management session cookie when management auth is enabled.
|
||
security:
|
||
- ManagementSessionAuth: []
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/UpdatePayloadRulesRequest"
|
||
responses:
|
||
"200":
|
||
description: Updated payload rules configuration
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/PayloadRulesConfig"
|
||
"400":
|
||
$ref: "#/components/responses/ValidationError"
|
||
"401":
|
||
$ref: "#/components/responses/ManagementAuthenticationRequired"
|
||
"403":
|
||
$ref: "#/components/responses/ManagementInvalidToken"
|
||
"500":
|
||
description: Failed to update payload rules configuration
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ApiErrorResponse"
|
||
|
||
/api/settings/combo-defaults:
|
||
get:
|
||
tags: [Settings]
|
||
summary: Get combo default settings
|
||
responses:
|
||
"200":
|
||
description: Default combo settings
|
||
|
||
/api/settings/proxy:
|
||
get:
|
||
tags: [Settings]
|
||
summary: Get proxy settings
|
||
responses:
|
||
"200":
|
||
description: Current proxy settings
|
||
patch:
|
||
tags: [Settings]
|
||
summary: Update proxy settings
|
||
responses:
|
||
"200":
|
||
description: Updated proxy settings
|
||
|
||
/api/settings/proxy/test:
|
||
post:
|
||
tags: [Settings]
|
||
summary: Test proxy connection
|
||
responses:
|
||
"200":
|
||
description: Test result
|
||
|
||
/api/settings/require-login:
|
||
post:
|
||
tags: [Settings]
|
||
summary: Toggle login requirement
|
||
responses:
|
||
"200":
|
||
description: Updated
|
||
|
||
/api/settings/ip-filter:
|
||
get:
|
||
tags: [Settings]
|
||
summary: Get IP filter configuration
|
||
description: Returns the current IP filter settings including blacklist, whitelist, and temp bans.
|
||
responses:
|
||
"200":
|
||
description: IP filter configuration
|
||
put:
|
||
tags: [Settings]
|
||
summary: Update IP filter configuration
|
||
description: |
|
||
Configure IP filtering with blacklist/whitelist modes, add/remove individual IPs, and manage temp bans.
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
enabled:
|
||
type: boolean
|
||
mode:
|
||
type: string
|
||
enum: [blacklist, whitelist]
|
||
blacklist:
|
||
type: array
|
||
items:
|
||
type: string
|
||
whitelist:
|
||
type: array
|
||
items:
|
||
type: string
|
||
addBlacklist:
|
||
type: string
|
||
removeBlacklist:
|
||
type: string
|
||
addWhitelist:
|
||
type: string
|
||
removeWhitelist:
|
||
type: string
|
||
tempBan:
|
||
type: object
|
||
properties:
|
||
ip:
|
||
type: string
|
||
durationMs:
|
||
type: integer
|
||
reason:
|
||
type: string
|
||
removeBan:
|
||
type: string
|
||
responses:
|
||
"200":
|
||
description: Updated IP filter configuration
|
||
|
||
/api/settings/system-prompt:
|
||
get:
|
||
tags: [Settings]
|
||
summary: Get system prompt configuration
|
||
description: Returns the current system prompt injection settings.
|
||
responses:
|
||
"200":
|
||
description: System prompt configuration
|
||
put:
|
||
tags: [Settings]
|
||
summary: Update system prompt configuration
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
prompt:
|
||
type: string
|
||
enabled:
|
||
type: boolean
|
||
responses:
|
||
"200":
|
||
description: Updated system prompt configuration
|
||
|
||
/api/settings/thinking-budget:
|
||
get:
|
||
tags: [Settings]
|
||
summary: Get thinking budget configuration
|
||
description: Returns the current thinking/reasoning budget settings for AI models.
|
||
responses:
|
||
"200":
|
||
description: Thinking budget configuration
|
||
put:
|
||
tags: [Settings]
|
||
summary: Update thinking budget configuration
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
mode:
|
||
type: string
|
||
description: Thinking mode (e.g., auto, manual, disabled)
|
||
customBudget:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 131072
|
||
effortLevel:
|
||
type: string
|
||
enum: [none, low, medium, high]
|
||
responses:
|
||
"200":
|
||
description: Updated thinking budget configuration
|
||
|
||
/api/rate-limit:
|
||
get:
|
||
tags: [Settings]
|
||
summary: Get rate limit configuration
|
||
responses:
|
||
"200":
|
||
description: Rate limit settings
|
||
post:
|
||
tags: [Settings]
|
||
summary: Update rate limit configuration
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
responses:
|
||
"200":
|
||
description: Updated rate limit settings
|
||
|
||
/api/tags:
|
||
get:
|
||
tags: [System]
|
||
summary: List Ollama-compatible model tags
|
||
description: Returns models in Ollama /api/tags format for Ollama client compatibility
|
||
responses:
|
||
"200":
|
||
description: Ollama model tags
|
||
|
||
# ─── Usage & Analytics ─────────────────────────────────────────
|
||
|
||
/api/usage/analytics:
|
||
get:
|
||
tags: [Usage]
|
||
summary: Get usage analytics
|
||
parameters:
|
||
- name: period
|
||
in: query
|
||
schema:
|
||
type: string
|
||
enum: [day, week, month]
|
||
default: day
|
||
responses:
|
||
"200":
|
||
description: Usage analytics data
|
||
|
||
/api/usage/call-logs:
|
||
get:
|
||
tags: [Usage]
|
||
summary: Get call logs
|
||
parameters:
|
||
- name: limit
|
||
in: query
|
||
schema:
|
||
type: integer
|
||
default: 50
|
||
- name: offset
|
||
in: query
|
||
schema:
|
||
type: integer
|
||
default: 0
|
||
responses:
|
||
"200":
|
||
description: Paginated call logs
|
||
|
||
/api/usage/call-logs/{id}:
|
||
get:
|
||
tags: [Usage]
|
||
summary: Get a specific call log
|
||
parameters:
|
||
- $ref: "#/components/parameters/ResourceId"
|
||
responses:
|
||
"200":
|
||
description: Call log detail
|
||
|
||
/api/usage/{connectionId}:
|
||
get:
|
||
tags: [Usage]
|
||
summary: Get usage for a specific connection
|
||
parameters:
|
||
- name: connectionId
|
||
in: path
|
||
required: true
|
||
schema:
|
||
type: string
|
||
responses:
|
||
"200":
|
||
description: Connection usage data
|
||
|
||
/api/usage/history:
|
||
get:
|
||
tags: [Usage]
|
||
summary: Get usage history
|
||
responses:
|
||
"200":
|
||
description: Historical usage data
|
||
|
||
/api/usage/logs:
|
||
get:
|
||
tags: [Usage]
|
||
summary: Get usage logs
|
||
responses:
|
||
"200":
|
||
description: Usage log entries
|
||
|
||
/api/usage/proxy-logs:
|
||
get:
|
||
tags: [Usage]
|
||
summary: Get proxy logs
|
||
responses:
|
||
"200":
|
||
description: Proxy log entries
|
||
|
||
/api/usage/request-logs:
|
||
get:
|
||
tags: [Usage]
|
||
summary: Get request logs
|
||
responses:
|
||
"200":
|
||
description: Request log entries
|
||
|
||
/api/usage/budget:
|
||
get:
|
||
tags: [Usage]
|
||
summary: Get usage budget status
|
||
description: Returns current budget limits and consumption.
|
||
responses:
|
||
"200":
|
||
description: Budget status
|
||
post:
|
||
tags: [Usage]
|
||
summary: Configure usage budget
|
||
description: Set or update budget limits for usage tracking.
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
responses:
|
||
"200":
|
||
description: Updated budget configuration
|
||
|
||
# ─── Pricing ───────────────────────────────────────────────────
|
||
|
||
/api/pricing:
|
||
get:
|
||
tags: [Pricing]
|
||
summary: Get model pricing
|
||
responses:
|
||
"200":
|
||
description: Current pricing configuration
|
||
post:
|
||
tags: [Pricing]
|
||
summary: Set model pricing
|
||
responses:
|
||
"200":
|
||
description: Updated pricing
|
||
|
||
/api/pricing/defaults:
|
||
get:
|
||
tags: [Pricing]
|
||
summary: Get default pricing
|
||
responses:
|
||
"200":
|
||
description: Default pricing data
|
||
|
||
/api/pricing/models:
|
||
get:
|
||
tags: [Pricing]
|
||
summary: Get pricing per model
|
||
description: Returns pricing information organized by model.
|
||
responses:
|
||
"200":
|
||
description: Per-model pricing data
|
||
|
||
# ─── Translator ────────────────────────────────────────────────
|
||
|
||
/api/translator/detect:
|
||
post:
|
||
tags: [Translator]
|
||
summary: Detect request format
|
||
description: Detects the API format of a request body (OpenAI, Claude, Gemini, etc.)
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [body]
|
||
properties:
|
||
body:
|
||
type: object
|
||
responses:
|
||
"200":
|
||
description: Detected format
|
||
|
||
/api/translator/translate:
|
||
post:
|
||
tags: [Translator]
|
||
summary: Translate between formats
|
||
description: Converts a request between API formats (e.g. Claude → OpenAI)
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [sourceFormat, targetFormat, body]
|
||
properties:
|
||
step:
|
||
type: string
|
||
sourceFormat:
|
||
type: string
|
||
targetFormat:
|
||
type: string
|
||
provider:
|
||
type: string
|
||
body:
|
||
type: object
|
||
responses:
|
||
"200":
|
||
description: Translated request
|
||
|
||
/api/translator/send:
|
||
post:
|
||
tags: [Translator]
|
||
summary: Send translated request to provider
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [provider, body]
|
||
properties:
|
||
provider:
|
||
type: string
|
||
body:
|
||
type: object
|
||
responses:
|
||
"200":
|
||
description: Provider response (may be SSE stream)
|
||
|
||
/api/translator/history:
|
||
get:
|
||
tags: [Translator]
|
||
summary: Get translation history
|
||
description: Returns recent translation events for the Live Monitor
|
||
responses:
|
||
"200":
|
||
description: Translation history entries
|
||
|
||
# ─── CLI Remote Mode ───────────────────────────────────────────
|
||
|
||
/api/cli/connect:
|
||
post:
|
||
tags: [CLI Remote Mode]
|
||
summary: Exchange the management password for a scoped CLI access token
|
||
description: >
|
||
Remote-mode bootstrap. Public (password-gated) route: verifies the
|
||
management password with brute-force lockout, then mints an `oma_`
|
||
access token. The plaintext token is returned once.
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [password]
|
||
properties:
|
||
password: { type: string }
|
||
name: { type: string }
|
||
scope: { type: string, enum: [read, write, admin] }
|
||
expiresInDays: { type: integer, minimum: 1, maximum: 3650 }
|
||
responses:
|
||
"200":
|
||
description: Token minted (token returned once)
|
||
"401":
|
||
description: Invalid password
|
||
"429":
|
||
description: Too many failed attempts
|
||
|
||
/api/cli/whoami:
|
||
get:
|
||
tags: [CLI Remote Mode]
|
||
summary: Report the current credential (scope, name, expiry)
|
||
responses:
|
||
"200":
|
||
description: Authenticated; access-token details when applicable
|
||
"401":
|
||
description: Authentication required
|
||
|
||
/api/cli/tokens:
|
||
get:
|
||
tags: [CLI Remote Mode]
|
||
summary: List access tokens (masked) — admin scope
|
||
responses:
|
||
"200":
|
||
description: Masked token list
|
||
"403":
|
||
description: Insufficient scope
|
||
post:
|
||
tags: [CLI Remote Mode]
|
||
summary: Create a scoped access token — admin scope
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [name]
|
||
properties:
|
||
name: { type: string }
|
||
scope: { type: string, enum: [read, write, admin] }
|
||
expiresInDays: { type: integer, minimum: 1, maximum: 3650 }
|
||
responses:
|
||
"200":
|
||
description: Token created (token returned once)
|
||
"403":
|
||
description: Insufficient scope
|
||
|
||
/api/cli/tokens/{id}:
|
||
delete:
|
||
tags: [CLI Remote Mode]
|
||
summary: Revoke an access token by id or display prefix — admin scope
|
||
parameters:
|
||
- name: id
|
||
in: path
|
||
required: true
|
||
schema: { type: string }
|
||
responses:
|
||
"200":
|
||
description: Token revoked
|
||
"403":
|
||
description: Insufficient scope
|
||
"404":
|
||
description: Token not found or already revoked
|
||
|
||
# ─── CLI Tools ─────────────────────────────────────────────────
|
||
|
||
/api/cli-tools/backups:
|
||
get:
|
||
tags: [CLI Tools]
|
||
summary: List CLI tool backups
|
||
responses:
|
||
"200":
|
||
description: Backup list
|
||
post:
|
||
tags: [CLI Tools]
|
||
summary: Create CLI tool backup
|
||
responses:
|
||
"200":
|
||
description: Backup created
|
||
|
||
/api/cli-tools/runtime/{toolId}:
|
||
get:
|
||
tags: [CLI Tools]
|
||
summary: Get runtime status for a CLI tool
|
||
parameters:
|
||
- name: toolId
|
||
in: path
|
||
required: true
|
||
schema:
|
||
type: string
|
||
responses:
|
||
"200":
|
||
description: Runtime status
|
||
|
||
/api/cli-tools/guide-settings/{toolId}:
|
||
get:
|
||
tags: [CLI Tools]
|
||
summary: Get guide settings for a tool
|
||
parameters:
|
||
- name: toolId
|
||
in: path
|
||
required: true
|
||
schema:
|
||
type: string
|
||
responses:
|
||
"200":
|
||
description: Guide settings
|
||
|
||
/api/cli-tools/antigravity-mitm:
|
||
get:
|
||
tags: [CLI Tools]
|
||
summary: Get Antigravity MITM proxy settings
|
||
responses:
|
||
"200":
|
||
description: MITM proxy configuration
|
||
post:
|
||
tags: [CLI Tools]
|
||
summary: Update Antigravity MITM proxy settings
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
responses:
|
||
"200":
|
||
description: Updated MITM proxy configuration
|
||
delete:
|
||
tags: [CLI Tools]
|
||
summary: Reset Antigravity MITM proxy settings
|
||
responses:
|
||
"200":
|
||
description: MITM proxy settings reset
|
||
|
||
/api/cli-tools/antigravity-mitm/alias:
|
||
get:
|
||
tags: [CLI Tools]
|
||
summary: Get Antigravity MITM alias configuration
|
||
responses:
|
||
"200":
|
||
description: Alias configuration
|
||
put:
|
||
tags: [CLI Tools]
|
||
summary: Update Antigravity MITM alias configuration
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
responses:
|
||
"200":
|
||
description: Updated alias configuration
|
||
|
||
/api/cli-tools/claude-settings:
|
||
get:
|
||
tags: [CLI Tools]
|
||
summary: Get Claude CLI settings
|
||
responses:
|
||
"200":
|
||
description: Claude CLI configuration
|
||
post:
|
||
tags: [CLI Tools]
|
||
summary: Apply Claude CLI settings
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
responses:
|
||
"200":
|
||
description: Claude CLI settings applied
|
||
delete:
|
||
tags: [CLI Tools]
|
||
summary: Reset Claude CLI settings
|
||
responses:
|
||
"200":
|
||
description: Claude CLI settings reset
|
||
|
||
/api/cli-tools/cline-settings:
|
||
get:
|
||
tags: [CLI Tools]
|
||
summary: Get Cline CLI settings
|
||
responses:
|
||
"200":
|
||
description: Cline CLI configuration
|
||
post:
|
||
tags: [CLI Tools]
|
||
summary: Apply Cline CLI settings
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
responses:
|
||
"200":
|
||
description: Cline CLI settings applied
|
||
delete:
|
||
tags: [CLI Tools]
|
||
summary: Reset Cline CLI settings
|
||
responses:
|
||
"200":
|
||
description: Cline CLI settings reset
|
||
|
||
/api/cli-tools/codex-profiles:
|
||
get:
|
||
tags: [CLI Tools]
|
||
summary: Get Codex profiles
|
||
responses:
|
||
"200":
|
||
description: Codex profile list
|
||
post:
|
||
tags: [CLI Tools]
|
||
summary: Create Codex profile
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
responses:
|
||
"200":
|
||
description: Profile created
|
||
put:
|
||
tags: [CLI Tools]
|
||
summary: Update Codex profile
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
responses:
|
||
"200":
|
||
description: Profile updated
|
||
delete:
|
||
tags: [CLI Tools]
|
||
summary: Delete Codex profile
|
||
responses:
|
||
"200":
|
||
description: Profile deleted
|
||
|
||
/api/cli-tools/codex-settings:
|
||
get:
|
||
tags: [CLI Tools]
|
||
summary: Get Codex CLI settings
|
||
responses:
|
||
"200":
|
||
description: Codex CLI configuration
|
||
post:
|
||
tags: [CLI Tools]
|
||
summary: Apply Codex CLI settings
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
responses:
|
||
"200":
|
||
description: Codex CLI settings applied
|
||
delete:
|
||
tags: [CLI Tools]
|
||
summary: Reset Codex CLI settings
|
||
responses:
|
||
"200":
|
||
description: Codex CLI settings reset
|
||
|
||
/api/cli-tools/droid-settings:
|
||
get:
|
||
tags: [CLI Tools]
|
||
summary: Get Droid CLI settings
|
||
responses:
|
||
"200":
|
||
description: Droid CLI configuration
|
||
post:
|
||
tags: [CLI Tools]
|
||
summary: Apply Droid CLI settings
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
responses:
|
||
"200":
|
||
description: Droid CLI settings applied
|
||
delete:
|
||
tags: [CLI Tools]
|
||
summary: Reset Droid CLI settings
|
||
responses:
|
||
"200":
|
||
description: Droid CLI settings reset
|
||
|
||
/api/cli-tools/kilo-settings:
|
||
get:
|
||
tags: [CLI Tools]
|
||
summary: Get Kilo CLI settings
|
||
responses:
|
||
"200":
|
||
description: Kilo CLI configuration
|
||
post:
|
||
tags: [CLI Tools]
|
||
summary: Apply Kilo CLI settings
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
responses:
|
||
"200":
|
||
description: Kilo CLI settings applied
|
||
delete:
|
||
tags: [CLI Tools]
|
||
summary: Reset Kilo CLI settings
|
||
responses:
|
||
"200":
|
||
description: Kilo CLI settings reset
|
||
|
||
/api/cli-tools/openclaw-settings:
|
||
get:
|
||
tags: [CLI Tools]
|
||
summary: Get OpenClaw CLI settings
|
||
responses:
|
||
"200":
|
||
description: OpenClaw CLI configuration
|
||
post:
|
||
tags: [CLI Tools]
|
||
summary: Apply OpenClaw CLI settings
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
responses:
|
||
"200":
|
||
description: OpenClaw CLI settings applied
|
||
delete:
|
||
tags: [CLI Tools]
|
||
summary: Reset OpenClaw CLI settings
|
||
responses:
|
||
"200":
|
||
description: OpenClaw CLI settings reset
|
||
|
||
# ─── Embedded Services ─────────────────────────────────────────
|
||
# All routes LOCAL_ONLY (loopback only) — hard rule #17.
|
||
# See docs/frameworks/EMBEDDED-SERVICES.md for full reference.
|
||
|
||
/api/services/9router/install:
|
||
post:
|
||
tags: [Embedded Services]
|
||
summary: Install 9Router from npm
|
||
description: >-
|
||
Installs the `9router` npm package under DATA_DIR/services/9router/.
|
||
Uses execFile (no shell interpolation — hard rule #13).
|
||
**LOCAL_ONLY** — loopback only.
|
||
requestBody:
|
||
required: false
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
version:
|
||
type: string
|
||
default: latest
|
||
description: npm version tag or semver to install
|
||
responses:
|
||
"200":
|
||
description: Install succeeded
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
ok:
|
||
type: boolean
|
||
installedVersion:
|
||
type: string
|
||
path:
|
||
type: string
|
||
"400":
|
||
description: Invalid request body
|
||
"500":
|
||
description: npm install failed
|
||
|
||
/api/services/9router/start:
|
||
post:
|
||
tags: [Embedded Services]
|
||
summary: Start 9Router
|
||
description: >-
|
||
Spawns the 9Router process. Idempotent if already running.
|
||
**LOCAL_ONLY** — loopback only.
|
||
responses:
|
||
"200":
|
||
description: Service started (or already running)
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ServiceStatus"
|
||
"409":
|
||
description: 9Router is not installed
|
||
"503":
|
||
description: Start failed
|
||
|
||
/api/services/9router/stop:
|
||
post:
|
||
tags: [Embedded Services]
|
||
summary: Stop 9Router
|
||
description: >-
|
||
Gracefully stops 9Router (SIGTERM → 15 s → SIGKILL). Idempotent.
|
||
**LOCAL_ONLY** — loopback only.
|
||
responses:
|
||
"200":
|
||
description: Service stopped
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ServiceStatus"
|
||
"503":
|
||
description: Stop failed
|
||
|
||
/api/services/9router/restart:
|
||
post:
|
||
tags: [Embedded Services]
|
||
summary: Restart 9Router
|
||
description: >-
|
||
Equivalent to stop() then start() under the operation lock.
|
||
**LOCAL_ONLY** — loopback only.
|
||
responses:
|
||
"200":
|
||
description: Service restarted
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ServiceStatus"
|
||
|
||
/api/services/9router/update:
|
||
post:
|
||
tags: [Embedded Services]
|
||
summary: Update 9Router to a newer npm version
|
||
description: >-
|
||
Stops the service (if running), installs the newer npm version, then restarts.
|
||
**LOCAL_ONLY** — loopback only.
|
||
requestBody:
|
||
required: false
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
version:
|
||
type: string
|
||
default: latest
|
||
responses:
|
||
"200":
|
||
description: Update succeeded
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
ok:
|
||
type: boolean
|
||
previousVersion:
|
||
type: string
|
||
installedVersion:
|
||
type: string
|
||
"400":
|
||
description: Invalid request body
|
||
"500":
|
||
description: Update failed
|
||
|
||
/api/services/9router/rotate-key:
|
||
post:
|
||
tags: [Embedded Services]
|
||
summary: Rotate the 9Router API key
|
||
description: >-
|
||
Generates a new API key, encrypts it at-rest, and restarts the service to
|
||
apply it. The plaintext key is never returned.
|
||
**LOCAL_ONLY** — loopback only.
|
||
responses:
|
||
"200":
|
||
description: Key rotated
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
keyRotated:
|
||
type: boolean
|
||
restarted:
|
||
type: boolean
|
||
"500":
|
||
description: Rotation failed
|
||
|
||
/api/services/9router/status:
|
||
get:
|
||
tags: [Embedded Services]
|
||
summary: Get 9Router status
|
||
description: >-
|
||
Returns combined live supervisor state and DB metadata.
|
||
**LOCAL_ONLY** — loopback only.
|
||
responses:
|
||
"200":
|
||
description: Status response
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ServiceStatusExtended"
|
||
"500":
|
||
description: Status read failed
|
||
|
||
/api/services/9router/auto-start:
|
||
post:
|
||
tags: [Embedded Services]
|
||
summary: Toggle 9Router auto-start
|
||
description: >-
|
||
When enabled, 9Router starts automatically on the next OmniRoute boot.
|
||
**LOCAL_ONLY** — loopback only.
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [enabled]
|
||
properties:
|
||
enabled:
|
||
type: boolean
|
||
responses:
|
||
"200":
|
||
description: Auto-start flag updated
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
autoStart:
|
||
type: boolean
|
||
"400":
|
||
description: Invalid request body
|
||
|
||
/api/services/cliproxy/install:
|
||
post:
|
||
tags: [Embedded Services]
|
||
summary: Install CLIProxyAPI from npm
|
||
description: >-
|
||
Installs the CLIProxyAPI package under DATA_DIR/services/cliproxy/.
|
||
**LOCAL_ONLY** — loopback only.
|
||
requestBody:
|
||
required: false
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
version:
|
||
type: string
|
||
default: latest
|
||
responses:
|
||
"200":
|
||
description: Install succeeded
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
ok:
|
||
type: boolean
|
||
installedVersion:
|
||
type: string
|
||
"400":
|
||
description: Invalid request body
|
||
"500":
|
||
description: npm install failed
|
||
|
||
/api/services/cliproxy/start:
|
||
post:
|
||
tags: [Embedded Services]
|
||
summary: Start CLIProxyAPI
|
||
description: >-
|
||
Spawns the CLIProxyAPI process. Idempotent if already running.
|
||
**LOCAL_ONLY** — loopback only.
|
||
responses:
|
||
"200":
|
||
description: Service started
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ServiceStatus"
|
||
"409":
|
||
description: CLIProxyAPI is not installed
|
||
"503":
|
||
description: Start failed
|
||
|
||
/api/services/cliproxy/stop:
|
||
post:
|
||
tags: [Embedded Services]
|
||
summary: Stop CLIProxyAPI
|
||
description: >-
|
||
Gracefully stops CLIProxyAPI. Idempotent.
|
||
**LOCAL_ONLY** — loopback only.
|
||
responses:
|
||
"200":
|
||
description: Service stopped
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ServiceStatus"
|
||
|
||
/api/services/cliproxy/restart:
|
||
post:
|
||
tags: [Embedded Services]
|
||
summary: Restart CLIProxyAPI
|
||
description: >-
|
||
stop() then start() under the operation lock.
|
||
**LOCAL_ONLY** — loopback only.
|
||
responses:
|
||
"200":
|
||
description: Service restarted
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ServiceStatus"
|
||
|
||
/api/services/cliproxy/update:
|
||
post:
|
||
tags: [Embedded Services]
|
||
summary: Update CLIProxyAPI to a newer npm version
|
||
description: >-
|
||
Stops, installs newer version, restarts.
|
||
**LOCAL_ONLY** — loopback only.
|
||
requestBody:
|
||
required: false
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
version:
|
||
type: string
|
||
default: latest
|
||
responses:
|
||
"200":
|
||
description: Update succeeded
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
ok:
|
||
type: boolean
|
||
installedVersion:
|
||
type: string
|
||
"500":
|
||
description: Update failed
|
||
|
||
/api/services/cliproxy/status:
|
||
get:
|
||
tags: [Embedded Services]
|
||
summary: Get CLIProxyAPI status
|
||
description: >-
|
||
Returns live supervisor state and DB metadata (no apiKeyMasked — CLIProxyAPI
|
||
does not use an injected API key).
|
||
**LOCAL_ONLY** — loopback only.
|
||
responses:
|
||
"200":
|
||
description: Status response
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ServiceStatus"
|
||
|
||
/api/services/cliproxy/auto-start:
|
||
post:
|
||
tags: [Embedded Services]
|
||
summary: Toggle CLIProxyAPI auto-start
|
||
description: >-
|
||
When enabled, CLIProxyAPI starts automatically on the next OmniRoute boot.
|
||
**LOCAL_ONLY** — loopback only.
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [enabled]
|
||
properties:
|
||
enabled:
|
||
type: boolean
|
||
responses:
|
||
"200":
|
||
description: Auto-start flag updated
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
autoStart:
|
||
type: boolean
|
||
"400":
|
||
description: Invalid request body
|
||
|
||
/api/services/mux/install:
|
||
post:
|
||
tags: [Embedded Services]
|
||
summary: Install Mux from npm
|
||
description: >-
|
||
Installs the `mux` npm package (coder/mux — local agent-orchestration
|
||
daemon) under DATA_DIR/services/mux/. **LOCAL_ONLY** — loopback only.
|
||
requestBody:
|
||
required: false
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
version:
|
||
type: string
|
||
default: latest
|
||
responses:
|
||
"200":
|
||
description: Install succeeded
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
ok:
|
||
type: boolean
|
||
installedVersion:
|
||
type: string
|
||
"400":
|
||
description: Invalid request body
|
||
"500":
|
||
description: npm install failed
|
||
|
||
/api/services/mux/start:
|
||
post:
|
||
tags: [Embedded Services]
|
||
summary: Start Mux
|
||
description: >-
|
||
Spawns `mux server --host 127.0.0.1 --port <port>`. Idempotent if
|
||
already running. **LOCAL_ONLY** — loopback only.
|
||
responses:
|
||
"200":
|
||
description: Service started
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ServiceStatus"
|
||
"409":
|
||
description: Mux is not installed
|
||
"503":
|
||
description: Start failed
|
||
|
||
/api/services/mux/stop:
|
||
post:
|
||
tags: [Embedded Services]
|
||
summary: Stop Mux
|
||
description: >-
|
||
Gracefully stops Mux. Idempotent.
|
||
**LOCAL_ONLY** — loopback only.
|
||
responses:
|
||
"200":
|
||
description: Service stopped
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ServiceStatus"
|
||
|
||
/api/services/mux/restart:
|
||
post:
|
||
tags: [Embedded Services]
|
||
summary: Restart Mux
|
||
description: >-
|
||
stop() then start() under the operation lock.
|
||
**LOCAL_ONLY** — loopback only.
|
||
responses:
|
||
"200":
|
||
description: Service restarted
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ServiceStatus"
|
||
|
||
/api/services/mux/update:
|
||
post:
|
||
tags: [Embedded Services]
|
||
summary: Update Mux to a newer npm version
|
||
description: >-
|
||
Stops, installs newer version, restarts.
|
||
**LOCAL_ONLY** — loopback only.
|
||
requestBody:
|
||
required: false
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
version:
|
||
type: string
|
||
default: latest
|
||
responses:
|
||
"200":
|
||
description: Update succeeded
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
ok:
|
||
type: boolean
|
||
installedVersion:
|
||
type: string
|
||
"500":
|
||
description: Update failed
|
||
|
||
/api/services/mux/status:
|
||
get:
|
||
tags: [Embedded Services]
|
||
summary: Get Mux status
|
||
description: >-
|
||
Returns live supervisor state and DB metadata.
|
||
**LOCAL_ONLY** — loopback only.
|
||
responses:
|
||
"200":
|
||
description: Status response
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ServiceStatus"
|
||
|
||
/api/services/mux/auto-start:
|
||
post:
|
||
tags: [Embedded Services]
|
||
summary: Toggle Mux auto-start
|
||
description: >-
|
||
When enabled, Mux starts automatically on the next OmniRoute boot.
|
||
**LOCAL_ONLY** — loopback only.
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [enabled]
|
||
properties:
|
||
enabled:
|
||
type: boolean
|
||
responses:
|
||
"200":
|
||
description: Auto-start flag updated
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
autoStart:
|
||
type: boolean
|
||
"400":
|
||
description: Invalid request body
|
||
|
||
/api/services/bifrost/install:
|
||
post:
|
||
tags: [Embedded Services]
|
||
summary: Install Bifrost
|
||
description: >-
|
||
Installs the `@maximhq/bifrost` npm package under DATA_DIR/services/bifrost/.
|
||
The package downloads the Go binary on first run. Accepts an optional `version`
|
||
field (semver or `latest`). **LOCAL_ONLY** — loopback only.
|
||
requestBody:
|
||
required: false
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
version:
|
||
type: string
|
||
default: latest
|
||
responses:
|
||
"200":
|
||
description: Installation result
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
ok:
|
||
type: boolean
|
||
installedVersion:
|
||
type: string
|
||
installPath:
|
||
type: string
|
||
durationMs:
|
||
type: number
|
||
|
||
/api/services/bifrost/start:
|
||
post:
|
||
tags: [Embedded Services]
|
||
summary: Start Bifrost
|
||
description: Starts the supervised Bifrost process. **LOCAL_ONLY** — loopback only.
|
||
responses:
|
||
"200":
|
||
description: Service status after start
|
||
"409":
|
||
description: Bifrost is not installed
|
||
|
||
/api/services/bifrost/stop:
|
||
post:
|
||
tags: [Embedded Services]
|
||
summary: Stop Bifrost
|
||
description: Stops the supervised Bifrost process. **LOCAL_ONLY** — loopback only.
|
||
responses:
|
||
"200":
|
||
description: Service status after stop
|
||
|
||
/api/services/bifrost/restart:
|
||
post:
|
||
tags: [Embedded Services]
|
||
summary: Restart Bifrost
|
||
description: Restarts the supervised Bifrost process. **LOCAL_ONLY** — loopback only.
|
||
responses:
|
||
"200":
|
||
description: Service status after restart
|
||
"409":
|
||
description: Bifrost is not installed
|
||
|
||
/api/services/bifrost/update:
|
||
post:
|
||
tags: [Embedded Services]
|
||
summary: Update Bifrost
|
||
description: >-
|
||
Updates Bifrost to the latest npm version. Stops the running process,
|
||
installs the new version, and restarts if it was previously running.
|
||
**LOCAL_ONLY** — loopback only.
|
||
responses:
|
||
"200":
|
||
description: Update result
|
||
|
||
/api/services/bifrost/status:
|
||
get:
|
||
tags: [Embedded Services]
|
||
summary: Get Bifrost status
|
||
description: Returns live and DB status for the supervised Bifrost service. **LOCAL_ONLY** — loopback only.
|
||
responses:
|
||
"200":
|
||
description: Bifrost service status
|
||
|
||
/api/services/bifrost/auto-start:
|
||
post:
|
||
tags: [Embedded Services]
|
||
summary: Toggle Bifrost auto-start
|
||
description: >-
|
||
When enabled, Bifrost starts automatically on the next OmniRoute boot.
|
||
**LOCAL_ONLY** — loopback only.
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [enabled]
|
||
properties:
|
||
enabled:
|
||
type: boolean
|
||
responses:
|
||
"204":
|
||
description: Auto-start flag updated
|
||
"400":
|
||
description: Invalid request body
|
||
|
||
/api/services/{name}/logs:
|
||
get:
|
||
tags: [Embedded Services]
|
||
summary: Stream service logs via SSE
|
||
description: >-
|
||
Returns a Server-Sent Events stream from the service's in-memory ring buffer
|
||
(5 MB, circular). Sends a `snapshot` event with historical lines first, then
|
||
live `log` events, plus a `heartbeat` every 15 s.
|
||
**LOCAL_ONLY** — loopback only.
|
||
parameters:
|
||
- name: name
|
||
in: path
|
||
required: true
|
||
schema:
|
||
type: string
|
||
enum: [9router, cliproxy]
|
||
- name: tail
|
||
in: query
|
||
schema:
|
||
type: integer
|
||
default: 200
|
||
maximum: 1000
|
||
description: Number of historical lines to include in the initial snapshot
|
||
- name: filter
|
||
in: query
|
||
schema:
|
||
type: string
|
||
maxLength: 200
|
||
description: >-
|
||
Case-insensitive substring filter applied to log lines.
|
||
No regex — ReDoS-safe by design.
|
||
responses:
|
||
"200":
|
||
description: SSE log stream
|
||
content:
|
||
text/event-stream:
|
||
schema:
|
||
type: string
|
||
description: >-
|
||
Events: `snapshot` (LogLine[]), `log` (LogLine), `heartbeat` ({})
|
||
"400":
|
||
description: filter parameter exceeds maximum length
|
||
"404":
|
||
description: Service not found
|
||
|
||
# ─── OAuth ─────────────────────────────────────────────────────
|
||
|
||
/api/oauth/{provider}/{action}:
|
||
get:
|
||
tags: [OAuth]
|
||
summary: OAuth flow handler
|
||
description: Handles OAuth authorization and callback for providers
|
||
parameters:
|
||
- name: provider
|
||
in: path
|
||
required: true
|
||
schema:
|
||
type: string
|
||
- name: action
|
||
in: path
|
||
required: true
|
||
schema:
|
||
type: string
|
||
enum: [authorize, callback, refresh, status]
|
||
responses:
|
||
"200":
|
||
description: OAuth flow response
|
||
"302":
|
||
description: Redirect to provider auth page
|
||
|
||
/api/oauth/cursor/auto-import:
|
||
get:
|
||
tags: [OAuth]
|
||
summary: Auto-import Cursor OAuth credentials
|
||
description: Automatically detects and imports Cursor credentials from local config.
|
||
responses:
|
||
"200":
|
||
description: Import result
|
||
|
||
/api/oauth/cursor/import:
|
||
get:
|
||
tags: [OAuth]
|
||
summary: Get Cursor import status
|
||
responses:
|
||
"200":
|
||
description: Current import status
|
||
post:
|
||
tags: [OAuth]
|
||
summary: Import Cursor OAuth credentials
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
responses:
|
||
"200":
|
||
description: Credentials imported
|
||
|
||
/api/oauth/kiro/auto-import:
|
||
get:
|
||
tags: [OAuth]
|
||
summary: Auto-import Kiro OAuth credentials
|
||
description: Automatically detects and imports Kiro credentials from local config.
|
||
responses:
|
||
"200":
|
||
description: Import result
|
||
|
||
/api/oauth/kiro/import:
|
||
get:
|
||
tags: [OAuth]
|
||
summary: Get Kiro import status
|
||
responses:
|
||
"200":
|
||
description: Current import status
|
||
post:
|
||
tags: [OAuth]
|
||
summary: Import Kiro OAuth credentials
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
responses:
|
||
"200":
|
||
description: Credentials imported
|
||
|
||
/api/oauth/kiro/social-authorize:
|
||
get:
|
||
tags: [OAuth]
|
||
summary: Initiate Kiro social OAuth authorization
|
||
description: Starts the social OAuth flow for Kiro.
|
||
responses:
|
||
"302":
|
||
description: Redirect to OAuth provider
|
||
|
||
/api/oauth/kiro/social-exchange:
|
||
post:
|
||
tags: [OAuth]
|
||
summary: Exchange Kiro social OAuth token
|
||
description: Exchanges the authorization code for access tokens.
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
responses:
|
||
"200":
|
||
description: Token exchange result
|
||
|
||
# ─── Cloud ─────────────────────────────────────────────────────
|
||
|
||
/api/cloud/auth:
|
||
post:
|
||
tags: [Cloud]
|
||
summary: Authenticate with cloud worker
|
||
description: Authenticates with the OmniRoute cloud worker for remote access.
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
responses:
|
||
"200":
|
||
description: Authentication result
|
||
|
||
/api/cloud/credentials/update:
|
||
put:
|
||
tags: [Cloud]
|
||
summary: Update cloud worker credentials
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
responses:
|
||
"200":
|
||
description: Credentials updated
|
||
|
||
/api/cloud/model/resolve:
|
||
post:
|
||
tags: [Cloud]
|
||
summary: Resolve model via cloud
|
||
description: Resolves a model request through the cloud worker.
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
responses:
|
||
"200":
|
||
description: Resolved model info
|
||
|
||
/api/cloud/models/alias:
|
||
get:
|
||
tags: [Cloud]
|
||
summary: Get cloud model aliases
|
||
responses:
|
||
"200":
|
||
description: Cloud model alias list
|
||
put:
|
||
tags: [Cloud]
|
||
summary: Update cloud model alias
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
responses:
|
||
"200":
|
||
description: Alias updated
|
||
|
||
# ─── Fallback ──────────────────────────────────────────────────
|
||
|
||
/api/fallback/chains:
|
||
get:
|
||
tags: [Fallback]
|
||
summary: List fallback chains
|
||
description: Returns all registered fallback chains for model routing.
|
||
responses:
|
||
"200":
|
||
description: Fallback chain list
|
||
post:
|
||
tags: [Fallback]
|
||
summary: Create fallback chain
|
||
description: Registers a fallback routing chain for a model.
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [model, chain]
|
||
properties:
|
||
model:
|
||
type: string
|
||
chain:
|
||
type: array
|
||
items:
|
||
type: object
|
||
properties:
|
||
provider:
|
||
type: string
|
||
priority:
|
||
type: integer
|
||
enabled:
|
||
type: boolean
|
||
responses:
|
||
"200":
|
||
description: Fallback chain created
|
||
delete:
|
||
tags: [Fallback]
|
||
summary: Delete fallback chain
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [model]
|
||
properties:
|
||
model:
|
||
type: string
|
||
responses:
|
||
"200":
|
||
description: Fallback chain deleted
|
||
|
||
# ─── System ────────────────────────────────────────────────────
|
||
|
||
/api/auth/login:
|
||
post:
|
||
tags: [System]
|
||
summary: Authenticate user
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [password]
|
||
properties:
|
||
password:
|
||
type: string
|
||
minLength: 1
|
||
responses:
|
||
"200":
|
||
description: JWT token returned
|
||
"400":
|
||
description: Invalid login request
|
||
"401":
|
||
description: Invalid password
|
||
"403":
|
||
description: Password setup required
|
||
"429":
|
||
description: Too many failed attempts
|
||
|
||
/api/auth/logout:
|
||
post:
|
||
tags: [System]
|
||
summary: Log out
|
||
responses:
|
||
"200":
|
||
description: Session cleared
|
||
|
||
/api/init:
|
||
get:
|
||
tags: [System]
|
||
summary: Initialize application
|
||
responses:
|
||
"200":
|
||
description: Init status
|
||
|
||
/api/restart:
|
||
post:
|
||
tags: [System]
|
||
summary: Restart the application
|
||
responses:
|
||
"200":
|
||
description: Restart initiated
|
||
|
||
/api/shutdown:
|
||
post:
|
||
tags: [System]
|
||
summary: Shutdown the application
|
||
x-always-protected: true
|
||
responses:
|
||
"200":
|
||
description: Shutdown initiated
|
||
|
||
/api/db-backups:
|
||
get:
|
||
tags: [System]
|
||
summary: List database backups
|
||
responses:
|
||
"200":
|
||
description: Backup list
|
||
post:
|
||
tags: [System]
|
||
summary: Create database backup
|
||
responses:
|
||
"200":
|
||
description: Backup created
|
||
patch:
|
||
tags: [System]
|
||
summary: Save database backup retention settings
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
keepLatest:
|
||
type: integer
|
||
minimum: 1
|
||
maximum: 200
|
||
retentionDays:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 3650
|
||
responses:
|
||
"200":
|
||
description: Backup retention settings saved
|
||
|
||
/api/storage/health:
|
||
get:
|
||
tags: [System]
|
||
summary: Check storage health
|
||
responses:
|
||
"200":
|
||
description: Storage health status
|
||
|
||
/api/sync/cloud:
|
||
post:
|
||
tags: [System]
|
||
summary: Sync with cloud
|
||
responses:
|
||
"200":
|
||
description: Sync result
|
||
|
||
/api/sync/initialize:
|
||
post:
|
||
tags: [System]
|
||
summary: Initialize cloud sync
|
||
responses:
|
||
"200":
|
||
description: Sync initialized
|
||
|
||
# ─── Resilience & Monitoring ────────────────────────────────────
|
||
|
||
/api/resilience:
|
||
get:
|
||
tags: [System]
|
||
summary: Get resilience configuration
|
||
responses:
|
||
"200":
|
||
description: Request queue, connection cooldown, provider breaker, and wait settings
|
||
patch:
|
||
tags: [System]
|
||
summary: Update resilience configuration
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
responses:
|
||
"200":
|
||
description: Updated resilience configuration
|
||
|
||
/api/resilience/reset:
|
||
post:
|
||
tags: [System]
|
||
summary: Reset circuit breakers
|
||
responses:
|
||
"200":
|
||
description: Circuit breakers reset
|
||
|
||
/api/monitoring/health:
|
||
get:
|
||
tags: [System]
|
||
summary: System health check
|
||
description: Returns system health including uptime, memory, circuit breakers, rate limits
|
||
responses:
|
||
"200":
|
||
description: Health status
|
||
|
||
/api/rate-limits:
|
||
get:
|
||
tags: [System]
|
||
summary: Get per-account rate limit status
|
||
responses:
|
||
"200":
|
||
description: Rate limit status by account
|
||
|
||
/api/sessions:
|
||
get:
|
||
tags: [System]
|
||
summary: Get active sessions
|
||
responses:
|
||
"200":
|
||
description: Active session list
|
||
|
||
/api/cache:
|
||
get:
|
||
tags: [System]
|
||
summary: Get cache statistics
|
||
responses:
|
||
"200":
|
||
description: Semantic cache and idempotency stats
|
||
delete:
|
||
tags: [System]
|
||
summary: Clear all caches
|
||
responses:
|
||
"200":
|
||
description: Caches cleared
|
||
|
||
/api/cache/stats:
|
||
get:
|
||
tags: [System]
|
||
summary: Get detailed cache statistics
|
||
description: Returns detailed statistics for all cache layers.
|
||
responses:
|
||
"200":
|
||
description: Detailed cache stats
|
||
delete:
|
||
tags: [System]
|
||
summary: Clear cache statistics
|
||
responses:
|
||
"200":
|
||
description: Cache stats cleared
|
||
|
||
# ─── Telemetry & Token Health ───────────────────────────────────
|
||
|
||
/api/telemetry/summary:
|
||
get:
|
||
tags: [Telemetry]
|
||
summary: Get telemetry summary
|
||
description: Returns aggregated telemetry data including request metrics and performance stats.
|
||
responses:
|
||
"200":
|
||
description: Telemetry summary data
|
||
|
||
/api/token-health:
|
||
get:
|
||
tags: [Telemetry]
|
||
summary: Get token health status
|
||
description: Returns health status of OAuth tokens across all providers.
|
||
responses:
|
||
"200":
|
||
description: Token health status
|
||
|
||
# ─── Evals & Policies ──────────────────────────────────────────
|
||
|
||
/api/evals:
|
||
get:
|
||
tags: [System]
|
||
summary: List eval suites
|
||
responses:
|
||
"200":
|
||
description: Eval suite list
|
||
post:
|
||
tags: [System]
|
||
summary: Run evaluation
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
responses:
|
||
"200":
|
||
description: Eval results
|
||
|
||
/api/evals/{suiteId}:
|
||
get:
|
||
tags: [System]
|
||
summary: Get eval suite details
|
||
parameters:
|
||
- name: suiteId
|
||
in: path
|
||
required: true
|
||
schema:
|
||
type: string
|
||
responses:
|
||
"200":
|
||
description: Eval suite details
|
||
|
||
/api/policies:
|
||
get:
|
||
tags: [System]
|
||
summary: List routing policies
|
||
responses:
|
||
"200":
|
||
description: Policy list
|
||
post:
|
||
tags: [System]
|
||
summary: Create routing policy
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
responses:
|
||
"201":
|
||
description: Created policy
|
||
delete:
|
||
tags: [System]
|
||
summary: Delete routing policy
|
||
responses:
|
||
"200":
|
||
description: Policy deleted
|
||
|
||
/api/compliance/audit-log:
|
||
get:
|
||
tags: [System]
|
||
summary: Get compliance audit log
|
||
description: >
|
||
Returns paginated audit log entries. Use `level=high` to filter to
|
||
high-level actions only (powers the Activity feed). Use `level=all`
|
||
(default) for full compliance table.
|
||
security:
|
||
- bearerAuth: []
|
||
parameters:
|
||
- name: level
|
||
in: query
|
||
schema:
|
||
type: string
|
||
enum: [high, all]
|
||
default: all
|
||
description: "high = Activity feed events only; all = all audit events"
|
||
- name: action
|
||
in: query
|
||
schema:
|
||
type: string
|
||
description: Filter by exact action string (e.g. "provider.added")
|
||
- name: actor
|
||
in: query
|
||
schema:
|
||
type: string
|
||
description: Filter by actor identifier
|
||
- name: limit
|
||
in: query
|
||
schema:
|
||
type: integer
|
||
default: 50
|
||
maximum: 500
|
||
- name: offset
|
||
in: query
|
||
schema:
|
||
type: integer
|
||
default: 0
|
||
responses:
|
||
"200":
|
||
description: Audit log entries
|
||
"401":
|
||
description: Unauthorized
|
||
"500":
|
||
description: Internal server error
|
||
|
||
# ─── Quota Sharing (Group B, plan 22) ────────────────────────────
|
||
|
||
/api/quota/pools:
|
||
get:
|
||
tags: [Quota]
|
||
summary: List quota pools
|
||
security:
|
||
- bearerAuth: []
|
||
responses:
|
||
"200":
|
||
description: Array of QuotaPool objects
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/QuotaPool"
|
||
"401":
|
||
description: Unauthorized
|
||
"500":
|
||
description: Internal server error
|
||
post:
|
||
tags: [Quota]
|
||
summary: Create quota pool
|
||
security:
|
||
- bearerAuth: []
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/PoolCreate"
|
||
responses:
|
||
"201":
|
||
description: Pool created
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/QuotaPool"
|
||
"400":
|
||
description: Validation error (Zod)
|
||
"401":
|
||
description: Unauthorized
|
||
"500":
|
||
description: Internal server error
|
||
|
||
/api/quota/pools/{id}:
|
||
get:
|
||
tags: [Quota]
|
||
summary: Get quota pool by ID
|
||
security:
|
||
- bearerAuth: []
|
||
parameters:
|
||
- name: id
|
||
in: path
|
||
required: true
|
||
schema:
|
||
type: string
|
||
responses:
|
||
"200":
|
||
description: QuotaPool object
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/QuotaPool"
|
||
"401":
|
||
description: Unauthorized
|
||
"404":
|
||
description: Pool not found
|
||
"500":
|
||
description: Internal server error
|
||
patch:
|
||
tags: [Quota]
|
||
summary: Update quota pool (name or allocations)
|
||
security:
|
||
- bearerAuth: []
|
||
parameters:
|
||
- name: id
|
||
in: path
|
||
required: true
|
||
schema:
|
||
type: string
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/PoolUpdate"
|
||
responses:
|
||
"200":
|
||
description: Updated pool
|
||
"400":
|
||
description: Validation error
|
||
"401":
|
||
description: Unauthorized
|
||
"404":
|
||
description: Pool not found
|
||
"500":
|
||
description: Internal server error
|
||
delete:
|
||
tags: [Quota]
|
||
summary: Delete quota pool
|
||
security:
|
||
- bearerAuth: []
|
||
parameters:
|
||
- name: id
|
||
in: path
|
||
required: true
|
||
schema:
|
||
type: string
|
||
responses:
|
||
"204":
|
||
description: Deleted
|
||
"401":
|
||
description: Unauthorized
|
||
"404":
|
||
description: Pool not found
|
||
"500":
|
||
description: Internal server error
|
||
|
||
/api/quota/pools/{id}/usage:
|
||
get:
|
||
tags: [Quota]
|
||
summary: Get pool usage snapshot (per-key consumption + burn rate)
|
||
security:
|
||
- bearerAuth: []
|
||
parameters:
|
||
- name: id
|
||
in: path
|
||
required: true
|
||
schema:
|
||
type: string
|
||
responses:
|
||
"200":
|
||
description: PoolUsageSnapshot
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/PoolUsageSnapshot"
|
||
"401":
|
||
description: Unauthorized
|
||
"404":
|
||
description: Pool not found
|
||
"500":
|
||
description: Internal server error
|
||
|
||
/api/quota/plans:
|
||
get:
|
||
tags: [Quota]
|
||
summary: List resolved provider plans (catalog + manual overrides)
|
||
security:
|
||
- bearerAuth: []
|
||
responses:
|
||
"200":
|
||
description: Array of ProviderPlan
|
||
"401":
|
||
description: Unauthorized
|
||
"500":
|
||
description: Internal server error
|
||
|
||
/api/quota/plans/{connectionId}:
|
||
get:
|
||
tags: [Quota]
|
||
summary: Get resolved plan for a connection
|
||
security:
|
||
- bearerAuth: []
|
||
parameters:
|
||
- name: connectionId
|
||
in: path
|
||
required: true
|
||
schema:
|
||
type: string
|
||
responses:
|
||
"200":
|
||
description: ProviderPlan (source = auto | manual)
|
||
"401":
|
||
description: Unauthorized
|
||
"404":
|
||
description: Connection not found
|
||
"500":
|
||
description: Internal server error
|
||
put:
|
||
tags: [Quota]
|
||
summary: Upsert manual plan override for a connection
|
||
security:
|
||
- bearerAuth: []
|
||
parameters:
|
||
- name: connectionId
|
||
in: path
|
||
required: true
|
||
schema:
|
||
type: string
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/PlanUpsert"
|
||
responses:
|
||
"200":
|
||
description: Updated plan
|
||
"400":
|
||
description: Validation error (Zod)
|
||
"401":
|
||
description: Unauthorized
|
||
"500":
|
||
description: Internal server error
|
||
delete:
|
||
tags: [Quota]
|
||
summary: Delete manual plan override (reverts to catalog/auto)
|
||
security:
|
||
- bearerAuth: []
|
||
parameters:
|
||
- name: connectionId
|
||
in: path
|
||
required: true
|
||
schema:
|
||
type: string
|
||
responses:
|
||
"204":
|
||
description: Override deleted
|
||
"401":
|
||
description: Unauthorized
|
||
"404":
|
||
description: Override not found
|
||
"500":
|
||
description: Internal server error
|
||
|
||
/api/quota/preview:
|
||
get:
|
||
tags: [Quota]
|
||
summary: Dry-run quota enforcement check (preview only, no consumption recorded)
|
||
security:
|
||
- bearerAuth: []
|
||
parameters:
|
||
- name: apiKeyId
|
||
in: query
|
||
required: true
|
||
schema:
|
||
type: string
|
||
- name: poolId
|
||
in: query
|
||
required: true
|
||
schema:
|
||
type: string
|
||
- name: estimatedTokens
|
||
in: query
|
||
schema:
|
||
type: number
|
||
- name: estimatedUsd
|
||
in: query
|
||
schema:
|
||
type: number
|
||
- name: estimatedRequests
|
||
in: query
|
||
schema:
|
||
type: integer
|
||
responses:
|
||
"200":
|
||
description: EnforceDecision (allow/block + reason)
|
||
"400":
|
||
description: Validation error (Zod)
|
||
"401":
|
||
description: Unauthorized
|
||
"500":
|
||
description: Internal server error
|
||
|
||
/api/settings/quota-store:
|
||
get:
|
||
tags: [Settings]
|
||
summary: Get current quota store driver settings
|
||
description: Redis URL is masked in the response (shows only scheme+host).
|
||
security:
|
||
- bearerAuth: []
|
||
responses:
|
||
"200":
|
||
description: QuotaStoreSettings (driver + masked redisUrl)
|
||
"401":
|
||
description: Unauthorized
|
||
"500":
|
||
description: Internal server error
|
||
put:
|
||
tags: [Settings]
|
||
summary: Update quota store driver settings
|
||
security:
|
||
- bearerAuth: []
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/QuotaStoreSettings"
|
||
responses:
|
||
"200":
|
||
description: Settings updated
|
||
"400":
|
||
description: Validation error (Zod) — e.g. driver=redis without valid URL
|
||
"401":
|
||
description: Unauthorized
|
||
"500":
|
||
description: Internal server error
|
||
|
||
# ─── v1beta (Gemini-Compatible) ─────────────────────────────────
|
||
|
||
/api/v1beta/models:
|
||
get:
|
||
tags: [Models]
|
||
summary: List models (Gemini format)
|
||
description: Returns models in Gemini v1beta format for native SDK compatibility
|
||
security:
|
||
- BearerAuth: []
|
||
responses:
|
||
"200":
|
||
description: Model list in Gemini format
|
||
|
||
/api/v1beta/models/{path}:
|
||
post:
|
||
tags: [Models]
|
||
summary: Gemini generateContent
|
||
description: Gemini-compatible generateContent endpoint
|
||
security:
|
||
- BearerAuth: []
|
||
parameters:
|
||
- name: path
|
||
in: path
|
||
required: true
|
||
schema:
|
||
type: string
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
responses:
|
||
"200":
|
||
description: Generated content
|
||
|
||
# ─── AgentBridge ──────────────────────────────────────────────
|
||
|
||
/api/tools/agent-bridge/agents:
|
||
get:
|
||
tags: [AgentBridge]
|
||
summary: List all 9 IDE agents with current state
|
||
description: >-
|
||
Returns the state (dns_enabled, cert_trusted, setup_completed, last_started_at,
|
||
last_error) for all 9 configured IDE agents. LOCAL_ONLY.
|
||
responses:
|
||
"200":
|
||
description: Array of agent state rows
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/AgentBridgeAgentState"
|
||
"403":
|
||
description: Loopback-only — request came from a non-loopback address
|
||
|
||
/api/tools/agent-bridge/state:
|
||
get:
|
||
tags: [AgentBridge]
|
||
summary: Get global AgentBridge server state
|
||
description: Returns running status, port, cert info, and intercepted request count.
|
||
responses:
|
||
"200":
|
||
description: Server state
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/AgentBridgeServerState"
|
||
|
||
/api/tools/agent-bridge/server:
|
||
post:
|
||
tags: [AgentBridge]
|
||
summary: Control AgentBridge MITM server
|
||
description: Start, stop, restart, trust-cert, or regenerate-cert. SPAWN_CAPABLE.
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/AgentBridgeServerAction"
|
||
responses:
|
||
"200":
|
||
description: Action executed
|
||
"400":
|
||
description: Invalid action
|
||
"409":
|
||
description: Port 443 conflict
|
||
|
||
/api/tools/agent-bridge/agents/{agentId}/dns:
|
||
post:
|
||
tags: [AgentBridge]
|
||
summary: Enable or disable DNS for one agent
|
||
description: Adds or removes /etc/hosts entries for the agent's host list. SPAWN_CAPABLE.
|
||
parameters:
|
||
- name: agentId
|
||
in: path
|
||
required: true
|
||
schema:
|
||
$ref: "#/components/schemas/AgentId"
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/AgentBridgeDnsAction"
|
||
responses:
|
||
"200":
|
||
description: DNS updated
|
||
"400":
|
||
description: Validation error
|
||
|
||
/api/tools/agent-bridge/agents/{agentId}/mappings:
|
||
get:
|
||
tags: [AgentBridge]
|
||
summary: Get model mappings for one agent
|
||
parameters:
|
||
- name: agentId
|
||
in: path
|
||
required: true
|
||
schema:
|
||
$ref: "#/components/schemas/AgentId"
|
||
responses:
|
||
"200":
|
||
description: Array of source→target model mappings
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/AgentBridgeMappingRow"
|
||
put:
|
||
tags: [AgentBridge]
|
||
summary: Update model mappings for one agent
|
||
parameters:
|
||
- name: agentId
|
||
in: path
|
||
required: true
|
||
schema:
|
||
$ref: "#/components/schemas/AgentId"
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/AgentBridgeMappingPut"
|
||
responses:
|
||
"200":
|
||
description: Mappings updated
|
||
|
||
/api/tools/agent-bridge/bypass:
|
||
get:
|
||
tags: [AgentBridge]
|
||
summary: List bypass patterns (hosts never decrypted)
|
||
responses:
|
||
"200":
|
||
description: Bypass patterns
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/AgentBridgeBypassRow"
|
||
put:
|
||
tags: [AgentBridge]
|
||
summary: Update user bypass patterns
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/AgentBridgeBypassUpsert"
|
||
responses:
|
||
"200":
|
||
description: Patterns updated
|
||
|
||
/api/tools/agent-bridge/cert:
|
||
post:
|
||
tags: [AgentBridge]
|
||
summary: Download or regenerate the AgentBridge CA certificate
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [action]
|
||
properties:
|
||
action:
|
||
type: string
|
||
enum: [download, regenerate]
|
||
responses:
|
||
"200":
|
||
description: CA certificate PEM (download) or regeneration confirmation
|
||
|
||
/api/tools/agent-bridge/upstream-ca:
|
||
get:
|
||
tags: [AgentBridge]
|
||
summary: Get configured upstream CA cert path
|
||
responses:
|
||
"200":
|
||
description: Upstream CA configuration
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
path:
|
||
type: string
|
||
nullable: true
|
||
post:
|
||
tags: [AgentBridge]
|
||
summary: Set upstream CA cert path for corporate TLS environments
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/AgentBridgeUpstreamCaPost"
|
||
responses:
|
||
"200":
|
||
description: Upstream CA configured
|
||
"400":
|
||
description: Path does not exist or is not readable
|
||
|
||
# ─── Traffic Inspector ─────────────────────────────────────────
|
||
|
||
/api/tools/traffic-inspector/requests:
|
||
get:
|
||
tags: [Traffic Inspector]
|
||
summary: List intercepted requests (filterable)
|
||
parameters:
|
||
- name: profile
|
||
in: query
|
||
schema:
|
||
type: string
|
||
enum: [llm, custom, all]
|
||
- name: host
|
||
in: query
|
||
schema:
|
||
type: string
|
||
- name: agent
|
||
in: query
|
||
schema:
|
||
$ref: "#/components/schemas/AgentId"
|
||
- name: status
|
||
in: query
|
||
schema:
|
||
type: string
|
||
enum: ["2xx", "3xx", "4xx", "5xx", error]
|
||
- name: source
|
||
in: query
|
||
schema:
|
||
$ref: "#/components/schemas/CaptureSource"
|
||
- name: sessionId
|
||
in: query
|
||
schema:
|
||
type: string
|
||
format: uuid
|
||
responses:
|
||
"200":
|
||
description: Array of intercepted requests
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/InterceptedRequest"
|
||
delete:
|
||
tags: [Traffic Inspector]
|
||
summary: Clear the in-memory traffic buffer
|
||
responses:
|
||
"204":
|
||
description: Buffer cleared
|
||
|
||
/api/tools/traffic-inspector/requests/{id}:
|
||
get:
|
||
tags: [Traffic Inspector]
|
||
summary: Get a single intercepted request by ID
|
||
parameters:
|
||
- name: id
|
||
in: path
|
||
required: true
|
||
schema:
|
||
type: string
|
||
format: uuid
|
||
responses:
|
||
"200":
|
||
description: Intercepted request details
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/InterceptedRequest"
|
||
"404":
|
||
description: Request not found in buffer
|
||
|
||
/api/tools/traffic-inspector/requests/{id}/replay:
|
||
post:
|
||
tags: [Traffic Inspector]
|
||
summary: Replay a captured request through OmniRoute router
|
||
description: Re-executes the original request body against /v1/chat/completions. Consumes quota.
|
||
parameters:
|
||
- name: id
|
||
in: path
|
||
required: true
|
||
schema:
|
||
type: string
|
||
format: uuid
|
||
responses:
|
||
"200":
|
||
description: Replay response (streaming or JSON)
|
||
"404":
|
||
description: Request not found
|
||
|
||
/api/tools/traffic-inspector/requests/{id}/annotation:
|
||
put:
|
||
tags: [Traffic Inspector]
|
||
summary: Save or update annotation on a request
|
||
parameters:
|
||
- name: id
|
||
in: path
|
||
required: true
|
||
schema:
|
||
type: string
|
||
format: uuid
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/InspectorAnnotationPut"
|
||
responses:
|
||
"200":
|
||
description: Annotation saved
|
||
|
||
/api/tools/traffic-inspector/ws:
|
||
get:
|
||
tags: [Traffic Inspector]
|
||
summary: Live WebSocket stream of intercepted requests
|
||
description: >-
|
||
Upgrade to WebSocket. On connect, server sends `{type:"snapshot", data:[...]}`.
|
||
Subsequent events: `{type:"new", data:{...}}`, `{type:"update", data:{...}}`,
|
||
`{type:"clear"}`. LOCAL_ONLY.
|
||
responses:
|
||
"101":
|
||
description: WebSocket upgrade successful
|
||
"403":
|
||
description: Non-loopback origin rejected
|
||
|
||
/api/tools/traffic-inspector/export.har:
|
||
get:
|
||
tags: [Traffic Inspector]
|
||
summary: Export current filtered request list as HAR 1.2
|
||
parameters:
|
||
- name: profile
|
||
in: query
|
||
schema:
|
||
type: string
|
||
enum: [llm, custom, all]
|
||
- name: sessionId
|
||
in: query
|
||
schema:
|
||
type: string
|
||
format: uuid
|
||
responses:
|
||
"200":
|
||
description: HAR file (JSON)
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
description: HAR 1.2 format
|
||
|
||
/api/tools/traffic-inspector/hosts:
|
||
get:
|
||
tags: [Traffic Inspector]
|
||
summary: List custom capture hosts
|
||
responses:
|
||
"200":
|
||
description: Custom hosts list
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/InspectorCustomHost"
|
||
post:
|
||
tags: [Traffic Inspector]
|
||
summary: Add a custom capture host (edits /etc/hosts)
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/InspectorCustomHostCreate"
|
||
responses:
|
||
"201":
|
||
description: Host added
|
||
"409":
|
||
description: Host already exists
|
||
|
||
/api/tools/traffic-inspector/hosts/{host}:
|
||
delete:
|
||
tags: [Traffic Inspector]
|
||
summary: Remove a custom capture host
|
||
parameters:
|
||
- name: host
|
||
in: path
|
||
required: true
|
||
schema:
|
||
type: string
|
||
responses:
|
||
"204":
|
||
description: Host removed
|
||
patch:
|
||
tags: [Traffic Inspector]
|
||
summary: Toggle enabled state of a custom host
|
||
parameters:
|
||
- name: host
|
||
in: path
|
||
required: true
|
||
schema:
|
||
type: string
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [enabled]
|
||
properties:
|
||
enabled:
|
||
type: boolean
|
||
responses:
|
||
"200":
|
||
description: Host updated
|
||
|
||
/api/tools/traffic-inspector/capture-modes:
|
||
get:
|
||
tags: [Traffic Inspector]
|
||
summary: Get state of all 4 capture modes
|
||
responses:
|
||
"200":
|
||
description: Capture modes state
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/InspectorCaptureModesState"
|
||
|
||
/api/tools/traffic-inspector/capture-modes/http-proxy:
|
||
post:
|
||
tags: [Traffic Inspector]
|
||
summary: Start or stop the HTTP_PROXY listener (port 8080)
|
||
description: SPAWN_CAPABLE — spawns a net.Server listener.
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/InspectorCaptureModeAction"
|
||
responses:
|
||
"200":
|
||
description: Action executed
|
||
"409":
|
||
description: Port conflict (EADDRINUSE) when starting
|
||
|
||
/api/tools/traffic-inspector/capture-modes/system-proxy:
|
||
post:
|
||
tags: [Traffic Inspector]
|
||
summary: Apply or revert system-wide proxy settings
|
||
description: SPAWN_CAPABLE — executes networksetup/gsettings/netsh. Requires admin.
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/InspectorSystemProxyAction"
|
||
responses:
|
||
"200":
|
||
description: System proxy updated
|
||
"500":
|
||
description: OS command failed (permission error)
|
||
|
||
/api/tools/traffic-inspector/capture-modes/tls-intercept:
|
||
post:
|
||
tags: [Traffic Inspector]
|
||
summary: Toggle TLS body decryption in HTTP_PROXY mode
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/InspectorTlsInterceptToggle"
|
||
responses:
|
||
"200":
|
||
description: TLS intercept mode updated
|
||
|
||
/api/tools/traffic-inspector/sessions:
|
||
get:
|
||
tags: [Traffic Inspector]
|
||
summary: List all saved recording sessions
|
||
responses:
|
||
"200":
|
||
description: Sessions list
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/InspectorSession"
|
||
post:
|
||
tags: [Traffic Inspector]
|
||
summary: Start a new recording session
|
||
requestBody:
|
||
required: false
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/InspectorSessionStart"
|
||
responses:
|
||
"201":
|
||
description: Session started
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/InspectorSession"
|
||
|
||
/api/tools/traffic-inspector/sessions/{id}:
|
||
get:
|
||
tags: [Traffic Inspector]
|
||
summary: Get session snapshot (all captured requests)
|
||
parameters:
|
||
- name: id
|
||
in: path
|
||
required: true
|
||
schema:
|
||
type: string
|
||
format: uuid
|
||
responses:
|
||
"200":
|
||
description: Session with embedded requests
|
||
"404":
|
||
description: Session not found
|
||
patch:
|
||
tags: [Traffic Inspector]
|
||
summary: Stop or rename a recording session
|
||
parameters:
|
||
- name: id
|
||
in: path
|
||
required: true
|
||
schema:
|
||
type: string
|
||
format: uuid
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/InspectorSessionPatch"
|
||
responses:
|
||
"200":
|
||
description: Session updated
|
||
delete:
|
||
tags: [Traffic Inspector]
|
||
summary: Delete a recording session
|
||
parameters:
|
||
- name: id
|
||
in: path
|
||
required: true
|
||
schema:
|
||
type: string
|
||
format: uuid
|
||
responses:
|
||
"204":
|
||
description: Session deleted
|
||
|
||
/api/tools/traffic-inspector/sessions/{id}/export.har:
|
||
get:
|
||
tags: [Traffic Inspector]
|
||
summary: Export a recorded session as HAR 1.2
|
||
parameters:
|
||
- name: id
|
||
in: path
|
||
required: true
|
||
schema:
|
||
type: string
|
||
format: uuid
|
||
responses:
|
||
"200":
|
||
description: HAR file for this session
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
description: HAR 1.2 format
|
||
"404":
|
||
description: Session not found
|
||
|
||
/api/tools/traffic-inspector/internal/ingest:
|
||
post:
|
||
tags: [Traffic Inspector]
|
||
summary: Internal ingest endpoint for server.cjs passthrough path
|
||
description: >-
|
||
Accepts a serialized InterceptedRequest from the CJS MITM server for requests
|
||
that do not go through TypeScript handlers (e.g., passthrough hosts). Requires
|
||
INSPECTOR_INTERNAL_INGEST_TOKEN header. LOCAL_ONLY.
|
||
security: []
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/InterceptedRequest"
|
||
responses:
|
||
"204":
|
||
description: Ingested
|
||
"401":
|
||
description: Invalid or missing ingest token
|
||
|
||
# ─── OpenAPI Spec ──────────────────────────────────────────────
|
||
|
||
/api/openapi/spec:
|
||
get:
|
||
tags: [System]
|
||
summary: Get OpenAPI specification catalog
|
||
description: >-
|
||
Returns a structured JSON catalog parsed from this `openapi.yaml`,
|
||
including info, servers, tags, schemas, and a flat list of endpoints
|
||
(method, path, tags, summary, security, parameters, responses).
|
||
Used by the in-app API explorer.
|
||
responses:
|
||
"200":
|
||
description: Parsed OpenAPI catalog
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
info:
|
||
type: object
|
||
servers:
|
||
type: array
|
||
items:
|
||
type: object
|
||
tags:
|
||
type: array
|
||
items:
|
||
type: object
|
||
endpoints:
|
||
type: array
|
||
items:
|
||
type: object
|
||
properties:
|
||
method:
|
||
type: string
|
||
path:
|
||
type: string
|
||
tags:
|
||
type: array
|
||
items:
|
||
type: string
|
||
summary:
|
||
type: string
|
||
description:
|
||
type: string
|
||
security:
|
||
type: boolean
|
||
parameters:
|
||
type: array
|
||
items:
|
||
type: object
|
||
requestBody:
|
||
type: boolean
|
||
responses:
|
||
type: array
|
||
items:
|
||
type: string
|
||
schemas:
|
||
type: array
|
||
items:
|
||
type: string
|
||
"404":
|
||
description: openapi.yaml file not found on disk
|
||
"500":
|
||
description: Failed to parse OpenAPI spec
|
||
|
||
# ─── Agent Skills Catalog ────────────────────────────────────────────────────
|
||
|
||
/api/agent-skills:
|
||
get:
|
||
tags: [Agent Skills]
|
||
summary: List agent skills catalog
|
||
description: |
|
||
Returns the full 42-entry Agent Skills catalog with optional filtering.
|
||
Skills describe how to use OmniRoute's REST API and CLI — they are structured
|
||
SKILL.md documentation files discoverable by external agents, MCP clients, and
|
||
A2A orchestrators. No authentication required.
|
||
parameters:
|
||
- name: category
|
||
in: query
|
||
required: false
|
||
schema:
|
||
type: string
|
||
enum: [api, cli]
|
||
description: Filter by category (api = REST API skills, cli = CLI skills)
|
||
- name: area
|
||
in: query
|
||
required: false
|
||
schema:
|
||
type: string
|
||
description: Filter by area slug (e.g. "providers", "models", "cli-serve")
|
||
responses:
|
||
"200":
|
||
description: Catalog list
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [skills, count, coverage]
|
||
properties:
|
||
skills:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/AgentSkill"
|
||
count:
|
||
type: integer
|
||
coverage:
|
||
$ref: "#/components/schemas/SkillCoverage"
|
||
"400":
|
||
$ref: "#/components/responses/BadRequest"
|
||
"500":
|
||
$ref: "#/components/responses/InternalError"
|
||
|
||
/api/agent-skills/{id}:
|
||
get:
|
||
tags: [Agent Skills]
|
||
summary: Get a single agent skill
|
||
description: |
|
||
Returns metadata for a single agent skill by its canonical ID
|
||
(e.g. `omni-providers`, `cli-serve`). No authentication required.
|
||
parameters:
|
||
- name: id
|
||
in: path
|
||
required: true
|
||
schema:
|
||
type: string
|
||
pattern: "^[a-z][a-z0-9-]*$"
|
||
description: Canonical skill ID
|
||
example: omni-providers
|
||
responses:
|
||
"200":
|
||
description: Agent skill metadata
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/AgentSkill"
|
||
"400":
|
||
$ref: "#/components/responses/BadRequest"
|
||
"404":
|
||
$ref: "#/components/responses/NotFound"
|
||
"500":
|
||
$ref: "#/components/responses/InternalError"
|
||
|
||
/api/agent-skills/{id}/raw:
|
||
get:
|
||
tags: [Agent Skills]
|
||
summary: Get raw SKILL.md content
|
||
description: |
|
||
Returns the SKILL.md content for a skill as `text/markdown`.
|
||
Resolution order: local filesystem `skills/{id}/SKILL.md` → GitHub raw URL (1-hour cache).
|
||
No authentication required.
|
||
parameters:
|
||
- name: id
|
||
in: path
|
||
required: true
|
||
schema:
|
||
type: string
|
||
pattern: "^[a-z][a-z0-9-]*$"
|
||
description: Canonical skill ID
|
||
example: omni-providers
|
||
responses:
|
||
"200":
|
||
description: SKILL.md content as Markdown
|
||
headers:
|
||
X-Skill-Source:
|
||
schema:
|
||
type: string
|
||
enum: [filesystem, github, generated]
|
||
description: Where the content was loaded from
|
||
X-Skill-Fetched-At:
|
||
schema:
|
||
type: string
|
||
format: date-time
|
||
description: ISO timestamp of when the content was fetched
|
||
Cache-Control:
|
||
schema:
|
||
type: string
|
||
description: "public, max-age=3600"
|
||
content:
|
||
text/markdown:
|
||
schema:
|
||
type: string
|
||
"400":
|
||
$ref: "#/components/responses/BadRequest"
|
||
"404":
|
||
$ref: "#/components/responses/NotFound"
|
||
"502":
|
||
description: Upstream GitHub fetch failed
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ErrorResponse"
|
||
"500":
|
||
$ref: "#/components/responses/InternalError"
|
||
|
||
/api/agent-skills/coverage:
|
||
get:
|
||
tags: [Agent Skills]
|
||
summary: Get SKILL.md coverage stats
|
||
description: |
|
||
Returns how many of the 22 API skills and 20 CLI skills have SKILL.md
|
||
files on the local filesystem vs the catalog totals. No authentication required.
|
||
responses:
|
||
"200":
|
||
description: Coverage stats
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/SkillCoverage"
|
||
"500":
|
||
$ref: "#/components/responses/InternalError"
|
||
|
||
/api/agent-skills/generate:
|
||
post:
|
||
tags: [Agent Skills]
|
||
summary: Trigger SKILL.md generator
|
||
description: |
|
||
Runs the Agent Skills generator which writes `skills/{id}/SKILL.md` for
|
||
all 42 catalog entries (or a subset via `onlyIds`). Preserves
|
||
`<!-- skill:custom-start --> ... <!-- skill:custom-end -->` blocks.
|
||
**Requires management authentication.**
|
||
security:
|
||
- BearerAuth: []
|
||
- ManagementSessionAuth: []
|
||
requestBody:
|
||
required: false
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
dryRun:
|
||
type: boolean
|
||
default: true
|
||
description: "If true, reports what would be generated without writing files"
|
||
prune:
|
||
type: boolean
|
||
default: false
|
||
description: "If true, deletes skill directories not in the catalog"
|
||
onlyIds:
|
||
type: array
|
||
items:
|
||
type: string
|
||
description: "If provided, only regenerate these skill IDs"
|
||
responses:
|
||
"200":
|
||
description: Generator report
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [generated, unchanged, pruned, orphansDetected, errors]
|
||
properties:
|
||
generated:
|
||
type: array
|
||
items:
|
||
type: string
|
||
description: IDs that got new/updated SKILL.md
|
||
unchanged:
|
||
type: array
|
||
items:
|
||
type: string
|
||
description: IDs whose content was already up to date
|
||
pruned:
|
||
type: array
|
||
items:
|
||
type: string
|
||
description: IDs whose directories were deleted (prune mode)
|
||
orphansDetected:
|
||
type: array
|
||
items:
|
||
type: string
|
||
description: Directories found in skills/ not in the catalog
|
||
errors:
|
||
type: array
|
||
items:
|
||
type: object
|
||
required: [id, error]
|
||
properties:
|
||
id:
|
||
type: string
|
||
error:
|
||
type: string
|
||
"400":
|
||
$ref: "#/components/responses/BadRequest"
|
||
"401":
|
||
$ref: "#/components/responses/Unauthorized"
|
||
"500":
|
||
$ref: "#/components/responses/InternalError"
|
||
"503":
|
||
description: Generator module not available
|
||
/api/v1/ocr:
|
||
post:
|
||
tags:
|
||
- Images
|
||
summary: Document OCR
|
||
description: >-
|
||
Mistral OCR–compatible document OCR endpoint. Accepts a JSON body
|
||
referencing a document/image and returns extracted text. Success
|
||
responses carry the `X-OmniRoute-*` cost-telemetry headers.
|
||
security:
|
||
- BearerAuth: []
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
model:
|
||
type: string
|
||
document:
|
||
type: object
|
||
responses:
|
||
"200":
|
||
description: OCR result with extracted text.
|
||
"400":
|
||
$ref: "#/components/responses/BadRequest"
|
||
"401":
|
||
$ref: "#/components/responses/Unauthorized"
|
||
"500":
|
||
$ref: "#/components/responses/InternalError"
|
||
/api/v1/audio/translations:
|
||
post:
|
||
tags:
|
||
- Audio
|
||
summary: Translate audio to English
|
||
description: >-
|
||
OpenAI Whisper–compatible audio translation (multipart/form-data).
|
||
Unlike `/api/v1/audio/transcriptions`, output is always English
|
||
regardless of the source language. Success responses carry the
|
||
`X-OmniRoute-*` cost-telemetry headers.
|
||
security:
|
||
- BearerAuth: []
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
multipart/form-data:
|
||
schema:
|
||
type: object
|
||
required:
|
||
- file
|
||
properties:
|
||
file:
|
||
type: string
|
||
format: binary
|
||
model:
|
||
type: string
|
||
responses:
|
||
"200":
|
||
description: English translation of the audio.
|
||
"400":
|
||
$ref: "#/components/responses/BadRequest"
|
||
"401":
|
||
$ref: "#/components/responses/Unauthorized"
|
||
"500":
|
||
$ref: "#/components/responses/InternalError"
|
||
/api/v1/providers/suggested-models:
|
||
get:
|
||
tags:
|
||
- Providers
|
||
summary: Suggested media models
|
||
description: >-
|
||
Read-only server-side proxy to the public HuggingFace Hub models search
|
||
API, used by the dashboard to suggest models for a media provider kind
|
||
without exposing an HF token client-side. Never accepts or returns
|
||
credentials.
|
||
parameters:
|
||
- name: type
|
||
in: query
|
||
schema:
|
||
type: string
|
||
description: Media kind to search for (e.g. `image`, `audio`, `video`).
|
||
responses:
|
||
"200":
|
||
description: List of suggested HuggingFace Hub models.
|
||
"500":
|
||
$ref: "#/components/responses/InternalError"
|
||
/api/v1/provider-plugin-manifest:
|
||
get:
|
||
tags:
|
||
- Providers
|
||
summary: Provider plugin manifest
|
||
description: Returns the manifest describing installed provider plugins.
|
||
responses:
|
||
"200":
|
||
description: Provider plugin manifest.
|
||
"500":
|
||
$ref: "#/components/responses/InternalError"
|
||
/api/keys/{id}/devices:
|
||
get:
|
||
tags:
|
||
- API Keys
|
||
summary: List devices for an API key
|
||
description: >-
|
||
Lists the distinct devices (masked IP + User-Agent fingerprints)
|
||
tracked for an API key by the in-memory device tracker. IPs are masked
|
||
before storage; the route never sees the raw client IP.
|
||
x-internal: true
|
||
parameters:
|
||
- name: id
|
||
in: path
|
||
required: true
|
||
schema:
|
||
type: string
|
||
responses:
|
||
"200":
|
||
description: Distinct devices seen for the API key.
|
||
"401":
|
||
$ref: "#/components/responses/ManagementAuthenticationRequired"
|
||
"404":
|
||
$ref: "#/components/responses/NotFound"
|
||
/api/settings/purge-usage-history:
|
||
post:
|
||
tags:
|
||
- Settings
|
||
summary: Purge usage history
|
||
description: Dashboard-only. Purges stored usage-history records.
|
||
x-internal: true
|
||
responses:
|
||
"200":
|
||
description: Usage history purged.
|
||
"401":
|
||
$ref: "#/components/responses/Unauthorized"
|
||
/api/oauth/codex/import-token:
|
||
post:
|
||
tags:
|
||
- OAuth
|
||
summary: Import a Codex connection from a bare access token
|
||
description: >-
|
||
Dashboard-only. Creates a Codex (ChatGPT/OpenAI) connection from a raw
|
||
access token with no refresh token (authType `access_token`).
|
||
x-internal: true
|
||
responses:
|
||
"200":
|
||
description: Connection imported.
|
||
"400":
|
||
$ref: "#/components/responses/BadRequest"
|
||
"401":
|
||
$ref: "#/components/responses/Unauthorized"
|
||
/api/cli-tools/crush-settings:
|
||
get:
|
||
tags:
|
||
- CLI Tools
|
||
summary: Read Crush CLI OmniRoute config
|
||
description: Local-only. Reads the OmniRoute provider block in Crush's config.
|
||
x-internal: true
|
||
responses:
|
||
"200":
|
||
description: Current Crush config state.
|
||
post:
|
||
tags:
|
||
- CLI Tools
|
||
summary: Write Crush CLI OmniRoute config
|
||
description: Local-only. Registers OmniRoute as an `openai-compat` provider in Crush's config.
|
||
x-internal: true
|
||
responses:
|
||
"200":
|
||
description: Crush config updated.
|
||
delete:
|
||
tags:
|
||
- CLI Tools
|
||
summary: Remove OmniRoute from Crush CLI config
|
||
description: Local-only. Removes the OmniRoute provider block from Crush's config.
|
||
x-internal: true
|
||
responses:
|
||
"200":
|
||
description: Crush config entry removed.
|
||
/api/cli-tools/codewhale-settings:
|
||
get:
|
||
tags:
|
||
- CLI Tools
|
||
summary: Read CodeWhale CLI OmniRoute config
|
||
description: >-
|
||
Local-only. Reads the OmniRoute config block from
|
||
`~/.codewhale/config.toml` (with `~/.deepseek/config.toml` legacy
|
||
fallback).
|
||
x-internal: true
|
||
responses:
|
||
"200":
|
||
description: Current CodeWhale config state.
|
||
post:
|
||
tags:
|
||
- CLI Tools
|
||
summary: Write CodeWhale CLI OmniRoute config
|
||
description: Local-only. Writes the OmniRoute config block in CodeWhale TOML format.
|
||
x-internal: true
|
||
responses:
|
||
"200":
|
||
description: CodeWhale config updated.
|
||
delete:
|
||
tags:
|
||
- CLI Tools
|
||
summary: Remove OmniRoute from CodeWhale CLI config
|
||
description: Local-only. Removes the OmniRoute config block from CodeWhale's config.
|
||
x-internal: true
|
||
responses:
|
||
"200":
|
||
description: CodeWhale config entry removed.
|
||
|
||
components:
|
||
securitySchemes:
|
||
BearerAuth:
|
||
type: http
|
||
scheme: bearer
|
||
description: API key obtained from the OmniRoute dashboard
|
||
ManagementSessionAuth:
|
||
type: apiKey
|
||
in: cookie
|
||
name: auth_token
|
||
description: Dashboard management session cookie for protected management routes
|
||
|
||
parameters:
|
||
ResourceId:
|
||
name: id
|
||
in: path
|
||
required: true
|
||
schema:
|
||
type: string
|
||
|
||
responses:
|
||
Unauthorized:
|
||
description: Missing or invalid API key
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
error:
|
||
type: string
|
||
example: Unauthorized
|
||
ManagementAuthenticationRequired:
|
||
description: Authentication required for management routes
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ApiErrorResponse"
|
||
example:
|
||
error:
|
||
message: Authentication required
|
||
type: invalid_request
|
||
requestId: 3f9f6f5a-509a-4b35-b0a7-2d2d99d73a01
|
||
ManagementInvalidToken:
|
||
description: Bearer tokens are not accepted for management routes
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ApiErrorResponse"
|
||
example:
|
||
error:
|
||
message: Invalid management token
|
||
type: invalid_request
|
||
requestId: 1b6a6ff8-d60c-4900-8d0a-25f81749f0a3
|
||
ValidationError:
|
||
description: Request body failed validation
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ValidationErrorResponse"
|
||
BadRequest:
|
||
description: The request was malformed or failed validation
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ApiErrorResponse"
|
||
example:
|
||
error:
|
||
message: Invalid request
|
||
type: invalid_request_error
|
||
requestId: 8c2b1d44-7a3e-4c91-9b0f-1e2d3c4b5a60
|
||
NotFound:
|
||
description: The requested resource was not found
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ApiErrorResponse"
|
||
example:
|
||
error:
|
||
message: Resource not found
|
||
type: not_found_error
|
||
requestId: 4d5e6f70-1a2b-3c4d-5e6f-7a8b9c0d1e2f
|
||
InternalError:
|
||
description: An unexpected server error occurred
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ApiErrorResponse"
|
||
example:
|
||
error:
|
||
message: Internal server error
|
||
type: api_error
|
||
requestId: 0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d
|
||
|
||
schemas:
|
||
PlaygroundPreset:
|
||
type: object
|
||
required:
|
||
- id
|
||
- name
|
||
- endpoint
|
||
- model
|
||
- params
|
||
- created_at
|
||
properties:
|
||
id:
|
||
type: string
|
||
format: uuid
|
||
name:
|
||
type: string
|
||
maxLength: 100
|
||
endpoint:
|
||
type: string
|
||
description: Playground endpoint key (e.g. "chat.completions")
|
||
model:
|
||
type: string
|
||
system:
|
||
type: string
|
||
nullable: true
|
||
params:
|
||
type: object
|
||
additionalProperties: true
|
||
description: Serialized parameter values (temperature, max_tokens, etc.)
|
||
created_at:
|
||
type: string
|
||
format: date-time
|
||
PlaygroundPresetCreate:
|
||
type: object
|
||
required:
|
||
- name
|
||
- endpoint
|
||
- model
|
||
properties:
|
||
name:
|
||
type: string
|
||
minLength: 1
|
||
maxLength: 100
|
||
endpoint:
|
||
type: string
|
||
minLength: 1
|
||
model:
|
||
type: string
|
||
minLength: 1
|
||
system:
|
||
type: string
|
||
nullable: true
|
||
params:
|
||
type: object
|
||
additionalProperties: true
|
||
default: {}
|
||
MemoryEntry:
|
||
type: object
|
||
description: A single persisted memory entry
|
||
properties:
|
||
id:
|
||
type: string
|
||
description: UUID
|
||
apiKeyId:
|
||
type: string
|
||
sessionId:
|
||
type: string
|
||
nullable: true
|
||
type:
|
||
type: string
|
||
enum:
|
||
- factual
|
||
- episodic
|
||
- procedural
|
||
- semantic
|
||
key:
|
||
type: string
|
||
description: Stable upsert key (e.g. preference:i_prefer_python)
|
||
content:
|
||
type: string
|
||
metadata:
|
||
type: object
|
||
additionalProperties: true
|
||
createdAt:
|
||
type: string
|
||
format: date-time
|
||
updatedAt:
|
||
type: string
|
||
format: date-time
|
||
expiresAt:
|
||
type: string
|
||
format: date-time
|
||
nullable: true
|
||
needsReindex:
|
||
type: integer
|
||
description: 1 if the vector for this memory is stale or missing
|
||
MemorySettingsExtended:
|
||
type: object
|
||
description: Extended memory settings including 7 new fields from plan 21. All fields are optional for PUT (patch semantics).
|
||
properties:
|
||
enabled:
|
||
type: boolean
|
||
maxTokens:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 16000
|
||
retentionDays:
|
||
type: integer
|
||
minimum: 1
|
||
maximum: 365
|
||
strategy:
|
||
type: string
|
||
enum:
|
||
- recent
|
||
- semantic
|
||
- hybrid
|
||
skillsEnabled:
|
||
type: boolean
|
||
embeddingSource:
|
||
type: string
|
||
enum:
|
||
- remote
|
||
- static
|
||
- transformers
|
||
- auto
|
||
description: Which embedding source to use. "auto" = remote > static > transformers.
|
||
embeddingProviderModel:
|
||
type: string
|
||
nullable: true
|
||
description: Embedding provider/model in "provider/model" format (e.g. openai/text-embedding-3-small).
|
||
transformersEnabled:
|
||
type: boolean
|
||
description: Opt-in for Transformers.js local MiniLM model (~400MB RAM)
|
||
staticEnabled:
|
||
type: boolean
|
||
description: Opt-in for static potion-base-8M local model
|
||
rerankEnabled:
|
||
type: boolean
|
||
description: Enable reranking step (+200-500ms/req)
|
||
rerankProviderModel:
|
||
type: string
|
||
nullable: true
|
||
description: Rerank provider/model in "provider/model" format
|
||
vectorStore:
|
||
type: string
|
||
enum:
|
||
- sqlite-vec
|
||
- qdrant
|
||
- auto
|
||
description: Which vector backend to use
|
||
QdrantSettings:
|
||
type: object
|
||
description: Qdrant vector database configuration (read shape — no raw apiKey)
|
||
properties:
|
||
enabled:
|
||
type: boolean
|
||
host:
|
||
type: string
|
||
port:
|
||
type: integer
|
||
minimum: 1
|
||
maximum: 65535
|
||
collection:
|
||
type: string
|
||
embeddingModel:
|
||
type: string
|
||
hasApiKey:
|
||
type: boolean
|
||
apiKeyMasked:
|
||
type: string
|
||
nullable: true
|
||
description: First 4 chars of the configured API key, or null
|
||
QdrantHealthResult:
|
||
type: object
|
||
description: Result of a Qdrant liveness probe
|
||
properties:
|
||
ok:
|
||
type: boolean
|
||
latencyMs:
|
||
type: number
|
||
error:
|
||
type: string
|
||
nullable: true
|
||
description: Sanitized error message (no stack traces)
|
||
AgentSkill:
|
||
type: object
|
||
description: >-
|
||
Single entry in the Agent Skills catalog. Describes one OmniRoute REST API surface
|
||
(category: api) or CLI subcommand group (category: cli) with a canonical ID and a
|
||
link to its SKILL.md documentation file.
|
||
required: [id, name, description, category, area, rawUrl, githubUrl]
|
||
properties:
|
||
id:
|
||
type: string
|
||
pattern: "^[a-z][a-z0-9-]*$"
|
||
description: Canonical skill ID (e.g. "omni-providers", "cli-serve")
|
||
example: omni-providers
|
||
name:
|
||
type: string
|
||
minLength: 1
|
||
maxLength: 100
|
||
description: Human-readable skill name
|
||
example: Provider Management
|
||
description:
|
||
type: string
|
||
minLength: 1
|
||
maxLength: 2000
|
||
description: One-paragraph description of what the skill covers
|
||
category:
|
||
type: string
|
||
enum: [api, cli]
|
||
description: "api = REST API skill; cli = CLI subcommand skill"
|
||
area:
|
||
type: string
|
||
minLength: 1
|
||
maxLength: 50
|
||
description: Functional area slug (e.g. "providers", "combos-routing", "cli-serve")
|
||
example: providers
|
||
endpoints:
|
||
type: array
|
||
items:
|
||
type: string
|
||
description: REST API endpoints (present for api-category skills only)
|
||
example: ["POST /api/providers", "GET /api/providers/:id"]
|
||
cliCommands:
|
||
type: array
|
||
items:
|
||
type: string
|
||
description: CLI subcommand names (present for cli-category skills only)
|
||
example: ["providers list", "providers test", "providers rotate"]
|
||
icon:
|
||
type: string
|
||
description: Material symbol icon name for dashboard display
|
||
isEntry:
|
||
type: boolean
|
||
description: Whether this is a recommended starting point
|
||
isNew:
|
||
type: boolean
|
||
description: Whether this skill was added in a recent release
|
||
rawUrl:
|
||
type: string
|
||
format: uri
|
||
description: GitHub raw URL of the SKILL.md file
|
||
example: "https://raw.githubusercontent.com/diegosouzapw/OmniRoute/refs/heads/main/skills/omni-providers/SKILL.md"
|
||
githubUrl:
|
||
type: string
|
||
format: uri
|
||
description: GitHub blob URL for viewing the SKILL.md in the browser
|
||
example: "https://github.com/diegosouzapw/OmniRoute/blob/main/skills/omni-providers/SKILL.md"
|
||
|
||
SkillCoverage:
|
||
type: object
|
||
description: >-
|
||
Coverage statistics for the Agent Skills catalog: how many of the 22 REST API
|
||
skills and 20 CLI skills have generated SKILL.md files on the local filesystem.
|
||
required: [api, cli, totalSkills, generatedAt]
|
||
properties:
|
||
api:
|
||
type: object
|
||
required: [have, total]
|
||
properties:
|
||
have:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 22
|
||
description: Number of API skills with SKILL.md on disk
|
||
total:
|
||
type: integer
|
||
enum: [22]
|
||
description: Canonical API skill count (always 22)
|
||
cli:
|
||
type: object
|
||
required: [have, total]
|
||
properties:
|
||
have:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 20
|
||
description: Number of CLI skills with SKILL.md on disk
|
||
total:
|
||
type: integer
|
||
enum: [20]
|
||
description: Canonical CLI skill count (always 20)
|
||
totalSkills:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 42
|
||
description: Sum of api.have + cli.have
|
||
generatedAt:
|
||
type: string
|
||
format: date-time
|
||
description: ISO datetime when coverage was last computed
|
||
|
||
ErrorResponse:
|
||
type: object
|
||
description: Standard error response body
|
||
required: [error]
|
||
properties:
|
||
error:
|
||
type: object
|
||
required: [message]
|
||
properties:
|
||
message:
|
||
type: string
|
||
description: Human-readable error message (never includes stack traces)
|
||
code:
|
||
type: string
|
||
description: Machine-readable error code
|
||
|
||
# ─── AgentBridge Schemas ────────────────────────────────────────
|
||
|
||
AgentId:
|
||
type: string
|
||
enum:
|
||
- antigravity
|
||
- kiro
|
||
- copilot
|
||
- codex
|
||
- cursor
|
||
- zed
|
||
- claude-code
|
||
- open-code
|
||
- trae
|
||
description: One of the 9 supported IDE agents
|
||
|
||
AgentBridgeAgentState:
|
||
type: object
|
||
description: Per-agent MITM state
|
||
properties:
|
||
agent_id:
|
||
$ref: "#/components/schemas/AgentId"
|
||
dns_enabled:
|
||
type: boolean
|
||
cert_trusted:
|
||
type: boolean
|
||
setup_completed:
|
||
type: boolean
|
||
last_started_at:
|
||
type: string
|
||
format: date-time
|
||
nullable: true
|
||
last_error:
|
||
type: string
|
||
nullable: true
|
||
|
||
AgentBridgeServerState:
|
||
type: object
|
||
description: Global AgentBridge MITM server state
|
||
properties:
|
||
running:
|
||
type: boolean
|
||
port:
|
||
type: integer
|
||
example: 443
|
||
certReady:
|
||
type: boolean
|
||
interceptedCount:
|
||
type: integer
|
||
activeConnections:
|
||
type: integer
|
||
lastStartedAt:
|
||
type: string
|
||
format: date-time
|
||
nullable: true
|
||
|
||
AgentBridgeServerAction:
|
||
type: object
|
||
required: [action]
|
||
properties:
|
||
action:
|
||
type: string
|
||
enum: [start, stop, restart, trust-cert, regenerate-cert]
|
||
|
||
AgentBridgeDnsAction:
|
||
type: object
|
||
required: [enabled]
|
||
properties:
|
||
enabled:
|
||
type: boolean
|
||
|
||
AgentBridgeMappingRow:
|
||
type: object
|
||
properties:
|
||
agent_id:
|
||
$ref: "#/components/schemas/AgentId"
|
||
source_model:
|
||
type: string
|
||
example: gpt-4o
|
||
target_model:
|
||
type: string
|
||
example: claude-sonnet-4.7
|
||
updated_at:
|
||
type: string
|
||
format: date-time
|
||
|
||
AgentBridgeMappingPut:
|
||
type: object
|
||
required: [mappings]
|
||
properties:
|
||
mappings:
|
||
type: array
|
||
items:
|
||
type: object
|
||
required: [source, target]
|
||
properties:
|
||
source:
|
||
type: string
|
||
example: gpt-4o
|
||
target:
|
||
type: string
|
||
example: claude-sonnet-4.7
|
||
|
||
AgentBridgeBypassRow:
|
||
type: object
|
||
properties:
|
||
pattern:
|
||
type: string
|
||
example: "*.bank.*"
|
||
source:
|
||
type: string
|
||
enum: [default, user]
|
||
created_at:
|
||
type: string
|
||
format: date-time
|
||
|
||
AgentBridgeBypassUpsert:
|
||
type: object
|
||
required: [patterns]
|
||
properties:
|
||
patterns:
|
||
type: array
|
||
items:
|
||
type: string
|
||
example: ["*.bank.*", "*.gov.*"]
|
||
|
||
AgentBridgeUpstreamCaPost:
|
||
type: object
|
||
required: [path]
|
||
properties:
|
||
path:
|
||
type: string
|
||
description: Absolute path to a PEM file for corporate upstream CA
|
||
example: "/etc/ssl/certs/corporate-ca.pem"
|
||
|
||
# ─── Traffic Inspector Schemas ──────────────────────────────────
|
||
|
||
CaptureSource:
|
||
type: string
|
||
enum: [agent-bridge, custom-host, http-proxy, system-proxy]
|
||
|
||
DetectedKind:
|
||
type: string
|
||
enum: [llm, app, unknown]
|
||
|
||
InterceptedRequest:
|
||
type: object
|
||
description: A single intercepted HTTP request captured by the Traffic Inspector
|
||
required:
|
||
[
|
||
id,
|
||
source,
|
||
timestamp,
|
||
method,
|
||
host,
|
||
path,
|
||
requestHeaders,
|
||
requestSize,
|
||
responseHeaders,
|
||
responseSize,
|
||
status,
|
||
]
|
||
properties:
|
||
id:
|
||
type: string
|
||
format: uuid
|
||
source:
|
||
$ref: "#/components/schemas/CaptureSource"
|
||
agent:
|
||
$ref: "#/components/schemas/AgentId"
|
||
timestamp:
|
||
type: string
|
||
format: date-time
|
||
method:
|
||
type: string
|
||
example: POST
|
||
host:
|
||
type: string
|
||
example: api.githubcopilot.com
|
||
path:
|
||
type: string
|
||
example: /v1/chat/completions
|
||
requestHeaders:
|
||
type: object
|
||
additionalProperties:
|
||
type: string
|
||
requestBody:
|
||
type: string
|
||
nullable: true
|
||
description: Masked (secrets replaced with ***)
|
||
requestSize:
|
||
type: integer
|
||
responseHeaders:
|
||
type: object
|
||
additionalProperties:
|
||
type: string
|
||
responseBody:
|
||
type: string
|
||
nullable: true
|
||
responseSize:
|
||
type: integer
|
||
status:
|
||
oneOf:
|
||
- type: integer
|
||
- type: string
|
||
enum: [in-flight, error]
|
||
proxyLatencyMs:
|
||
type: number
|
||
nullable: true
|
||
upstreamLatencyMs:
|
||
type: number
|
||
nullable: true
|
||
totalLatencyMs:
|
||
type: number
|
||
nullable: true
|
||
error:
|
||
type: string
|
||
nullable: true
|
||
description: Sanitized error message (no stack traces)
|
||
sourceModel:
|
||
type: string
|
||
nullable: true
|
||
mappedModel:
|
||
type: string
|
||
nullable: true
|
||
detectedKind:
|
||
$ref: "#/components/schemas/DetectedKind"
|
||
contextKey:
|
||
type: string
|
||
nullable: true
|
||
description: 12-char SHA-256 hex of the system prompt (for conversation grouping)
|
||
example: a3f9c2b1d5e4
|
||
annotation:
|
||
type: string
|
||
nullable: true
|
||
sessionId:
|
||
type: string
|
||
format: uuid
|
||
nullable: true
|
||
note:
|
||
type: string
|
||
nullable: true
|
||
description: Informational note (e.g. TLS tunnel metadata)
|
||
|
||
InspectorCustomHost:
|
||
type: object
|
||
properties:
|
||
host:
|
||
type: string
|
||
example: api.openai.com
|
||
enabled:
|
||
type: boolean
|
||
label:
|
||
type: string
|
||
nullable: true
|
||
kind:
|
||
type: string
|
||
enum: [llm, app, custom]
|
||
added_at:
|
||
type: string
|
||
format: date-time
|
||
last_seen_at:
|
||
type: string
|
||
format: date-time
|
||
nullable: true
|
||
|
||
InspectorCustomHostCreate:
|
||
type: object
|
||
required: [host]
|
||
properties:
|
||
host:
|
||
type: string
|
||
minLength: 1
|
||
example: my-internal-llm.company.com
|
||
enabled:
|
||
type: boolean
|
||
default: true
|
||
label:
|
||
type: string
|
||
nullable: true
|
||
kind:
|
||
type: string
|
||
enum: [llm, app, custom]
|
||
default: custom
|
||
|
||
InspectorCaptureModesState:
|
||
type: object
|
||
properties:
|
||
agentBridge:
|
||
type: object
|
||
properties:
|
||
active:
|
||
type: boolean
|
||
customHosts:
|
||
type: object
|
||
properties:
|
||
active:
|
||
type: boolean
|
||
count:
|
||
type: integer
|
||
httpProxy:
|
||
type: object
|
||
properties:
|
||
active:
|
||
type: boolean
|
||
port:
|
||
type: integer
|
||
example: 8080
|
||
systemProxy:
|
||
type: object
|
||
properties:
|
||
active:
|
||
type: boolean
|
||
guardMinutes:
|
||
type: integer
|
||
|
||
InspectorCaptureModeAction:
|
||
type: object
|
||
required: [action]
|
||
properties:
|
||
action:
|
||
type: string
|
||
enum: [start, stop]
|
||
|
||
InspectorSystemProxyAction:
|
||
type: object
|
||
required: [action]
|
||
properties:
|
||
action:
|
||
type: string
|
||
enum: [apply, revert]
|
||
port:
|
||
type: integer
|
||
minimum: 1
|
||
maximum: 65535
|
||
example: 8080
|
||
guardMinutes:
|
||
type: integer
|
||
minimum: 1
|
||
example: 30
|
||
|
||
InspectorTlsInterceptToggle:
|
||
type: object
|
||
required: [enabled]
|
||
properties:
|
||
enabled:
|
||
type: boolean
|
||
|
||
InspectorAnnotationPut:
|
||
type: object
|
||
required: [annotation]
|
||
properties:
|
||
annotation:
|
||
type: string
|
||
maxLength: 10000
|
||
|
||
InspectorSession:
|
||
type: object
|
||
properties:
|
||
id:
|
||
type: string
|
||
format: uuid
|
||
name:
|
||
type: string
|
||
nullable: true
|
||
started_at:
|
||
type: string
|
||
format: date-time
|
||
ended_at:
|
||
type: string
|
||
format: date-time
|
||
nullable: true
|
||
request_count:
|
||
type: integer
|
||
profile:
|
||
type: string
|
||
enum: [llm, custom, all]
|
||
nullable: true
|
||
|
||
InspectorSessionStart:
|
||
type: object
|
||
properties:
|
||
name:
|
||
type: string
|
||
example: "Antigravity test run #1"
|
||
|
||
InspectorSessionPatch:
|
||
type: object
|
||
required: [action]
|
||
properties:
|
||
action:
|
||
type: string
|
||
enum: [stop, rename]
|
||
name:
|
||
type: string
|
||
QuotaPool:
|
||
type: object
|
||
description: A quota sharing pool — binds a provider connection to allocation rules.
|
||
required: [id, connectionId, name, createdAt, allocations]
|
||
properties:
|
||
id:
|
||
type: string
|
||
connectionId:
|
||
type: string
|
||
name:
|
||
type: string
|
||
createdAt:
|
||
type: string
|
||
format: date-time
|
||
allocations:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/PoolAllocation"
|
||
|
||
PoolAllocation:
|
||
type: object
|
||
required: [apiKeyId, weight, policy]
|
||
properties:
|
||
apiKeyId:
|
||
type: string
|
||
weight:
|
||
type: number
|
||
minimum: 0
|
||
maximum: 100
|
||
description: Share percentage (0–100)
|
||
capValue:
|
||
type: number
|
||
nullable: true
|
||
description: Absolute cap value (optional)
|
||
capUnit:
|
||
type: string
|
||
enum: [percent, requests, tokens, usd]
|
||
nullable: true
|
||
policy:
|
||
type: string
|
||
enum: [hard, soft, burst]
|
||
|
||
PoolCreate:
|
||
type: object
|
||
required: [connectionId, name]
|
||
properties:
|
||
connectionId:
|
||
type: string
|
||
name:
|
||
type: string
|
||
maxLength: 120
|
||
allocations:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/PoolAllocation"
|
||
default: []
|
||
|
||
PoolUpdate:
|
||
type: object
|
||
properties:
|
||
name:
|
||
type: string
|
||
maxLength: 120
|
||
allocations:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/PoolAllocation"
|
||
|
||
PoolUsageSnapshot:
|
||
type: object
|
||
required: [poolId, generatedAt, dimensions]
|
||
properties:
|
||
poolId:
|
||
type: string
|
||
generatedAt:
|
||
type: string
|
||
format: date-time
|
||
dimensions:
|
||
type: array
|
||
items:
|
||
type: object
|
||
properties:
|
||
unit:
|
||
type: string
|
||
enum: [percent, requests, tokens, usd]
|
||
window:
|
||
type: string
|
||
enum: ["5h", hourly, daily, weekly, monthly]
|
||
limit:
|
||
type: number
|
||
consumedTotal:
|
||
type: number
|
||
perKey:
|
||
type: array
|
||
items:
|
||
type: object
|
||
properties:
|
||
apiKeyId:
|
||
type: string
|
||
consumed:
|
||
type: number
|
||
fairShare:
|
||
type: number
|
||
deficit:
|
||
type: number
|
||
description: "Negative = surplus; positive = over-allocation"
|
||
borrowing:
|
||
type: boolean
|
||
burnRate:
|
||
type: object
|
||
nullable: true
|
||
properties:
|
||
tokensPerSecond:
|
||
type: number
|
||
timeToExhaustionMs:
|
||
type: number
|
||
nullable: true
|
||
|
||
QuotaDimension:
|
||
type: object
|
||
required: [unit, window, limit]
|
||
properties:
|
||
unit:
|
||
type: string
|
||
enum: [percent, requests, tokens, usd]
|
||
window:
|
||
type: string
|
||
enum: ["5h", hourly, daily, weekly, monthly]
|
||
limit:
|
||
type: number
|
||
minimum: 0
|
||
|
||
PlanUpsert:
|
||
type: object
|
||
required: [dimensions]
|
||
properties:
|
||
dimensions:
|
||
type: array
|
||
minItems: 1
|
||
items:
|
||
$ref: "#/components/schemas/QuotaDimension"
|
||
|
||
QuotaStoreSettings:
|
||
type: object
|
||
required: [driver]
|
||
properties:
|
||
driver:
|
||
type: string
|
||
enum: [sqlite, redis]
|
||
redisUrl:
|
||
type: string
|
||
format: uri
|
||
nullable: true
|
||
description: Redis connection URL (write-only; masked in GET responses)
|
||
|
||
ServiceStatus:
|
||
type: object
|
||
description: Live supervisor state for an embedded service
|
||
properties:
|
||
tool:
|
||
type: string
|
||
example: 9router
|
||
state:
|
||
type: string
|
||
enum: [not_installed, stopped, starting, running, stopping, error]
|
||
pid:
|
||
type: integer
|
||
nullable: true
|
||
port:
|
||
type: integer
|
||
example: 20130
|
||
health:
|
||
type: string
|
||
enum: [unknown, healthy, degraded]
|
||
startedAt:
|
||
type: string
|
||
format: date-time
|
||
nullable: true
|
||
lastError:
|
||
type: string
|
||
nullable: true
|
||
|
||
ServiceStatusExtended:
|
||
allOf:
|
||
- $ref: "#/components/schemas/ServiceStatus"
|
||
- type: object
|
||
description: >-
|
||
Extended status including version metadata and (for 9Router) API key preview.
|
||
properties:
|
||
installedVersion:
|
||
type: string
|
||
nullable: true
|
||
latestVersion:
|
||
type: string
|
||
nullable: true
|
||
updateAvailable:
|
||
type: boolean
|
||
apiKeyMasked:
|
||
type: string
|
||
nullable: true
|
||
description: >-
|
||
Masked API key preview (e.g. "nr_****abcd").
|
||
Present only for services that use an injected API key (9Router).
|
||
autoStart:
|
||
type: boolean
|
||
providerExpose:
|
||
type: boolean
|
||
description: >-
|
||
Whether models from this service are exposed as a routing provider.
|
||
9Router only.
|
||
|
||
ApiErrorResponse:
|
||
type: object
|
||
properties:
|
||
error:
|
||
type: object
|
||
properties:
|
||
message:
|
||
type: string
|
||
type:
|
||
type: string
|
||
details:
|
||
description: Optional additional error details
|
||
requestId:
|
||
type: string
|
||
format: uuid
|
||
|
||
ValidationErrorResponse:
|
||
type: object
|
||
properties:
|
||
error:
|
||
type: object
|
||
required: [message, details]
|
||
properties:
|
||
message:
|
||
type: string
|
||
example: Invalid request
|
||
details:
|
||
type: array
|
||
items:
|
||
type: object
|
||
required: [field, message]
|
||
properties:
|
||
field:
|
||
type: string
|
||
message:
|
||
type: string
|
||
|
||
PayloadRuleModelSpec:
|
||
type: object
|
||
additionalProperties: false
|
||
required: [name]
|
||
properties:
|
||
name:
|
||
type: string
|
||
minLength: 1
|
||
protocol:
|
||
type: string
|
||
minLength: 1
|
||
|
||
PayloadMutationRule:
|
||
type: object
|
||
additionalProperties: false
|
||
required: [models, params]
|
||
properties:
|
||
models:
|
||
type: array
|
||
minItems: 1
|
||
items:
|
||
$ref: "#/components/schemas/PayloadRuleModelSpec"
|
||
params:
|
||
type: object
|
||
minProperties: 1
|
||
additionalProperties: true
|
||
|
||
PayloadFilterRule:
|
||
type: object
|
||
additionalProperties: false
|
||
required: [models, params]
|
||
properties:
|
||
models:
|
||
type: array
|
||
minItems: 1
|
||
items:
|
||
$ref: "#/components/schemas/PayloadRuleModelSpec"
|
||
params:
|
||
type: array
|
||
minItems: 1
|
||
items:
|
||
type: string
|
||
minLength: 1
|
||
|
||
PayloadRulesConfig:
|
||
type: object
|
||
additionalProperties: false
|
||
required: [default, override, filter, defaultRaw]
|
||
properties:
|
||
default:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/PayloadMutationRule"
|
||
override:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/PayloadMutationRule"
|
||
filter:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/PayloadFilterRule"
|
||
defaultRaw:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/PayloadMutationRule"
|
||
|
||
UpdatePayloadRulesRequest:
|
||
type: object
|
||
additionalProperties: false
|
||
description: At least one payload-rules section must be present in the request body.
|
||
properties:
|
||
default:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/PayloadMutationRule"
|
||
override:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/PayloadMutationRule"
|
||
filter:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/PayloadFilterRule"
|
||
defaultRaw:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/PayloadMutationRule"
|
||
default-raw:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/PayloadMutationRule"
|
||
anyOf:
|
||
- required: [default]
|
||
- required: [override]
|
||
- required: [filter]
|
||
- required: [defaultRaw]
|
||
- required: [default-raw]
|
||
|
||
ChatCompletionRequest:
|
||
type: object
|
||
required: [model, messages]
|
||
properties:
|
||
model:
|
||
type: string
|
||
example: gpt-4o
|
||
messages:
|
||
type: array
|
||
items:
|
||
type: object
|
||
required: [role]
|
||
properties:
|
||
role:
|
||
type: string
|
||
description: >-
|
||
Message role. The proxy accepts any non-empty string; common values
|
||
include system, user, assistant, tool, function, and developer.
|
||
example: user
|
||
content:
|
||
description: >-
|
||
Message content. May be a plain string, an array of content parts
|
||
for multimodal inputs (text, image, audio, etc.), or null when the
|
||
message only carries tool/function calls.
|
||
oneOf:
|
||
- type: string
|
||
- type: array
|
||
items:
|
||
type: object
|
||
- type: "null"
|
||
name:
|
||
type: string
|
||
tool_call_id:
|
||
type: string
|
||
tool_calls:
|
||
type: array
|
||
items:
|
||
type: object
|
||
function_call:
|
||
type: object
|
||
stream:
|
||
type: boolean
|
||
default: false
|
||
temperature:
|
||
type: number
|
||
minimum: 0
|
||
maximum: 2
|
||
max_tokens:
|
||
type: integer
|
||
top_p:
|
||
type: number
|
||
minimum: 0
|
||
maximum: 1
|
||
n:
|
||
type: integer
|
||
minimum: 1
|
||
default: 1
|
||
stop:
|
||
description: Up to 4 stop sequences (string or array of strings).
|
||
oneOf:
|
||
- type: string
|
||
- type: array
|
||
items:
|
||
type: string
|
||
maxItems: 4
|
||
frequency_penalty:
|
||
type: number
|
||
minimum: -2
|
||
maximum: 2
|
||
presence_penalty:
|
||
type: number
|
||
minimum: -2
|
||
maximum: 2
|
||
seed:
|
||
type: integer
|
||
logprobs:
|
||
type: boolean
|
||
top_logprobs:
|
||
type: integer
|
||
minimum: 0
|
||
maximum: 20
|
||
response_format:
|
||
type: object
|
||
description: Output format constraint (e.g. JSON mode or JSON Schema).
|
||
properties:
|
||
type:
|
||
type: string
|
||
example: json_object
|
||
tools:
|
||
type: array
|
||
description: Tool definitions available to the model.
|
||
items:
|
||
type: object
|
||
tool_choice:
|
||
description: Controls which tool (if any) is invoked by the model.
|
||
oneOf:
|
||
- type: string
|
||
example: auto
|
||
- type: object
|
||
parallel_tool_calls:
|
||
type: boolean
|
||
default: true
|
||
service_tier:
|
||
type: string
|
||
example: auto
|
||
user:
|
||
type: string
|
||
description: Stable end-user identifier for abuse monitoring.
|
||
|
||
ChatCompletionResponse:
|
||
type: object
|
||
properties:
|
||
id:
|
||
type: string
|
||
object:
|
||
type: string
|
||
example: chat.completion
|
||
choices:
|
||
type: array
|
||
items:
|
||
type: object
|
||
properties:
|
||
index:
|
||
type: integer
|
||
message:
|
||
type: object
|
||
properties:
|
||
role:
|
||
type: string
|
||
content:
|
||
type: string
|
||
finish_reason:
|
||
type: string
|
||
usage:
|
||
type: object
|
||
properties:
|
||
prompt_tokens:
|
||
type: integer
|
||
completion_tokens:
|
||
type: integer
|
||
total_tokens:
|
||
type: integer
|
||
|
||
MessagesRequest:
|
||
type: object
|
||
required: [model, messages, max_tokens]
|
||
properties:
|
||
model:
|
||
type: string
|
||
example: claude-sonnet-4-5-20250514
|
||
messages:
|
||
type: array
|
||
items:
|
||
type: object
|
||
required: [role, content]
|
||
properties:
|
||
role:
|
||
type: string
|
||
enum: [user, assistant]
|
||
content:
|
||
type: string
|
||
max_tokens:
|
||
type: integer
|
||
stream:
|
||
type: boolean
|
||
default: false
|
||
system:
|
||
type: string
|
||
|
||
Model:
|
||
type: object
|
||
properties:
|
||
id:
|
||
type: string
|
||
object:
|
||
type: string
|
||
example: model
|
||
owned_by:
|
||
type: string
|
||
|
||
ProviderConnection:
|
||
type: object
|
||
properties:
|
||
id:
|
||
type: string
|
||
provider:
|
||
type: string
|
||
name:
|
||
type: string
|
||
url:
|
||
type: string
|
||
isActive:
|
||
type: boolean
|
||
maxConcurrent:
|
||
type: integer
|
||
nullable: true
|
||
minimum: 0
|
||
priority:
|
||
type: integer
|
||
testStatus:
|
||
type: string
|
||
enum: [active, error, untested]
|
||
createdAt:
|
||
type: string
|
||
format: date-time
|
||
|
||
ProviderConnectionCreate:
|
||
type: object
|
||
required: [provider, url]
|
||
properties:
|
||
provider:
|
||
type: string
|
||
example: openai
|
||
name:
|
||
type: string
|
||
url:
|
||
type: string
|
||
apiKey:
|
||
type: string
|
||
isActive:
|
||
type: boolean
|
||
default: true
|
||
maxConcurrent:
|
||
type: integer
|
||
nullable: true
|
||
minimum: 0
|
||
|
||
ApiKey:
|
||
type: object
|
||
properties:
|
||
id:
|
||
type: string
|
||
label:
|
||
type: string
|
||
keyPreview:
|
||
type: string
|
||
description: Last 4 characters of the key
|
||
isActive:
|
||
type: boolean
|
||
createdAt:
|
||
type: string
|
||
format: date-time
|
||
|
||
ComboCreate:
|
||
type: object
|
||
required: [name, model]
|
||
properties:
|
||
name:
|
||
type: string
|
||
model:
|
||
type: string
|
||
strategy:
|
||
type: string
|
||
enum:
|
||
- priority
|
||
- weighted
|
||
- round-robin
|
||
- context-relay
|
||
- fill-first
|
||
- p2c
|
||
- random
|
||
- least-used
|
||
- cost-optimized
|
||
- reset-aware
|
||
- reset-window
|
||
- headroom
|
||
- strict-random
|
||
- auto
|
||
- lkgp
|
||
- context-optimized
|
||
- fusion
|
||
default: priority
|
||
nodes:
|
||
type: array
|
||
items:
|
||
type: object
|
||
properties:
|
||
connectionId:
|
||
type: string
|
||
weight:
|
||
type: integer
|
||
priority:
|
||
type: integer
|