From 463565c71a75304e56397cb1ced10daee1057e7d Mon Sep 17 00:00:00 2001 From: KooshaPari <42529354+KooshaPari@users.noreply.github.com> Date: Tue, 23 Jun 2026 06:29:40 -0700 Subject: [PATCH] feat(api-docs): Redoc-rendered /api/docs + consolidate OpenAPI spec to docs/openapi.yaml (#4781) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Redoc /api/docs + OpenAPI spec consolidated to docs/openapi.yaml (canonical 201-path complete spec; old path → legacy fallback). All refs/gates/tests/CI updated. Integrated into release/v3.8.35. --- .github/workflows/ci.yml | 4 +- .github/workflows/dast-smoke.yml | 2 +- .github/workflows/nightly-schemathesis.yml | 2 +- AGENTS.md | 2 +- CHANGELOG.md | 2 +- CLAUDE.md | 4 +- README.md | 2 +- bin/cli/api-commands/agent-skills.mjs | 2 +- bin/cli/api-commands/agentbridge.mjs | 2 +- bin/cli/api-commands/api-keys.mjs | 2 +- bin/cli/api-commands/audio.mjs | 2 +- bin/cli/api-commands/chat.mjs | 2 +- bin/cli/api-commands/cli-tools.mjs | 2 +- bin/cli/api-commands/cloud.mjs | 2 +- bin/cli/api-commands/combos.mjs | 2 +- bin/cli/api-commands/compression.mjs | 2 +- bin/cli/api-commands/embedded-services.mjs | 2 +- bin/cli/api-commands/embeddings.mjs | 2 +- bin/cli/api-commands/fallback.mjs | 2 +- bin/cli/api-commands/images.mjs | 2 +- bin/cli/api-commands/memory.mjs | 2 +- bin/cli/api-commands/messages.mjs | 2 +- bin/cli/api-commands/models.mjs | 2 +- bin/cli/api-commands/moderations.mjs | 2 +- bin/cli/api-commands/oauth.mjs | 2 +- bin/cli/api-commands/playground.mjs | 2 +- bin/cli/api-commands/pricing.mjs | 2 +- bin/cli/api-commands/provider-nodes.mjs | 2 +- bin/cli/api-commands/providers.mjs | 2 +- bin/cli/api-commands/quota.mjs | 2 +- bin/cli/api-commands/rerank.mjs | 2 +- bin/cli/api-commands/responses.mjs | 2 +- bin/cli/api-commands/settings.mjs | 2 +- bin/cli/api-commands/system.mjs | 2 +- bin/cli/api-commands/telemetry.mjs | 2 +- bin/cli/api-commands/traffic-inspector.mjs | 2 +- bin/cli/api-commands/translator.mjs | 2 +- bin/cli/api-commands/usage.mjs | 2 +- docs/README.md | 2 +- docs/architecture/CODEBASE_DOCUMENTATION.md | 2 +- docs/architecture/QUALITY_GATES.md | 2 +- docs/frameworks/AGENT-SKILLS.md | 2 +- docs/frameworks/AGENTBRIDGE.md | 2 +- docs/frameworks/EMBEDDED-SERVICES.md | 4 +- docs/frameworks/TRAFFIC_INSPECTOR.md | 2 +- docs/guides/REMOTE-MODE.md | 2 +- docs/{reference => }/openapi.yaml | 0 docs/ops/DOCUMENTATION_AUDIT_REPORT.md | 2 +- docs/ops/RELEASE_CHECKLIST.md | 4 +- docs/security/ROUTE_GUARD_TIERS.md | 2 +- public/openapi.yaml | 6690 +++++++++++++++++ scripts/build/pack-artifact-policy.ts | 2 +- scripts/check/check-docs-sync.mjs | 4 +- scripts/check/check-fabricated-docs.mjs | 2 +- scripts/check/check-openapi-breaking.mjs | 8 +- scripts/check/check-openapi-coverage.mjs | 2 +- scripts/check/check-openapi-routes.mjs | 4 +- .../check/check-openapi-security-tiers.mjs | 2 +- scripts/cli/generate-api-commands.mjs | 2 +- scripts/docs/gen-openapi-module.mjs | 6 +- scripts/quality/collect-metrics.mjs | 2 +- skills/omni-agents-a2a/SKILL.md | 2 +- skills/omni-api-keys/SKILL.md | 2 +- skills/omni-auth/SKILL.md | 2 +- skills/omni-budget/SKILL.md | 2 +- skills/omni-cache/SKILL.md | 2 +- skills/omni-cli-tools/SKILL.md | 2 +- skills/omni-combos-routing/SKILL.md | 2 +- skills/omni-compression/SKILL.md | 2 +- skills/omni-context-rtk/SKILL.md | 2 +- skills/omni-db-backups/SKILL.md | 2 +- skills/omni-inference/SKILL.md | 2 +- skills/omni-mcp/SKILL.md | 2 +- skills/omni-models/SKILL.md | 2 +- skills/omni-providers/SKILL.md | 2 +- skills/omni-proxies/SKILL.md | 2 +- skills/omni-resilience/SKILL.md | 2 +- skills/omni-settings/SKILL.md | 2 +- skills/omni-sync-cloud/SKILL.md | 2 +- skills/omni-tunnels/SKILL.md | 2 +- skills/omni-usage-logs/SKILL.md | 2 +- skills/omni-version-manager/SKILL.md | 2 +- skills/omni-webhooks/SKILL.md | 2 +- src/app/api/docs/route.ts | 89 + src/app/api/openapi/spec/route.ts | 7 +- src/app/docs/lib/openapi.generated.ts | 17 +- src/lib/agentSkills/generator.ts | 2 +- src/lib/agentSkills/openapiParser.ts | 6 +- tests/integration/integration-wiring.test.ts | 6 +- tests/unit/api-docs-route.test.ts | 24 + tests/unit/dast-method-not-allowed.test.ts | 2 +- tests/unit/openapi-coverage.test.ts | 2 +- tests/unit/openapi-security-tiers.test.ts | 2 +- tests/unit/openapi-ws-endpoint.test.ts | 2 +- 94 files changed, 6923 insertions(+), 110 deletions(-) rename docs/{reference => }/openapi.yaml (100%) create mode 100644 public/openapi.yaml create mode 100644 src/app/api/docs/route.ts create mode 100644 tests/unit/api-docs-route.test.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 93e71bc2f8..0da08470d4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -227,7 +227,7 @@ jobs: runs-on: ubuntu-latest steps: # fetch-depth: 0 — the OpenAPI breaking-change gate (oasdiff) reads the base - # spec via `git show :docs/reference/openapi.yaml`; a shallow clone + # spec via `git show :docs/openapi.yaml`; a shallow clone # would lack the base ref and the gate would self-skip (base-unresolved). - uses: actions/checkout@v7 with: @@ -318,7 +318,7 @@ jobs: - name: Workflow lint (actionlint+zizmor, ratchet, blocking) run: npm run check:workflows -- --ratchet # OpenAPI breaking-change detection (oasdiff). Diffs the PR's public API - # contract (docs/reference/openapi.yaml) against the base branch's spec. + # contract (docs/openapi.yaml) against the base branch's spec. # BLOCKING ratchet (Fase 9 Onda 0): reads metrics.openapiBreaking.value and # exits 1 ONLY on a measured regression (count > baseline). It SKIPs (exit 0) # when oasdiff is absent or the base spec can't be resolved — a missing diff --git a/.github/workflows/dast-smoke.yml b/.github/workflows/dast-smoke.yml index e06968cf61..5d8676cea7 100644 --- a/.github/workflows/dast-smoke.yml +++ b/.github/workflows/dast-smoke.yml @@ -42,7 +42,7 @@ jobs: - run: pip install schemathesis - name: Schemathesis smoke (high-risk endpoints, blocking) run: | - schemathesis run docs/reference/openapi.yaml --url http://localhost:20128 \ + schemathesis run docs/openapi.yaml --url http://localhost:20128 \ --include-path-regex '^/v1/(chat/completions|models)$|^/api/(auth|keys)' \ --max-examples 8 --workers 4 --checks all --max-response-time 30 \ --request-timeout 20 --suppress-health-check all --no-color diff --git a/.github/workflows/nightly-schemathesis.yml b/.github/workflows/nightly-schemathesis.yml index 0ee4149c8e..e430677638 100644 --- a/.github/workflows/nightly-schemathesis.yml +++ b/.github/workflows/nightly-schemathesis.yml @@ -45,7 +45,7 @@ jobs: # PROVE the contract is fuzzable and surface regressions, not to gate the build. continue-on-error: true run: | - schemathesis run docs/reference/openapi.yaml \ + schemathesis run docs/openapi.yaml \ --url http://localhost:20128 \ --max-examples 20 \ --workers 4 \ diff --git a/AGENTS.md b/AGENTS.md index cf7ec1b4a6..b06fc9b2d6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -554,7 +554,7 @@ For any non-trivial change, read the matching deep-dive first: | Agent protocols (A2A / ACP / Cloud) | [`docs/frameworks/AGENT_PROTOCOLS_GUIDE.md`](docs/frameworks/AGENT_PROTOCOLS_GUIDE.md) | | MCP server | [`docs/frameworks/MCP-SERVER.md`](docs/frameworks/MCP-SERVER.md) | | A2A server | [`docs/frameworks/A2A-SERVER.md`](docs/frameworks/A2A-SERVER.md) | -| API reference | [`docs/reference/API_REFERENCE.md`](docs/reference/API_REFERENCE.md) + [`docs/reference/openapi.yaml`](docs/reference/openapi.yaml) | +| API reference | [`docs/reference/API_REFERENCE.md`](docs/reference/API_REFERENCE.md) + [`docs/openapi.yaml`](docs/openapi.yaml) | | Provider catalog (auto-generated) | [`docs/reference/PROVIDER_REFERENCE.md`](docs/reference/PROVIDER_REFERENCE.md) | | Tunnels | [`docs/ops/TUNNELS_GUIDE.md`](docs/ops/TUNNELS_GUIDE.md) | | Electron desktop | [`docs/guides/ELECTRON_GUIDE.md`](docs/guides/ELECTRON_GUIDE.md) | diff --git a/CHANGELOG.md b/CHANGELOG.md index 16797e93d4..097cbeeed9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1625,7 +1625,7 @@ And thank you to the OmniRoute community for the bug reports, reproductions, and `tests/e2e/traffic-inspector.spec.ts`, `tests/e2e/agent-bridge-traffic-cross.spec.ts` (skip-gated on CI by `RUN_AGENT_BRIDGE_E2E` / `RUN_TRAFFIC_INSPECTOR_E2E` / `RUN_CROSS_E2E`). - **Documentation** — `docs/frameworks/AGENTBRIDGE.md` and `docs/frameworks/TRAFFIC_INSPECTOR.md`; - `docs/architecture/REPOSITORY_MAP.md` updated; `docs/reference/openapi.yaml` updated with + `docs/architecture/REPOSITORY_MAP.md` updated; `docs/openapi.yaml` updated with ~28 new routes and 20+ new schemas. - **i18n:** translate Ukrainian (uk-UA) menu and UI strings, plus complete uk-UA UI coverage (#2981 / #2988 — thanks @Lion-killer) - **providers:** add SiliconFlow endpoint selector (#2975 — thanks @xz-dev) diff --git a/CLAUDE.md b/CLAUDE.md index 86763792a1..75df82dab0 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -311,7 +311,7 @@ connection continue serving other models. 4. Create 7 API endpoints under `src/app/api/services/{name}/` (`_lib.ts`, `install`, `start`, `stop`, `restart`, `update`, `status`, `auto-start`). All delegate errors through `createErrorResponse()`. The shared `logs` endpoint is already wired via `[name]/logs/route.ts`. 5. Verify `/api/services/` is in `LOCAL_ONLY_API_PREFIXES` in `src/server/authz/routeGuard.ts`; add a test asserting `isLocalOnlyPath()` returns `true` for the new prefix if you add one (hard rule #17). 6. Add a UI tab in `src/app/(dashboard)/dashboard/providers/services/tabs/` reusing `ServiceStatusCard`, `ServiceLifecycleButtons`, `ServiceLogsPanel`. -7. Document in `docs/frameworks/EMBEDDED-SERVICES.md` (update §1 service table + §4 API reference) and `docs/reference/openapi.yaml`. +7. Document in `docs/frameworks/EMBEDDED-SERVICES.md` (update §1 service table + §4 API reference) and `docs/openapi.yaml`. 8. Write tests: unit (`tests/unit/services/`), integration (`tests/integration/services/`, gated by `RUN_SERVICES_INT=1`), and update `docs/ops/RELEASE_CHECKLIST.md` smoke section. ### Adding a New Guardrail / Eval / Skill / Webhook event @@ -349,7 +349,7 @@ For any non-trivial change, read the matching deep-dive first: | Agent protocols (A2A / ACP / Cloud) | `docs/frameworks/AGENT_PROTOCOLS_GUIDE.md` | | MCP server | `docs/frameworks/MCP-SERVER.md` | | A2A server | `docs/frameworks/A2A-SERVER.md` | -| API reference + OpenAPI | `docs/reference/API_REFERENCE.md` + `docs/reference/openapi.yaml` | +| API reference + OpenAPI | `docs/reference/API_REFERENCE.md` + `docs/openapi.yaml` | | Provider catalog (auto-generated) | `docs/reference/PROVIDER_REFERENCE.md` | | Release flow | `docs/ops/RELEASE_CHECKLIST.md` | | Embedded services | `docs/frameworks/EMBEDDED-SERVICES.md` | diff --git a/README.md b/README.md index f28f1be3d4..03508d9da8 100644 --- a/README.md +++ b/README.md @@ -921,7 +921,7 @@ Compression: aggressive (~50%) → double your free quota · Cost: $0/mo | Document | Description | | ------------------------------------------------- | --------------------------------------------------- | | [API Reference](docs/reference/API_REFERENCE.md) | All endpoints with examples | -| [OpenAPI Spec](docs/reference/openapi.yaml) | OpenAPI 3.0 specification | +| [OpenAPI Spec](docs/openapi.yaml) | OpenAPI 3.0 specification | | [MCP Server](open-sse/mcp-server/README.md) | 87 MCP tools, IDE configs, Python/TS/Go clients | | [MCP Server Guide](docs/frameworks/MCP-SERVER.md) | MCP installation, transports, and tool reference | | [A2A Server](src/lib/a2a/README.md) | JSON-RPC 2.0 protocol, skills, streaming, task mgmt | diff --git a/bin/cli/api-commands/agent-skills.mjs b/bin/cli/api-commands/agent-skills.mjs index 34b7f9e8fa..233d318f8d 100644 --- a/bin/cli/api-commands/agent-skills.mjs +++ b/bin/cli/api-commands/agent-skills.mjs @@ -1,4 +1,4 @@ -// AUTO-GENERATED from docs/reference/openapi.yaml. Do not edit. +// AUTO-GENERATED from docs/openapi.yaml. Do not edit. import { apiFetch } from "../api.mjs"; import { emit } from "../output.mjs"; import { readFileSync } from "node:fs"; diff --git a/bin/cli/api-commands/agentbridge.mjs b/bin/cli/api-commands/agentbridge.mjs index 6622291383..6d51e57b21 100644 --- a/bin/cli/api-commands/agentbridge.mjs +++ b/bin/cli/api-commands/agentbridge.mjs @@ -1,4 +1,4 @@ -// AUTO-GENERATED from docs/reference/openapi.yaml. Do not edit. +// AUTO-GENERATED from docs/openapi.yaml. Do not edit. import { apiFetch } from "../api.mjs"; import { emit } from "../output.mjs"; import { readFileSync } from "node:fs"; diff --git a/bin/cli/api-commands/api-keys.mjs b/bin/cli/api-commands/api-keys.mjs index 445df84546..21a64437db 100644 --- a/bin/cli/api-commands/api-keys.mjs +++ b/bin/cli/api-commands/api-keys.mjs @@ -1,4 +1,4 @@ -// AUTO-GENERATED from docs/reference/openapi.yaml. Do not edit. +// AUTO-GENERATED from docs/openapi.yaml. Do not edit. import { apiFetch } from "../api.mjs"; import { emit } from "../output.mjs"; import { readFileSync } from "node:fs"; diff --git a/bin/cli/api-commands/audio.mjs b/bin/cli/api-commands/audio.mjs index 4465f65bc3..74c0b0fa35 100644 --- a/bin/cli/api-commands/audio.mjs +++ b/bin/cli/api-commands/audio.mjs @@ -1,4 +1,4 @@ -// AUTO-GENERATED from docs/reference/openapi.yaml. Do not edit. +// AUTO-GENERATED from docs/openapi.yaml. Do not edit. import { apiFetch } from "../api.mjs"; import { emit } from "../output.mjs"; import { readFileSync } from "node:fs"; diff --git a/bin/cli/api-commands/chat.mjs b/bin/cli/api-commands/chat.mjs index 9b6497beb6..4d99413b4e 100644 --- a/bin/cli/api-commands/chat.mjs +++ b/bin/cli/api-commands/chat.mjs @@ -1,4 +1,4 @@ -// AUTO-GENERATED from docs/reference/openapi.yaml. Do not edit. +// AUTO-GENERATED from docs/openapi.yaml. Do not edit. import { apiFetch } from "../api.mjs"; import { emit } from "../output.mjs"; import { readFileSync } from "node:fs"; diff --git a/bin/cli/api-commands/cli-tools.mjs b/bin/cli/api-commands/cli-tools.mjs index 40c879e825..49df272def 100644 --- a/bin/cli/api-commands/cli-tools.mjs +++ b/bin/cli/api-commands/cli-tools.mjs @@ -1,4 +1,4 @@ -// AUTO-GENERATED from docs/reference/openapi.yaml. Do not edit. +// AUTO-GENERATED from docs/openapi.yaml. Do not edit. import { apiFetch } from "../api.mjs"; import { emit } from "../output.mjs"; import { readFileSync } from "node:fs"; diff --git a/bin/cli/api-commands/cloud.mjs b/bin/cli/api-commands/cloud.mjs index e942307196..eaebb1f7cc 100644 --- a/bin/cli/api-commands/cloud.mjs +++ b/bin/cli/api-commands/cloud.mjs @@ -1,4 +1,4 @@ -// AUTO-GENERATED from docs/reference/openapi.yaml. Do not edit. +// AUTO-GENERATED from docs/openapi.yaml. Do not edit. import { apiFetch } from "../api.mjs"; import { emit } from "../output.mjs"; import { readFileSync } from "node:fs"; diff --git a/bin/cli/api-commands/combos.mjs b/bin/cli/api-commands/combos.mjs index 2c3647f94b..e4e4ff62f5 100644 --- a/bin/cli/api-commands/combos.mjs +++ b/bin/cli/api-commands/combos.mjs @@ -1,4 +1,4 @@ -// AUTO-GENERATED from docs/reference/openapi.yaml. Do not edit. +// AUTO-GENERATED from docs/openapi.yaml. Do not edit. import { apiFetch } from "../api.mjs"; import { emit } from "../output.mjs"; import { readFileSync } from "node:fs"; diff --git a/bin/cli/api-commands/compression.mjs b/bin/cli/api-commands/compression.mjs index 9f96e0f8b0..3eb6a0af2a 100644 --- a/bin/cli/api-commands/compression.mjs +++ b/bin/cli/api-commands/compression.mjs @@ -1,4 +1,4 @@ -// AUTO-GENERATED from docs/reference/openapi.yaml. Do not edit. +// AUTO-GENERATED from docs/openapi.yaml. Do not edit. import { apiFetch } from "../api.mjs"; import { emit } from "../output.mjs"; import { readFileSync } from "node:fs"; diff --git a/bin/cli/api-commands/embedded-services.mjs b/bin/cli/api-commands/embedded-services.mjs index 0baa58b8a7..e53f019977 100644 --- a/bin/cli/api-commands/embedded-services.mjs +++ b/bin/cli/api-commands/embedded-services.mjs @@ -1,4 +1,4 @@ -// AUTO-GENERATED from docs/reference/openapi.yaml. Do not edit. +// AUTO-GENERATED from docs/openapi.yaml. Do not edit. import { apiFetch } from "../api.mjs"; import { emit } from "../output.mjs"; import { readFileSync } from "node:fs"; diff --git a/bin/cli/api-commands/embeddings.mjs b/bin/cli/api-commands/embeddings.mjs index 8ae1235a88..67db58e434 100644 --- a/bin/cli/api-commands/embeddings.mjs +++ b/bin/cli/api-commands/embeddings.mjs @@ -1,4 +1,4 @@ -// AUTO-GENERATED from docs/reference/openapi.yaml. Do not edit. +// AUTO-GENERATED from docs/openapi.yaml. Do not edit. import { apiFetch } from "../api.mjs"; import { emit } from "../output.mjs"; import { readFileSync } from "node:fs"; diff --git a/bin/cli/api-commands/fallback.mjs b/bin/cli/api-commands/fallback.mjs index b769ec8032..d790e85f63 100644 --- a/bin/cli/api-commands/fallback.mjs +++ b/bin/cli/api-commands/fallback.mjs @@ -1,4 +1,4 @@ -// AUTO-GENERATED from docs/reference/openapi.yaml. Do not edit. +// AUTO-GENERATED from docs/openapi.yaml. Do not edit. import { apiFetch } from "../api.mjs"; import { emit } from "../output.mjs"; import { readFileSync } from "node:fs"; diff --git a/bin/cli/api-commands/images.mjs b/bin/cli/api-commands/images.mjs index 14cc850844..cd80da2507 100644 --- a/bin/cli/api-commands/images.mjs +++ b/bin/cli/api-commands/images.mjs @@ -1,4 +1,4 @@ -// AUTO-GENERATED from docs/reference/openapi.yaml. Do not edit. +// AUTO-GENERATED from docs/openapi.yaml. Do not edit. import { apiFetch } from "../api.mjs"; import { emit } from "../output.mjs"; import { readFileSync } from "node:fs"; diff --git a/bin/cli/api-commands/memory.mjs b/bin/cli/api-commands/memory.mjs index 6cf8f68679..4fb9151f1c 100644 --- a/bin/cli/api-commands/memory.mjs +++ b/bin/cli/api-commands/memory.mjs @@ -1,4 +1,4 @@ -// AUTO-GENERATED from docs/reference/openapi.yaml. Do not edit. +// AUTO-GENERATED from docs/openapi.yaml. Do not edit. import { apiFetch } from "../api.mjs"; import { emit } from "../output.mjs"; import { readFileSync } from "node:fs"; diff --git a/bin/cli/api-commands/messages.mjs b/bin/cli/api-commands/messages.mjs index 6618f31a9e..9aeb2135be 100644 --- a/bin/cli/api-commands/messages.mjs +++ b/bin/cli/api-commands/messages.mjs @@ -1,4 +1,4 @@ -// AUTO-GENERATED from docs/reference/openapi.yaml. Do not edit. +// AUTO-GENERATED from docs/openapi.yaml. Do not edit. import { apiFetch } from "../api.mjs"; import { emit } from "../output.mjs"; import { readFileSync } from "node:fs"; diff --git a/bin/cli/api-commands/models.mjs b/bin/cli/api-commands/models.mjs index 09d0fa0e8a..1cec5a1b72 100644 --- a/bin/cli/api-commands/models.mjs +++ b/bin/cli/api-commands/models.mjs @@ -1,4 +1,4 @@ -// AUTO-GENERATED from docs/reference/openapi.yaml. Do not edit. +// AUTO-GENERATED from docs/openapi.yaml. Do not edit. import { apiFetch } from "../api.mjs"; import { emit } from "../output.mjs"; import { readFileSync } from "node:fs"; diff --git a/bin/cli/api-commands/moderations.mjs b/bin/cli/api-commands/moderations.mjs index 0c716202f6..2c039f5fc1 100644 --- a/bin/cli/api-commands/moderations.mjs +++ b/bin/cli/api-commands/moderations.mjs @@ -1,4 +1,4 @@ -// AUTO-GENERATED from docs/reference/openapi.yaml. Do not edit. +// AUTO-GENERATED from docs/openapi.yaml. Do not edit. import { apiFetch } from "../api.mjs"; import { emit } from "../output.mjs"; import { readFileSync } from "node:fs"; diff --git a/bin/cli/api-commands/oauth.mjs b/bin/cli/api-commands/oauth.mjs index 1645bc3220..e21c7b7224 100644 --- a/bin/cli/api-commands/oauth.mjs +++ b/bin/cli/api-commands/oauth.mjs @@ -1,4 +1,4 @@ -// AUTO-GENERATED from docs/reference/openapi.yaml. Do not edit. +// AUTO-GENERATED from docs/openapi.yaml. Do not edit. import { apiFetch } from "../api.mjs"; import { emit } from "../output.mjs"; import { readFileSync } from "node:fs"; diff --git a/bin/cli/api-commands/playground.mjs b/bin/cli/api-commands/playground.mjs index 3866409833..d5b6ba018f 100644 --- a/bin/cli/api-commands/playground.mjs +++ b/bin/cli/api-commands/playground.mjs @@ -1,4 +1,4 @@ -// AUTO-GENERATED from docs/reference/openapi.yaml. Do not edit. +// AUTO-GENERATED from docs/openapi.yaml. Do not edit. import { apiFetch } from "../api.mjs"; import { emit } from "../output.mjs"; import { readFileSync } from "node:fs"; diff --git a/bin/cli/api-commands/pricing.mjs b/bin/cli/api-commands/pricing.mjs index b790e24665..4e67966a2a 100644 --- a/bin/cli/api-commands/pricing.mjs +++ b/bin/cli/api-commands/pricing.mjs @@ -1,4 +1,4 @@ -// AUTO-GENERATED from docs/reference/openapi.yaml. Do not edit. +// AUTO-GENERATED from docs/openapi.yaml. Do not edit. import { apiFetch } from "../api.mjs"; import { emit } from "../output.mjs"; import { readFileSync } from "node:fs"; diff --git a/bin/cli/api-commands/provider-nodes.mjs b/bin/cli/api-commands/provider-nodes.mjs index 1444f8d267..d9f51a23e3 100644 --- a/bin/cli/api-commands/provider-nodes.mjs +++ b/bin/cli/api-commands/provider-nodes.mjs @@ -1,4 +1,4 @@ -// AUTO-GENERATED from docs/reference/openapi.yaml. Do not edit. +// AUTO-GENERATED from docs/openapi.yaml. Do not edit. import { apiFetch } from "../api.mjs"; import { emit } from "../output.mjs"; import { readFileSync } from "node:fs"; diff --git a/bin/cli/api-commands/providers.mjs b/bin/cli/api-commands/providers.mjs index d0e56e2f84..f362860385 100644 --- a/bin/cli/api-commands/providers.mjs +++ b/bin/cli/api-commands/providers.mjs @@ -1,4 +1,4 @@ -// AUTO-GENERATED from docs/reference/openapi.yaml. Do not edit. +// AUTO-GENERATED from docs/openapi.yaml. Do not edit. import { apiFetch } from "../api.mjs"; import { emit } from "../output.mjs"; import { readFileSync } from "node:fs"; diff --git a/bin/cli/api-commands/quota.mjs b/bin/cli/api-commands/quota.mjs index 5f72dad905..13a0e44eeb 100644 --- a/bin/cli/api-commands/quota.mjs +++ b/bin/cli/api-commands/quota.mjs @@ -1,4 +1,4 @@ -// AUTO-GENERATED from docs/reference/openapi.yaml. Do not edit. +// AUTO-GENERATED from docs/openapi.yaml. Do not edit. import { apiFetch } from "../api.mjs"; import { emit } from "../output.mjs"; import { readFileSync } from "node:fs"; diff --git a/bin/cli/api-commands/rerank.mjs b/bin/cli/api-commands/rerank.mjs index 522999ede6..1f14b74ce1 100644 --- a/bin/cli/api-commands/rerank.mjs +++ b/bin/cli/api-commands/rerank.mjs @@ -1,4 +1,4 @@ -// AUTO-GENERATED from docs/reference/openapi.yaml. Do not edit. +// AUTO-GENERATED from docs/openapi.yaml. Do not edit. import { apiFetch } from "../api.mjs"; import { emit } from "../output.mjs"; import { readFileSync } from "node:fs"; diff --git a/bin/cli/api-commands/responses.mjs b/bin/cli/api-commands/responses.mjs index 45ee00080e..d4546a4c4c 100644 --- a/bin/cli/api-commands/responses.mjs +++ b/bin/cli/api-commands/responses.mjs @@ -1,4 +1,4 @@ -// AUTO-GENERATED from docs/reference/openapi.yaml. Do not edit. +// AUTO-GENERATED from docs/openapi.yaml. Do not edit. import { apiFetch } from "../api.mjs"; import { emit } from "../output.mjs"; import { readFileSync } from "node:fs"; diff --git a/bin/cli/api-commands/settings.mjs b/bin/cli/api-commands/settings.mjs index 2a2d5ee7b5..fd9758b9c2 100644 --- a/bin/cli/api-commands/settings.mjs +++ b/bin/cli/api-commands/settings.mjs @@ -1,4 +1,4 @@ -// AUTO-GENERATED from docs/reference/openapi.yaml. Do not edit. +// AUTO-GENERATED from docs/openapi.yaml. Do not edit. import { apiFetch } from "../api.mjs"; import { emit } from "../output.mjs"; import { readFileSync } from "node:fs"; diff --git a/bin/cli/api-commands/system.mjs b/bin/cli/api-commands/system.mjs index c0def12050..18a3b1bfab 100644 --- a/bin/cli/api-commands/system.mjs +++ b/bin/cli/api-commands/system.mjs @@ -1,4 +1,4 @@ -// AUTO-GENERATED from docs/reference/openapi.yaml. Do not edit. +// AUTO-GENERATED from docs/openapi.yaml. Do not edit. import { apiFetch } from "../api.mjs"; import { emit } from "../output.mjs"; import { readFileSync } from "node:fs"; diff --git a/bin/cli/api-commands/telemetry.mjs b/bin/cli/api-commands/telemetry.mjs index 68a22de0c1..173582dead 100644 --- a/bin/cli/api-commands/telemetry.mjs +++ b/bin/cli/api-commands/telemetry.mjs @@ -1,4 +1,4 @@ -// AUTO-GENERATED from docs/reference/openapi.yaml. Do not edit. +// AUTO-GENERATED from docs/openapi.yaml. Do not edit. import { apiFetch } from "../api.mjs"; import { emit } from "../output.mjs"; import { readFileSync } from "node:fs"; diff --git a/bin/cli/api-commands/traffic-inspector.mjs b/bin/cli/api-commands/traffic-inspector.mjs index 1572090b54..ea8f926f49 100644 --- a/bin/cli/api-commands/traffic-inspector.mjs +++ b/bin/cli/api-commands/traffic-inspector.mjs @@ -1,4 +1,4 @@ -// AUTO-GENERATED from docs/reference/openapi.yaml. Do not edit. +// AUTO-GENERATED from docs/openapi.yaml. Do not edit. import { apiFetch } from "../api.mjs"; import { emit } from "../output.mjs"; import { readFileSync } from "node:fs"; diff --git a/bin/cli/api-commands/translator.mjs b/bin/cli/api-commands/translator.mjs index 10c1a03e22..d2cdf24cb3 100644 --- a/bin/cli/api-commands/translator.mjs +++ b/bin/cli/api-commands/translator.mjs @@ -1,4 +1,4 @@ -// AUTO-GENERATED from docs/reference/openapi.yaml. Do not edit. +// AUTO-GENERATED from docs/openapi.yaml. Do not edit. import { apiFetch } from "../api.mjs"; import { emit } from "../output.mjs"; import { readFileSync } from "node:fs"; diff --git a/bin/cli/api-commands/usage.mjs b/bin/cli/api-commands/usage.mjs index 40a8b1afb1..b00d35dd2b 100644 --- a/bin/cli/api-commands/usage.mjs +++ b/bin/cli/api-commands/usage.mjs @@ -1,4 +1,4 @@ -// AUTO-GENERATED from docs/reference/openapi.yaml. Do not edit. +// AUTO-GENERATED from docs/openapi.yaml. Do not edit. import { apiFetch } from "../api.mjs"; import { emit } from "../output.mjs"; import { readFileSync } from "node:fs"; diff --git a/docs/README.md b/docs/README.md index 745b6be723..4ca2ffbefe 100644 --- a/docs/README.md +++ b/docs/README.md @@ -56,7 +56,7 @@ Lookup material — API surface, environment variables, CLI flags, provider cata - [API_REFERENCE.md](reference/API_REFERENCE.md) — REST API endpoints and shapes. - [PROVIDER_REFERENCE.md](reference/PROVIDER_REFERENCE.md) — auto-generated provider catalog. -- [openapi.yaml](reference/openapi.yaml) — OpenAPI 3.1 spec for the public API. +- [openapi.yaml](openapi.yaml) — OpenAPI 3.1 spec for the public API. - [ENVIRONMENT.md](reference/ENVIRONMENT.md) — environment variables reference. - [CLI-TOOLS.md](reference/CLI-TOOLS.md) — bundled CLI commands. - [FREE_TIERS.md](reference/FREE_TIERS.md) — free-tier LLM provider directory. diff --git a/docs/architecture/CODEBASE_DOCUMENTATION.md b/docs/architecture/CODEBASE_DOCUMENTATION.md index 12c822ade3..4340edd251 100644 --- a/docs/architecture/CODEBASE_DOCUMENTATION.md +++ b/docs/architecture/CODEBASE_DOCUMENTATION.md @@ -755,7 +755,7 @@ See [RESILIENCE_GUIDE.md](./RESILIENCE_GUIDE.md) and the dedicated section in 4. If management-only: add the path to `src/shared/constants/publicApiRoutes.ts` (denylist for the public API surface). 5. Add tests under `tests/unit/`. -6. Update `docs/reference/API_REFERENCE.md` and `docs/reference/openapi.yaml`. +6. Update `docs/reference/API_REFERENCE.md` and `docs/openapi.yaml`. ### Add a new DB module diff --git a/docs/architecture/QUALITY_GATES.md b/docs/architecture/QUALITY_GATES.md index cd41b1ad2c..89b3890658 100644 --- a/docs/architecture/QUALITY_GATES.md +++ b/docs/architecture/QUALITY_GATES.md @@ -139,7 +139,7 @@ These run on a cron schedule (and `workflow_dispatch`), never on PRs. All are ad | `nightly-property` | fast-check property tests with a random seed + high run count | **Advisory** | | `nightly-resilience` | heap-growth gate, chaos fault-injection, k6 load/soak | **Advisory** | | `nightly-llm-security` | promptfoo injection guard (block mode) + garak probes (skipped without a provider secret) | **Advisory** | -| `nightly-schemathesis` | OpenAPI contract fuzzing (schemathesis) against a live OmniRoute using `docs/reference/openapi.yaml` — surfaces spec violations / unhandled 500s (Fase 8 B.4) | **Advisory** | +| `nightly-schemathesis` | OpenAPI contract fuzzing (schemathesis) against a live OmniRoute using `docs/openapi.yaml` — surfaces spec violations / unhandled 500s (Fase 8 B.4) | **Advisory** | --- diff --git a/docs/frameworks/AGENT-SKILLS.md b/docs/frameworks/AGENT-SKILLS.md index ab787b9c62..b76e0a7e02 100644 --- a/docs/frameworks/AGENT-SKILLS.md +++ b/docs/frameworks/AGENT-SKILLS.md @@ -31,7 +31,7 @@ src/shared/constants/agentSkills.ts — 42-entry curated list (name/desc/cate src/lib/agentSkills/ catalog.ts — getCatalog(), getSkillById(), filterCatalog(), computeCoverage() generator.ts — generateAgentSkills() writes SKILL.md to skills/{id}/ - openapiParser.ts — extracts REST endpoints from docs/reference/openapi.yaml + openapiParser.ts — extracts REST endpoints from docs/openapi.yaml cliRegistryParser.ts — extracts CLI subcommands from bin/cli-registry.ts schemas.ts — Zod schemas: AgentSkillSchema, SkillCoverageSchema, etc. types.ts — TypeScript interfaces: AgentSkill, SkillCoverage, etc. diff --git a/docs/frameworks/AGENTBRIDGE.md b/docs/frameworks/AGENTBRIDGE.md index ecf024be0e..29cc6b8ce5 100644 --- a/docs/frameworks/AGENTBRIDGE.md +++ b/docs/frameworks/AGENTBRIDGE.md @@ -448,7 +448,7 @@ Base path: `/api/tools/agent-bridge/` | POST | `/api/tools/agent-bridge/upstream-ca/test` | Validate-only (dry-run) an upstream CA path — does not persist | | GET / POST / DELETE | `/api/tools/agent-bridge/tproxy` | TPROXY transparent-decrypt capture mode — see [`docs/security/MITM-TPROXY-DECRYPT.md`](../security/MITM-TPROXY-DECRYPT.md) | -Full OpenAPI schemas: `docs/reference/openapi.yaml` → tag `AgentBridge`. +Full OpenAPI schemas: `docs/openapi.yaml` → tag `AgentBridge`. --- diff --git a/docs/frameworks/EMBEDDED-SERVICES.md b/docs/frameworks/EMBEDDED-SERVICES.md index 25daa4f7c9..6a8042c990 100644 --- a/docs/frameworks/EMBEDDED-SERVICES.md +++ b/docs/frameworks/EMBEDDED-SERVICES.md @@ -623,7 +623,7 @@ If the embedded service exposes an OpenAI-compatible `/v1/chat/completions` endp table in §1 and any new endpoints to §4. 2. Add unit tests in `tests/unit/services/` (lifecycle, installer, API shape). 3. Add integration test in `tests/integration/services/` (behind `RUN_SERVICES_INT=1`). -4. Update `docs/reference/openapi.yaml` with the new endpoints. +4. Update `docs/openapi.yaml` with the new endpoints. --- @@ -791,5 +791,5 @@ the most recent lines within the `tail` limit. Logs are not persisted to disk un - `docs/security/ROUTE_GUARD_TIERS.md` — LOCAL_ONLY tier details - `docs/architecture/CODEBASE_DOCUMENTATION.md` — §3.2 Embedded Services module mapping - `docs/architecture/ARCHITECTURE.md` — system-level context -- `docs/reference/openapi.yaml` — machine-readable endpoint definitions +- `docs/openapi.yaml` — machine-readable endpoint definitions - `CLAUDE.md` §"Adding a New Embedded Service" — quick-reference checklist diff --git a/docs/frameworks/TRAFFIC_INSPECTOR.md b/docs/frameworks/TRAFFIC_INSPECTOR.md index eff615c830..9b88442ca0 100644 --- a/docs/frameworks/TRAFFIC_INSPECTOR.md +++ b/docs/frameworks/TRAFFIC_INSPECTOR.md @@ -477,4 +477,4 @@ Base path: `/api/tools/traffic-inspector/` |--------|------|-------------| | POST | `/internal/ingest` | Accepts intercepted request from `server.cjs` passthrough path; requires `INSPECTOR_INTERNAL_INGEST_TOKEN` header | -Full OpenAPI schemas: `docs/reference/openapi.yaml` → tag `Traffic Inspector`. +Full OpenAPI schemas: `docs/openapi.yaml` → tag `Traffic Inspector`. diff --git a/docs/guides/REMOTE-MODE.md b/docs/guides/REMOTE-MODE.md index dbec59c0c7..63f0bb952e 100644 --- a/docs/guides/REMOTE-MODE.md +++ b/docs/guides/REMOTE-MODE.md @@ -284,4 +284,4 @@ omniroute contexts remove 192-168-0-15 --yes # drop the local context (even if | POST | `/api/cli/tokens` | access token | admin | | DELETE | `/api/cli/tokens/:id` | access token | admin | -See [openapi.yaml](../reference/openapi.yaml) for full schemas. +See [openapi.yaml](../openapi.yaml) for full schemas. diff --git a/docs/reference/openapi.yaml b/docs/openapi.yaml similarity index 100% rename from docs/reference/openapi.yaml rename to docs/openapi.yaml diff --git a/docs/ops/DOCUMENTATION_AUDIT_REPORT.md b/docs/ops/DOCUMENTATION_AUDIT_REPORT.md index 8155dd5a21..7204713364 100644 --- a/docs/ops/DOCUMENTATION_AUDIT_REPORT.md +++ b/docs/ops/DOCUMENTATION_AUDIT_REPORT.md @@ -84,7 +84,7 @@ lastUpdated: 2026-06-13 - **Como funciona:** `docs//*.md` → `source.config.ts` (globs) → `.source/server.ts` (gerado) → `src/lib/source.ts` → `src/app/docs/layout.tsx` (sidebar = `pageTree` dos `meta.json`) → `[...slug]/page.tsx`. **60 docs em inglês** entram no site. - **Navegação curada por `meta.json`** → arquivo novo em `/docs` **não aparece** até ser adicionado manualmente ao `meta.json` da seção. Hoje há 4 arquivos importados mas fora da sidebar (acima). - **i18n no site:** `[...slug]/page.tsx` lê cookie `NEXT_LOCALE`; se ≠ en, tenta `docs/i18n//docs//.md` via `marked.parse()`, com fallback para o MDX inglês. Seletor: `LanguageSelector.tsx` (40 idiomas em `LANGUAGES`). -- **API Explorer:** `openapi.generated.ts` é gerado por `scripts/docs/gen-openapi-module.mjs` a partir de `docs/reference/openapi.yaml` no `prebuild:docs`. +- **API Explorer:** `openapi.generated.ts` é gerado por `scripts/docs/gen-openapi-module.mjs` a partir de `docs/openapi.yaml` no `prebuild:docs`. - **Riscos de drift:** (a) `meta.json` manual; (b) traduções não atualizam quando o inglês muda; (c) `openapi.yaml` precisa de regen; (d) `LANGUAGES` no app diz 40, config diz 42 → **divergência app vs config**. ### 2.4 Wiki do GitHub (`/wiki`) — **mais defasada de todas** diff --git a/docs/ops/RELEASE_CHECKLIST.md b/docs/ops/RELEASE_CHECKLIST.md index c0e7546997..fffe182769 100644 --- a/docs/ops/RELEASE_CHECKLIST.md +++ b/docs/ops/RELEASE_CHECKLIST.md @@ -52,7 +52,7 @@ npm run test:e2e # optional but recommended - [ ] Manually review CHANGELOG.md and clean up commit messages if needed - [ ] Ensure the latest semver section in `CHANGELOG.md` equals `package.json` version - [ ] Keep `## [Unreleased]` as the first changelog section for upcoming work -- [ ] Update `docs/reference/openapi.yaml` → `info.version` must equal `package.json` version +- [ ] Update `docs/openapi.yaml` → `info.version` must equal `package.json` version ### Code Quality @@ -104,7 +104,7 @@ Breaking changes: add `BREAKING CHANGE:` footer or `!` after the scope (e.g. `fe - [ ] `docs/guides/TROUBLESHOOTING.md` reviewed for env var and operational drift - [ ] If `.env.example` changed: `docs/reference/ENVIRONMENT.md` updated - [ ] If new feature has a UI: `docs/guides/USER_GUIDE.md` mentions it -- [ ] If new feature has API: `docs/reference/API_REFERENCE.md` + `docs/reference/openapi.yaml` updated +- [ ] If new feature has API: `docs/reference/API_REFERENCE.md` + `docs/openapi.yaml` updated - [ ] If new feature is a module: dedicated `docs/.md` exists - [ ] If breaking change: `docs/guides/TROUBLESHOOTING.md` has migration note diff --git a/docs/security/ROUTE_GUARD_TIERS.md b/docs/security/ROUTE_GUARD_TIERS.md index 4a4329eed5..da7eb1d0c2 100644 --- a/docs/security/ROUTE_GUARD_TIERS.md +++ b/docs/security/ROUTE_GUARD_TIERS.md @@ -136,7 +136,7 @@ expired DB doesn't silently downgrade to "deny". ## Documenting Security Tiers in OpenAPI -When adding a new route to `docs/reference/openapi.yaml`, apply the corresponding +When adding a new route to `docs/openapi.yaml`, apply the corresponding vendor extension if the route is classified by `routeGuard.ts`: | routeGuard.ts classification | YAML annotation | Enforcement | diff --git a/public/openapi.yaml b/public/openapi.yaml new file mode 100644 index 0000000000..ca00d23623 --- /dev/null +++ b/public/openapi.yaml @@ -0,0 +1,6690 @@ +openapi: 3.1.0 +info: + title: OmniRoute API + version: 3.8.35 + 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/{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 + ` ... ` 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 + +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 + - strict-random + - auto + - lkgp + - context-optimized + default: priority + nodes: + type: array + items: + type: object + properties: + connectionId: + type: string + weight: + type: integer + priority: + type: integer diff --git a/scripts/build/pack-artifact-policy.ts b/scripts/build/pack-artifact-policy.ts index b7df9b321e..53331d35d0 100644 --- a/scripts/build/pack-artifact-policy.ts +++ b/scripts/build/pack-artifact-policy.ts @@ -34,7 +34,7 @@ export const APP_STAGING_REMOVAL_PATHS: string[] = [ export const APP_STAGING_ALLOWED_EXACT_PATHS: string[] = [ ".env.example", "BUILD_SHA", - "docs/reference/openapi.yaml", + "docs/openapi.yaml", "http-method-guard.cjs", "open-sse/mcp-server/server.js", // LLMLingua ONNX worker — esbuild'd standalone .js spawned via worker_threads diff --git a/scripts/check/check-docs-sync.mjs b/scripts/check/check-docs-sync.mjs index c983a26c3b..bb704bbc5f 100644 --- a/scripts/check/check-docs-sync.mjs +++ b/scripts/check/check-docs-sync.mjs @@ -5,7 +5,7 @@ import path from "node:path"; const cwd = process.cwd(); const packageJsonPath = path.resolve(cwd, "package.json"); -const openApiPath = path.resolve(cwd, "docs/reference/openapi.yaml"); +const openApiPath = path.resolve(cwd, "docs/openapi.yaml"); const changelogPath = path.resolve(cwd, "CHANGELOG.md"); const llmPath = path.resolve(cwd, "llm.txt"); const i18nDocsPath = path.resolve(cwd, "docs/i18n"); @@ -209,7 +209,7 @@ try { const openApiVersion = extractOpenApiVersion(readText(openApiPath)); if (!openApiVersion) { - fail("could not extract docs/reference/openapi.yaml info.version"); + fail("could not extract docs/openapi.yaml info.version"); } else if (openApiVersion !== packageVersion) { fail(`OpenAPI version (${openApiVersion}) differs from package.json (${packageVersion})`); } else { diff --git a/scripts/check/check-fabricated-docs.mjs b/scripts/check/check-fabricated-docs.mjs index 02ba62ac33..53f96413e9 100644 --- a/scripts/check/check-fabricated-docs.mjs +++ b/scripts/check/check-fabricated-docs.mjs @@ -358,7 +358,7 @@ const ENDPOINT_ALLOWLIST = new Set([ /** Doc files to skip (auto-generated, vendored, or third-party). */ const SKIP_DOC_FILES = new Set([ "docs/reference/PROVIDER_REFERENCE.md", // auto-generated from providers.ts - "docs/reference/openapi.yaml", + "docs/openapi.yaml", "docs/i18n", // translations — separate workflow // Point-in-time documentation audit (v3.8.24): intentionally references drift, // counts, and not-yet-existing files as part of documenting them — not living docs. diff --git a/scripts/check/check-openapi-breaking.mjs b/scripts/check/check-openapi-breaking.mjs index 168f5a07bd..df66545357 100644 --- a/scripts/check/check-openapi-breaking.mjs +++ b/scripts/check/check-openapi-breaking.mjs @@ -2,7 +2,7 @@ // scripts/check/check-openapi-breaking.mjs // Catraca de breaking-change na API pública (Fase 8 B.4 — backlog opcional). // -// Diffa a spec do PR (docs/reference/openapi.yaml na working tree = HEAD) contra +// Diffa a spec do PR (docs/openapi.yaml na working tree = HEAD) contra // a MESMA spec no branch base, via `oasdiff breaking`. Pega regressões de contrato: // endpoint removido, parâmetro novo obrigatório, campo de resposta removido, enum // estreitado, etc. — mudanças que quebram clientes existentes. @@ -32,7 +32,7 @@ // • CI passa BASE_REF=${{ github.base_ref }} (ex.: "release/vX.Y.Z"). // • Local: default derivado da versão do package.json (releaseBranchForVersion), // ex.: package 3.8.29 → "origin/release/v3.8.29" — nunca fica stale entre ciclos. -// A spec base é extraída com `git show :docs/reference/openapi.yaml`. +// A spec base é extraída com `git show :docs/openapi.yaml`. // // Uso: // node scripts/check/check-openapi-breaking.mjs @@ -52,8 +52,8 @@ const QUIET = process.argv.includes("--quiet"); const PRINT_JSON = process.argv.includes("--json"); const RATCHET = process.argv.includes("--ratchet"); -const SPEC_REL = "docs/reference/openapi.yaml"; -const SPEC_PATH = path.join(ROOT, "docs", "reference", "openapi.yaml"); +const SPEC_REL = "docs/openapi.yaml"; +const SPEC_PATH = path.join(ROOT, "docs", "openapi.yaml"); /** * Deriva o branch base de release a partir de uma versão semver diff --git a/scripts/check/check-openapi-coverage.mjs b/scripts/check/check-openapi-coverage.mjs index 0476107ea8..cc66d0fc6d 100644 --- a/scripts/check/check-openapi-coverage.mjs +++ b/scripts/check/check-openapi-coverage.mjs @@ -13,7 +13,7 @@ import * as yaml from "js-yaml"; const ROOT = process.cwd(); const API_ROOT = path.join(ROOT, "src", "app", "api"); -const OPENAPI_PATH = path.join(ROOT, "docs", "reference", "openapi.yaml"); +const OPENAPI_PATH = path.join(ROOT, "docs", "openapi.yaml"); // Floor recorded on 2026-05-26 for release/v3.8.4: 137/365 routes documented. // The original ≥99% target tracks the OpenAPI audit follow-up (#2701); // until the backlog (services, free-proxies, relay-tokens, key-groups, diff --git a/scripts/check/check-openapi-routes.mjs b/scripts/check/check-openapi-routes.mjs index 7f8b8b4923..db4db2a702 100644 --- a/scripts/check/check-openapi-routes.mjs +++ b/scripts/check/check-openapi-routes.mjs @@ -1,6 +1,6 @@ #!/usr/bin/env node // scripts/check/check-openapi-routes.mjs -// Gate anti-alucinação (docs): toda `path` documentada em docs/reference/openapi.yaml +// Gate anti-alucinação (docs): toda `path` documentada em docs/openapi.yaml // deve resolver para um route.ts real em src/app/api/. Pega endpoint INVENTADO/obsoleto // na spec (a IA escreve docs descrevendo rota que não existe). Complementa // check-openapi-coverage.mjs (que mede a direção inversa: % de rotas documentadas). @@ -14,7 +14,7 @@ import { assertNoStale } from "./lib/allowlist.mjs"; const ROOT = process.cwd(); const API_ROOT = path.join(ROOT, "src", "app", "api"); -const OPENAPI_PATH = path.join(ROOT, "docs", "reference", "openapi.yaml"); +const OPENAPI_PATH = path.join(ROOT, "docs", "openapi.yaml"); // Entradas da spec sem rota real, congeladas para triagem (catraca: bloqueia NOVAS). export const KNOWN_STALE_SPEC = new Set([ diff --git a/scripts/check/check-openapi-security-tiers.mjs b/scripts/check/check-openapi-security-tiers.mjs index c2acfd6d23..812e4a0c1b 100644 --- a/scripts/check/check-openapi-security-tiers.mjs +++ b/scripts/check/check-openapi-security-tiers.mjs @@ -11,7 +11,7 @@ import path from "node:path"; import * as yaml from "js-yaml"; const ROOT = process.cwd(); -const OPENAPI_PATH = path.join(ROOT, "docs", "reference", "openapi.yaml"); +const OPENAPI_PATH = path.join(ROOT, "docs", "openapi.yaml"); const ROUTE_GUARD_PATH = path.join(ROOT, "src", "server", "authz", "routeGuard.ts"); function parseStringArray(match) { diff --git a/scripts/cli/generate-api-commands.mjs b/scripts/cli/generate-api-commands.mjs index ef4ce7d3a3..b937ceb389 100644 --- a/scripts/cli/generate-api-commands.mjs +++ b/scripts/cli/generate-api-commands.mjs @@ -10,7 +10,7 @@ import * as yaml from "js-yaml"; const __dirname = dirname(fileURLToPath(import.meta.url)); const ROOT = join(__dirname, "..", ".."); -const SPEC_PATH = process.env.OPENAPI_SPEC || join(ROOT, "docs/reference/openapi.yaml"); +const SPEC_PATH = process.env.OPENAPI_SPEC || join(ROOT, "docs/openapi.yaml"); const OUT_DIR = join(ROOT, "bin/cli/api-commands"); // Operations already covered by hand-crafted commands — skip in generated output. diff --git a/scripts/docs/gen-openapi-module.mjs b/scripts/docs/gen-openapi-module.mjs index 785ee055c2..184ec404e0 100644 --- a/scripts/docs/gen-openapi-module.mjs +++ b/scripts/docs/gen-openapi-module.mjs @@ -1,6 +1,6 @@ #!/usr/bin/env node /** - * gen-openapi-module.mjs — build helper that reads docs/reference/openapi.yaml, + * gen-openapi-module.mjs — build helper that reads docs/openapi.yaml, * flattens the path/method matrix, and emits * src/app/docs/lib/openapi.generated.ts so the Api Explorer client can * iterate endpoints without parsing YAML at runtime. @@ -23,7 +23,7 @@ import * as yaml from "js-yaml"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const ROOT = path.resolve(__dirname, "..", ".."); -const OPENAPI_PATH = path.join(ROOT, "docs", "reference", "openapi.yaml"); +const OPENAPI_PATH = path.join(ROOT, "docs", "openapi.yaml"); const OUT_PATH = path.join(ROOT, "src", "app", "docs", "lib", "openapi.generated.ts"); const HTTP_METHODS = ["get", "post", "put", "delete", "patch", "options", "head"]; @@ -108,7 +108,7 @@ async function main() { const header = `// AUTO-GENERATED by scripts/docs/gen-openapi-module.mjs — DO NOT EDIT MANUALLY // Regenerate with: node scripts/docs/gen-openapi-module.mjs // -// Source of truth: docs/reference/openapi.yaml +// Source of truth: docs/openapi.yaml // // The Api Explorer consumes \`OPENAPI_ENDPOINTS\`; the spec metadata // (\`OPENAPI_VERSION\`, \`OPENAPI_TITLE\`) is surfaced in the page header. diff --git a/scripts/quality/collect-metrics.mjs b/scripts/quality/collect-metrics.mjs index 7baa98fb65..eaa7b6bc8d 100644 --- a/scripts/quality/collect-metrics.mjs +++ b/scripts/quality/collect-metrics.mjs @@ -119,7 +119,7 @@ function coverageByModule() { // 4) OpenAPI coverage: percentage of implemented routes documented in openapi.yaml function openapiCoverage() { const API_ROOT = path.join(cwd, "src", "app", "api"); - const OPENAPI_PATH = path.join(cwd, "docs", "reference", "openapi.yaml"); + const OPENAPI_PATH = path.join(cwd, "docs", "openapi.yaml"); if (!fs.existsSync(API_ROOT) || !fs.existsSync(OPENAPI_PATH)) return; function collectRoutePaths(dir) { diff --git a/skills/omni-agents-a2a/SKILL.md b/skills/omni-agents-a2a/SKILL.md index b2d3c5788b..4dcf94ed5c 100644 --- a/skills/omni-agents-a2a/SKILL.md +++ b/skills/omni-agents-a2a/SKILL.md @@ -17,7 +17,7 @@ All requests require a valid Bearer token or session cookie. Obtain a token via _No endpoints mapped for this area yet._ ## Payloads -See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/reference/openapi.yaml` for detailed request/response schemas. +See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/openapi.yaml` for detailed request/response schemas. diff --git a/skills/omni-api-keys/SKILL.md b/skills/omni-api-keys/SKILL.md index c6c2a2b374..a2563b0ea2 100644 --- a/skills/omni-api-keys/SKILL.md +++ b/skills/omni-api-keys/SKILL.md @@ -45,4 +45,4 @@ curl -X DELETE https://localhost:20128/api/keys/{id} \ ## Payloads -See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/reference/openapi.yaml` for detailed request/response schemas. +See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/openapi.yaml` for detailed request/response schemas. diff --git a/skills/omni-auth/SKILL.md b/skills/omni-auth/SKILL.md index 67a75981b3..f010763008 100644 --- a/skills/omni-auth/SKILL.md +++ b/skills/omni-auth/SKILL.md @@ -38,7 +38,7 @@ curl -X POST https://localhost:20128/api/auth/logout \ ## Payloads -See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/reference/openapi.yaml` for detailed request/response schemas. +See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/openapi.yaml` for detailed request/response schemas. diff --git a/skills/omni-budget/SKILL.md b/skills/omni-budget/SKILL.md index d3f4f9f6ac..09744616d2 100644 --- a/skills/omni-budget/SKILL.md +++ b/skills/omni-budget/SKILL.md @@ -36,4 +36,4 @@ curl -X POST https://localhost:20128/api/rate-limit \ ## Payloads -See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/reference/openapi.yaml` for detailed request/response schemas. +See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/openapi.yaml` for detailed request/response schemas. diff --git a/skills/omni-cache/SKILL.md b/skills/omni-cache/SKILL.md index 79a15c841b..e0fb260d1c 100644 --- a/skills/omni-cache/SKILL.md +++ b/skills/omni-cache/SKILL.md @@ -54,4 +54,4 @@ curl -X DELETE https://localhost:20128/api/cache/stats \ ## Payloads -See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/reference/openapi.yaml` for detailed request/response schemas. +See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/openapi.yaml` for detailed request/response schemas. diff --git a/skills/omni-cli-tools/SKILL.md b/skills/omni-cli-tools/SKILL.md index 1755add8b4..ff468040af 100644 --- a/skills/omni-cli-tools/SKILL.md +++ b/skills/omni-cli-tools/SKILL.md @@ -317,4 +317,4 @@ curl -X DELETE https://localhost:20128/api/cli-tools/openclaw-settings \ ## Payloads -See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/reference/openapi.yaml` for detailed request/response schemas. +See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/openapi.yaml` for detailed request/response schemas. diff --git a/skills/omni-combos-routing/SKILL.md b/skills/omni-combos-routing/SKILL.md index 315d46cd59..4517bb6d47 100644 --- a/skills/omni-combos-routing/SKILL.md +++ b/skills/omni-combos-routing/SKILL.md @@ -109,7 +109,7 @@ curl -X DELETE https://localhost:20128/api/fallback/chains \ ## Payloads -See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/reference/openapi.yaml` for detailed request/response schemas. +See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/openapi.yaml` for detailed request/response schemas. diff --git a/skills/omni-compression/SKILL.md b/skills/omni-compression/SKILL.md index 9addc4d4a3..eb89f08ccf 100644 --- a/skills/omni-compression/SKILL.md +++ b/skills/omni-compression/SKILL.md @@ -45,7 +45,7 @@ curl https://localhost:20128/api/compression/rules \ ## Payloads -See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/reference/openapi.yaml` for detailed request/response schemas. +See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/openapi.yaml` for detailed request/response schemas. diff --git a/skills/omni-context-rtk/SKILL.md b/skills/omni-context-rtk/SKILL.md index 34f24d19d6..8cb4d8193f 100644 --- a/skills/omni-context-rtk/SKILL.md +++ b/skills/omni-context-rtk/SKILL.md @@ -65,4 +65,4 @@ curl https://localhost:20128/api/context/rtk/raw-output/{id} \ ## Payloads -See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/reference/openapi.yaml` for detailed request/response schemas. +See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/openapi.yaml` for detailed request/response schemas. diff --git a/skills/omni-db-backups/SKILL.md b/skills/omni-db-backups/SKILL.md index e483f997d0..93fe053482 100644 --- a/skills/omni-db-backups/SKILL.md +++ b/skills/omni-db-backups/SKILL.md @@ -17,4 +17,4 @@ All requests require a valid Bearer token or session cookie. Obtain a token via _No endpoints mapped for this area yet._ ## Payloads -See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/reference/openapi.yaml` for detailed request/response schemas. +See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/openapi.yaml` for detailed request/response schemas. diff --git a/skills/omni-inference/SKILL.md b/skills/omni-inference/SKILL.md index 488bd70b27..8c9e0e0a42 100644 --- a/skills/omni-inference/SKILL.md +++ b/skills/omni-inference/SKILL.md @@ -210,7 +210,7 @@ curl https://localhost:20128/api/v1/providers/{provider}/models \ ## Payloads -See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/reference/openapi.yaml` for detailed request/response schemas. +See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/openapi.yaml` for detailed request/response schemas. diff --git a/skills/omni-mcp/SKILL.md b/skills/omni-mcp/SKILL.md index 68aeb1b7a7..e65e4795c8 100644 --- a/skills/omni-mcp/SKILL.md +++ b/skills/omni-mcp/SKILL.md @@ -17,7 +17,7 @@ All requests require a valid Bearer token or session cookie. Obtain a token via _No endpoints mapped for this area yet._ ## Payloads -See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/reference/openapi.yaml` for detailed request/response schemas. +See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/openapi.yaml` for detailed request/response schemas. diff --git a/skills/omni-models/SKILL.md b/skills/omni-models/SKILL.md index 9c7706623b..a86ac9fd79 100644 --- a/skills/omni-models/SKILL.md +++ b/skills/omni-models/SKILL.md @@ -56,4 +56,4 @@ curl https://localhost:20128/api/models/catalog \ ## Payloads -See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/reference/openapi.yaml` for detailed request/response schemas. +See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/openapi.yaml` for detailed request/response schemas. diff --git a/skills/omni-providers/SKILL.md b/skills/omni-providers/SKILL.md index 0dd1dfab38..1856ca0ff8 100644 --- a/skills/omni-providers/SKILL.md +++ b/skills/omni-providers/SKILL.md @@ -176,7 +176,7 @@ curl https://localhost:20128/api/provider-models \ ## Payloads -See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/reference/openapi.yaml` for detailed request/response schemas. +See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/openapi.yaml` for detailed request/response schemas. ## Additional endpoints diff --git a/skills/omni-proxies/SKILL.md b/skills/omni-proxies/SKILL.md index aaac2fd8c8..ea6860effe 100644 --- a/skills/omni-proxies/SKILL.md +++ b/skills/omni-proxies/SKILL.md @@ -17,4 +17,4 @@ All requests require a valid Bearer token or session cookie. Obtain a token via _No endpoints mapped for this area yet._ ## Payloads -See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/reference/openapi.yaml` for detailed request/response schemas. +See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/openapi.yaml` for detailed request/response schemas. diff --git a/skills/omni-resilience/SKILL.md b/skills/omni-resilience/SKILL.md index ecacdb7a4f..b1a4b8f7d2 100644 --- a/skills/omni-resilience/SKILL.md +++ b/skills/omni-resilience/SKILL.md @@ -27,7 +27,7 @@ curl https://localhost:20128/api/monitoring/health \ ## Payloads -See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/reference/openapi.yaml` for detailed request/response schemas. +See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/openapi.yaml` for detailed request/response schemas. diff --git a/skills/omni-settings/SKILL.md b/skills/omni-settings/SKILL.md index 778b5d7a25..fc9bcae210 100644 --- a/skills/omni-settings/SKILL.md +++ b/skills/omni-settings/SKILL.md @@ -219,4 +219,4 @@ curl https://localhost:20128/api/tags \ ## Payloads -See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/reference/openapi.yaml` for detailed request/response schemas. +See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/openapi.yaml` for detailed request/response schemas. diff --git a/skills/omni-sync-cloud/SKILL.md b/skills/omni-sync-cloud/SKILL.md index 474c33781a..d6d7c5d41a 100644 --- a/skills/omni-sync-cloud/SKILL.md +++ b/skills/omni-sync-cloud/SKILL.md @@ -95,4 +95,4 @@ curl -X POST https://localhost:20128/api/sync/initialize \ ## Payloads -See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/reference/openapi.yaml` for detailed request/response schemas. +See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/openapi.yaml` for detailed request/response schemas. diff --git a/skills/omni-tunnels/SKILL.md b/skills/omni-tunnels/SKILL.md index 669d3c80a8..19cc2725a6 100644 --- a/skills/omni-tunnels/SKILL.md +++ b/skills/omni-tunnels/SKILL.md @@ -17,4 +17,4 @@ All requests require a valid Bearer token or session cookie. Obtain a token via _No endpoints mapped for this area yet._ ## Payloads -See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/reference/openapi.yaml` for detailed request/response schemas. +See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/openapi.yaml` for detailed request/response schemas. diff --git a/skills/omni-usage-logs/SKILL.md b/skills/omni-usage-logs/SKILL.md index 5010258ff1..120e6d8106 100644 --- a/skills/omni-usage-logs/SKILL.md +++ b/skills/omni-usage-logs/SKILL.md @@ -112,4 +112,4 @@ curl -X POST https://localhost:20128/api/usage/budget \ ## Payloads -See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/reference/openapi.yaml` for detailed request/response schemas. +See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/openapi.yaml` for detailed request/response schemas. diff --git a/skills/omni-version-manager/SKILL.md b/skills/omni-version-manager/SKILL.md index 29ce881028..167b5815a7 100644 --- a/skills/omni-version-manager/SKILL.md +++ b/skills/omni-version-manager/SKILL.md @@ -218,4 +218,4 @@ curl https://localhost:20128/api/services/{name}/logs \ ## Payloads -See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/reference/openapi.yaml` for detailed request/response schemas. +See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/openapi.yaml` for detailed request/response schemas. diff --git a/skills/omni-webhooks/SKILL.md b/skills/omni-webhooks/SKILL.md index 707241b96c..251b5f5817 100644 --- a/skills/omni-webhooks/SKILL.md +++ b/skills/omni-webhooks/SKILL.md @@ -17,4 +17,4 @@ All requests require a valid Bearer token or session cookie. Obtain a token via _No endpoints mapped for this area yet._ ## Payloads -See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/reference/openapi.yaml` for detailed request/response schemas. +See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/openapi.yaml` for detailed request/response schemas. diff --git a/src/app/api/docs/route.ts b/src/app/api/docs/route.ts new file mode 100644 index 0000000000..978449f1d1 --- /dev/null +++ b/src/app/api/docs/route.ts @@ -0,0 +1,89 @@ +/** + * GET /api/docs — Rendered API reference (Redoc UI). + * + * Serves an HTML page that loads Redoc from a CDN and points it at + * `/openapi.yaml` (the same canonical spec maintained at + * `docs/openapi.yaml` in the repo). The CDN dependency is documented in + * `docs/architecture/standalone-renderer-strategy.md`; if the deployment + * is air-gapped, mirror the Redoc assets under `public/vendor/redoc/` + * and update the + + + +`; + +export function GET() { + return new Response(REDOC_HTML, { + status: 200, + headers: { + "Content-Type": "text/html; charset=utf-8", + "Cache-Control": "public, max-age=300, s-maxage=300", + "X-Robots-Tag": "noindex", + }, + }); +} diff --git a/src/app/api/openapi/spec/route.ts b/src/app/api/openapi/spec/route.ts index a4092bd143..37c5a9daf8 100644 --- a/src/app/api/openapi/spec/route.ts +++ b/src/app/api/openapi/spec/route.ts @@ -11,11 +11,12 @@ import { sanitizeErrorMessage } from "@omniroute/open-sse/utils/error"; let cachedSpec: { data: any; mtime: number } | null = null; const OPENAPI_SPEC_CANDIDATES = [ - path.join(/* turbopackIgnore: true */ process.cwd(), "docs", "reference", "openapi.yaml"), - path.join(/* turbopackIgnore: true */ process.cwd(), "app", "docs", "reference", "openapi.yaml"), - // Legacy locations kept as fallback for old standalone bundles. path.join(/* turbopackIgnore: true */ process.cwd(), "docs", "openapi.yaml"), path.join(/* turbopackIgnore: true */ process.cwd(), "app", "docs", "openapi.yaml"), + // Legacy locations kept as fallback for old standalone bundles (pre-#4781 move + // from docs/reference/openapi.yaml to the canonical docs/openapi.yaml). + path.join(/* turbopackIgnore: true */ process.cwd(), "docs", "reference", "openapi.yaml"), + path.join(/* turbopackIgnore: true */ process.cwd(), "app", "docs", "reference", "openapi.yaml"), ]; /** diff --git a/src/app/docs/lib/openapi.generated.ts b/src/app/docs/lib/openapi.generated.ts index 3681b7770c..7f0053095e 100644 --- a/src/app/docs/lib/openapi.generated.ts +++ b/src/app/docs/lib/openapi.generated.ts @@ -1,7 +1,7 @@ // AUTO-GENERATED by scripts/docs/gen-openapi-module.mjs — DO NOT EDIT MANUALLY // Regenerate with: node scripts/docs/gen-openapi-module.mjs // -// Source of truth: docs/reference/openapi.yaml +// Source of truth: docs/openapi.yaml // // The Api Explorer consumes `OPENAPI_ENDPOINTS`; the spec metadata // (`OPENAPI_VERSION`, `OPENAPI_TITLE`) is surfaced in the page header. @@ -25,7 +25,7 @@ export interface OpenApiEndpoint { hasRequestBody: boolean; } -export const OPENAPI_VERSION = "3.8.24"; +export const OPENAPI_VERSION = "3.8.35"; export const OPENAPI_TITLE = "OmniRoute API"; export const OPENAPI_ENDPOINTS: OpenApiEndpoint[] = [ @@ -173,8 +173,7 @@ export const OPENAPI_ENDPOINTS: OpenApiEndpoint[] = [ path: "/api/v1/providers/{provider}/models", method: "GET", summary: "List models for a specific provider", - description: - "Returns only models for the selected provider with provider prefix removed from each model id.", + description: "Returns only models for the selected provider with provider prefix removed from each model id.", tag: "Models", tags: ["Models"], requiresAuth: true, @@ -200,6 +199,16 @@ export const OPENAPI_ENDPOINTS: OpenApiEndpoint[] = [ requiresAuth: true, hasRequestBody: true, }, + { + path: "/api/v1/ws", + method: "GET", + 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.", + tag: "Chat", + tags: ["Chat"], + requiresAuth: true, + hasRequestBody: false, + }, { path: "/api/v1beta/models", method: "GET", diff --git a/src/lib/agentSkills/generator.ts b/src/lib/agentSkills/generator.ts index 23b3a10955..96836a5bd0 100644 --- a/src/lib/agentSkills/generator.ts +++ b/src/lib/agentSkills/generator.ts @@ -124,7 +124,7 @@ function buildApiBody(skill: AgentSkill, sources: BuildSources): string { lines.push("## Payloads\n"); lines.push( "See the full OpenAPI specification at `GET /api/openapi/spec` or " + - "`docs/reference/openapi.yaml` for detailed request/response schemas.", + "`docs/openapi.yaml` for detailed request/response schemas.", ); lines.push(""); diff --git a/src/lib/agentSkills/openapiParser.ts b/src/lib/agentSkills/openapiParser.ts index c6cc0648fb..25fa40a912 100644 --- a/src/lib/agentSkills/openapiParser.ts +++ b/src/lib/agentSkills/openapiParser.ts @@ -1,5 +1,5 @@ /** - * openapiParser.ts — parses docs/reference/openapi.yaml to extract endpoint info + * openapiParser.ts — parses docs/openapi.yaml to extract endpoint info * grouped by SkillArea. Used by the catalog and the generator. * * Reads the OpenAPI YAML synchronously at runtime (same pattern as @@ -139,7 +139,7 @@ function extractOperations(pathsObj: Record): OpenapiPath[] { } /** - * Parses `docs/reference/openapi.yaml` and returns: + * Parses `docs/openapi.yaml` and returns: * - `paths`: all operations keyed by `"METHOD /path"` * - `areas`: operations grouped by SkillArea (api skills only) * @@ -147,7 +147,7 @@ function extractOperations(pathsObj: Record): OpenapiPath[] { * and standalone scripts without async machinery. */ export function parseOpenapi(): ParsedOpenapi { - const yamlPath = path.resolve(process.cwd(), "docs", "reference", "openapi.yaml"); + const yamlPath = path.resolve(process.cwd(), "docs", "openapi.yaml"); let rawContent: string; try { diff --git a/tests/integration/integration-wiring.test.ts b/tests/integration/integration-wiring.test.ts index cbe47150b7..acad6b66c6 100644 --- a/tests/integration/integration-wiring.test.ts +++ b/tests/integration/integration-wiring.test.ts @@ -309,7 +309,7 @@ describe("API Routes — dashboard and tool consumers", () => { it("keeps legacy usage history and raw request-log APIs explicitly classified", () => { const usageStats = readProjectFile("src/shared/components/UsageStats.tsx"); const apiReference = readProjectFile("docs/reference/API_REFERENCE.md"); - const openApi = readProjectFile("docs/reference/openapi.yaml"); + const openApi = readProjectFile("docs/openapi.yaml"); assert.ok(usageStats, "UsageStats compatibility component should exist"); assert.ok(apiReference, "API reference should exist"); @@ -359,7 +359,7 @@ describe("Dashboard Wiring — T05 payload rules", () => { const payloadRulesTabSrc = readProjectFile( "src/app/(dashboard)/dashboard/settings/components/PayloadRulesTab.tsx" ); - const openapiSrc = readProjectFile("docs/reference/openapi.yaml"); + const openapiSrc = readProjectFile("docs/openapi.yaml"); it.skip("settings page should surface payload rules inside advanced settings", () => { assert.ok(settingsPageSrc, "settings page source should exist"); @@ -379,7 +379,7 @@ describe("Dashboard Wiring — T05 payload rules", () => { }); it("openapi should document the payload rules management surface", () => { - assert.ok(openapiSrc, "docs/reference/openapi.yaml should exist"); + assert.ok(openapiSrc, "docs/openapi.yaml should exist"); assert.match(openapiSrc, /\/api\/settings\/payload-rules:/); assert.match(openapiSrc, /summary:\s+Get payload rules configuration/); assert.match(openapiSrc, /ManagementSessionAuth:/); diff --git a/tests/unit/api-docs-route.test.ts b/tests/unit/api-docs-route.test.ts new file mode 100644 index 0000000000..e4d0a13dd8 --- /dev/null +++ b/tests/unit/api-docs-route.test.ts @@ -0,0 +1,24 @@ +import test from "node:test"; +import assert from "node:assert/strict"; + +// GET /api/docs serves the Redoc-rendered API reference (#4781). Static HTML +// shell that loads Redoc from a CDN and points it at /openapi.yaml (the +// canonical spec served from public/openapi.yaml). PUBLIC tier — no auth. +const docsRoute = await import("../../src/app/api/docs/route.ts"); + +test("GET /api/docs returns a 200 text/html Redoc shell", async () => { + const response = docsRoute.GET(); + assert.equal(response.status, 200); + assert.match(response.headers.get("content-type") ?? "", /text\/html/); + + const body = await response.text(); + // Renders Redoc and points it at the canonical spec. + assert.match(body, /redoc/i); + assert.ok(body.includes("/openapi.yaml"), "Redoc must load the canonical /openapi.yaml spec"); + // No stack traces / secrets leaked into the static shell. + assert.ok(!body.includes("at /"), "static shell must not leak stack-trace frames"); +}); + +test("GET /api/docs is statically renderable (no per-request work)", () => { + assert.equal(docsRoute.dynamic, "force-static"); +}); diff --git a/tests/unit/dast-method-not-allowed.test.ts b/tests/unit/dast-method-not-allowed.test.ts index 4a7f16e0d1..55e04ed9e4 100644 --- a/tests/unit/dast-method-not-allowed.test.ts +++ b/tests/unit/dast-method-not-allowed.test.ts @@ -75,7 +75,7 @@ test("raw HTTP guard allows documented methods through", () => { }); test("OpenAPI documents high-risk route auth and setup responses", () => { - const spec = readFileSync("docs/reference/openapi.yaml", "utf8"); + const spec = readFileSync("docs/openapi.yaml", "utf8"); const apiKeyDetailStart = spec.indexOf(" /api/keys/{id}:"); const apiKeyDetailEnd = spec.indexOf("\n /api/combos:", apiKeyDetailStart); const apiKeyDetail = spec.slice(apiKeyDetailStart, apiKeyDetailEnd); diff --git a/tests/unit/openapi-coverage.test.ts b/tests/unit/openapi-coverage.test.ts index be5c8425f4..d69f65b52e 100644 --- a/tests/unit/openapi-coverage.test.ts +++ b/tests/unit/openapi-coverage.test.ts @@ -6,7 +6,7 @@ import * as yaml from "js-yaml"; const ROOT = process.cwd(); const API_ROOT = path.join(ROOT, "src", "app", "api"); -const OPENAPI_PATH = path.join(ROOT, "docs", "reference", "openapi.yaml"); +const OPENAPI_PATH = path.join(ROOT, "docs", "openapi.yaml"); function collectRoutePaths(dir: string): string[] { const entries = fs.readdirSync(dir, { withFileTypes: true }); diff --git a/tests/unit/openapi-security-tiers.test.ts b/tests/unit/openapi-security-tiers.test.ts index beab4d4815..35c5d31520 100644 --- a/tests/unit/openapi-security-tiers.test.ts +++ b/tests/unit/openapi-security-tiers.test.ts @@ -5,7 +5,7 @@ import path from "node:path"; import * as yaml from "js-yaml"; const ROOT = process.cwd(); -const OPENAPI_PATH = path.join(ROOT, "docs", "reference", "openapi.yaml"); +const OPENAPI_PATH = path.join(ROOT, "docs", "openapi.yaml"); const { LOCAL_ONLY_API_PREFIXES, ALWAYS_PROTECTED_API_PATHS } = await import("../../src/server/authz/routeGuard.ts"); diff --git a/tests/unit/openapi-ws-endpoint.test.ts b/tests/unit/openapi-ws-endpoint.test.ts index 46a917bade..7077e2585e 100644 --- a/tests/unit/openapi-ws-endpoint.test.ts +++ b/tests/unit/openapi-ws-endpoint.test.ts @@ -11,7 +11,7 @@ import * as yaml from "js-yaml"; // in the spec, so it was invisible in the endpoints reference. const ROOT = fileURLToPath(new URL("../../", import.meta.url)); -const spec = yaml.load(readFileSync(ROOT + "docs/reference/openapi.yaml", "utf8")) as { +const spec = yaml.load(readFileSync(ROOT + "docs/openapi.yaml", "utf8")) as { paths: Record }>>; };