mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-26 09:52:11 +03:00
chore(release): prepare v3.8.0 docs and coverage ratchet
Refresh the v3.8.0 documentation set across API, setup, Docker, environment, Fly.io, VM deployment, and troubleshooting guides. Document the updated budget payload, management-auth requirements, WebSocket bridge secret, Compose profiles, production deployment details, and expanded Node support. Also update the OpenAPI catalog, raise coverage thresholds to match the current baseline, and align cloud-agent task route typing and schemas with the stricter runtime behavior.
This commit is contained in:
@@ -457,12 +457,17 @@ POST /api/usage/budget
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"keyId": "key-123",
|
||||
"limit": 50.00,
|
||||
"period": "monthly"
|
||||
"apiKeyId": "key-123",
|
||||
"dailyLimitUsd": 5.00,
|
||||
"weeklyLimitUsd": 30.00,
|
||||
"monthlyLimitUsd": 100.00,
|
||||
"warningThreshold": 0.8,
|
||||
"resetInterval": "monthly"
|
||||
}
|
||||
```
|
||||
|
||||
> **Schema notes** (`setBudgetSchema`): `apiKeyId` is required; at least one of `dailyLimitUsd`, `weeklyLimitUsd`, or `monthlyLimitUsd` must be greater than zero. Optional fields: `warningThreshold` (0–1), `resetInterval` (`daily` | `weekly` | `monthly`), `resetTime` (`HH:MM`). The legacy `{keyId, limit, period}` shape returns `400 Bad Request`.
|
||||
|
||||
## Request Processing
|
||||
|
||||
1. Client sends request to `/v1/*`
|
||||
@@ -486,3 +491,5 @@ Full architecture reference: [`ARCHITECTURE.md`](ARCHITECTURE.md)
|
||||
- Login uses saved password hash; fallback to `INITIAL_PASSWORD`
|
||||
- `requireLogin` toggleable via `/api/settings/require-login`
|
||||
- `/v1/*` routes optionally require Bearer API key when `REQUIRE_API_KEY=true`
|
||||
|
||||
> **Breaking change (v3.8.0)** — `/api/v1/agents/tasks/*` and the cooldown management endpoints now require **management auth** (dashboard `auth_token` cookie or a management-scoped API key). Clients that previously called these routes unauthenticated will receive `401 Unauthorized`. See commit `588a0333` (`fix(auth): require management auth for agent and cooldown APIs`).
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
# Test Coverage Plan
|
||||
|
||||
Last updated: 2026-03-28
|
||||
Last updated: 2026-05-13
|
||||
|
||||
> Status measured on 2026-05-13: lines 82.58%, statements 82.58%, functions 84.23%, branches 75.22%. Phases 1-5 are complete. Current focus is Phase 6 (>=85%) and Phase 7 (>=90%).
|
||||
|
||||
## Baseline
|
||||
|
||||
@@ -10,7 +12,7 @@ There are multiple coverage numbers depending on how the report is computed. For
|
||||
| -------------------- | ----------------------------------------------------- | -----------------: | -------: | --------: | --------------------------------------------------- |
|
||||
| Legacy | Old `npm run test:coverage` | 79.42% | 75.15% | 67.94% | Inflated: counts test files and excludes `open-sse` |
|
||||
| Diagnostic | Source-only, excluding tests and excluding `open-sse` | 68.16% | 63.55% | 64.06% | Useful only to isolate `src/**` |
|
||||
| Recommended baseline | Source-only, excluding tests and including `open-sse` | 56.95% | 66.05% | 57.80% | This is the project-wide baseline to improve |
|
||||
| Recommended baseline | Source-only, excluding tests and including `open-sse` | 82.58% | 75.22% | 84.23% | This is the project-wide baseline to improve |
|
||||
|
||||
The recommended baseline is the number to optimize against.
|
||||
|
||||
@@ -34,50 +36,51 @@ The recommended baseline is the number to optimize against.
|
||||
|
||||
## Milestones
|
||||
|
||||
| Phase | Target | Focus |
|
||||
| ------- | ---------------------: | ------------------------------------------------- |
|
||||
| Phase 1 | 60% statements / lines | Quick wins and low-risk utility coverage |
|
||||
| Phase 2 | 65% statements / lines | DB and route foundations |
|
||||
| Phase 3 | 70% statements / lines | Provider validation and usage analytics |
|
||||
| Phase 4 | 75% statements / lines | `open-sse` translators and helpers |
|
||||
| Phase 5 | 80% statements / lines | `open-sse` handlers and executor branches |
|
||||
| Phase 6 | 85% statements / lines | Harder edge cases, branch debt, regression suites |
|
||||
| Phase 7 | 90% statements / lines | Final sweep, gap closure, strict ratchet |
|
||||
| Phase | Target | Focus | Status |
|
||||
| ------- | ---------------------: | ------------------------------------------------- | ---------- |
|
||||
| Phase 1 | 60% statements / lines | Quick wins and low-risk utility coverage | ✅ Done |
|
||||
| Phase 2 | 65% statements / lines | DB and route foundations | ✅ Done |
|
||||
| Phase 3 | 70% statements / lines | Provider validation and usage analytics | ✅ Done |
|
||||
| Phase 4 | 75% statements / lines | `open-sse` translators and helpers | ✅ Done |
|
||||
| Phase 5 | 80% statements / lines | `open-sse` handlers and executor branches | ✅ Done |
|
||||
| Phase 6 | 85% statements / lines | Harder edge cases, branch debt, regression suites | In progress |
|
||||
| Phase 7 | 90% statements / lines | Final sweep, gap closure, strict ratchet | Pending |
|
||||
|
||||
Branches and functions should ratchet upward with each phase, but the primary hard target is statements / lines.
|
||||
|
||||
## Priority hotspots
|
||||
|
||||
These files or areas offer the best return for the next phases:
|
||||
These files have the lowest line coverage today (< 60%) and offer the best return for Phases 6-7. Generated from `coverage/coverage-summary.json` on 2026-05-13:
|
||||
|
||||
1. `open-sse/handlers`
|
||||
- `chatCore.ts` at 7.57%
|
||||
- Overall directory at 29.07%
|
||||
2. `open-sse/translator/request`
|
||||
- Overall directory at 36.39%
|
||||
- Many translators are still near single-digit coverage
|
||||
3. `open-sse/translator/response`
|
||||
- Overall directory at 8.07%
|
||||
4. `open-sse/executors`
|
||||
- Overall directory at 36.62%
|
||||
5. `src/lib/db`
|
||||
- `models.ts` at 20.66%
|
||||
- `registeredKeys.ts` at 34.46%
|
||||
- `modelComboMappings.ts` at 36.25%
|
||||
- `settings.ts` at 46.40%
|
||||
- `webhooks.ts` at 33.33%
|
||||
6. `src/lib/usage`
|
||||
- `usageHistory.ts` at 21.12%
|
||||
- `usageStats.ts` at 9.56%
|
||||
- `costCalculator.ts` at 30.00%
|
||||
7. `src/lib/providers`
|
||||
- `validation.ts` at 41.16%
|
||||
8. Low-risk utility and API files for early gains
|
||||
- `src/shared/utils/upstreamError.ts`
|
||||
- `src/shared/utils/apiAuth.ts`
|
||||
- `src/lib/api/errorResponse.ts`
|
||||
- `src/app/api/settings/require-login/route.ts`
|
||||
- `src/app/api/providers/[id]/models/route.ts`
|
||||
| # | File | Lines % |
|
||||
| --- | ----------------------------------------------------------------- | ------: |
|
||||
| 1 | `open-sse/services/compression/validation.ts` | 7.87% |
|
||||
| 2 | `src/app/api/v1/batches/route.ts` | 9.67% |
|
||||
| 3 | `src/app/docs/components/FeedbackWidget.tsx` | 9.80% |
|
||||
| 4 | `open-sse/services/compression/toolResultCompressor.ts` | 10.00% |
|
||||
| 5 | `src/app/docs/components/DocCodeBlocks.tsx` | 10.63% |
|
||||
| 6 | `open-sse/services/compression/engines/rtk/lineFilter.ts` | 10.96% |
|
||||
| 7 | `open-sse/services/specificityRules.ts` | 11.28% |
|
||||
| 8 | `src/mitm/systemCommands.ts` | 12.19% |
|
||||
| 9 | `open-sse/services/compression/aggressive.ts` | 12.77% |
|
||||
| 10 | `src/app/api/v1/batches/[id]/cancel/route.ts` | 12.98% |
|
||||
| 11 | `open-sse/services/compression/progressiveAging.ts` | 13.26% |
|
||||
| 12 | `open-sse/services/compression/engines/rtk/smartTruncate.ts` | 13.43% |
|
||||
| 13 | `open-sse/services/compression/engines/rtk/deduplicator.ts` | 13.51% |
|
||||
| 14 | `src/lib/cloudAgent/agents/jules.ts` | 13.52% |
|
||||
| 15 | `open-sse/services/compression/lite.ts` | 14.46% |
|
||||
| 16 | `src/app/api/v1/rerank/route.ts` | 14.94% |
|
||||
| 17 | `open-sse/services/compression/preservation.ts` | 15.07% |
|
||||
| 18 | `src/lib/cloudAgent/agents/codex.ts` | 15.54% |
|
||||
| 19 | `open-sse/services/tierResolver.ts` | 16.66% |
|
||||
| 20 | `src/app/docs/components/DocsLazyWrapper.tsx` | 16.66% |
|
||||
|
||||
Themes for Phases 6-7:
|
||||
|
||||
- `open-sse/services/compression/**` is the densest cluster of low coverage and dominates the remaining gap.
|
||||
- Batch and rerank API routes (`src/app/api/v1/batches/**`, `src/app/api/v1/rerank/route.ts`) need handler-level tests.
|
||||
- Cloud agent adapters (`src/lib/cloudAgent/agents/jules.ts`, `codex.ts`) and `tierResolver.ts` need scenario tests.
|
||||
- Docs UI components and `src/mitm/systemCommands.ts` are lower priority but cheap branch wins.
|
||||
|
||||
## Execution checklist
|
||||
|
||||
@@ -148,18 +151,26 @@ These files or areas offer the best return for the next phases:
|
||||
|
||||
Update `npm run test:coverage` thresholds only after the project actually exceeds the next milestone with a comfortable buffer.
|
||||
|
||||
Recommended ratchet sequence:
|
||||
**Current gate (as of 2026-05-13):** `npm run test:coverage` enforces **75 statements / 75 lines / 75 functions / 70 branches**. This is the conservative ratchet against the measured baseline (82.58% / 82.58% / 84.23% / 75.22%) and preserves headroom for transient flakiness.
|
||||
|
||||
For ad-hoc threshold checks against the latest report use:
|
||||
|
||||
```bash
|
||||
node scripts/test-report-summary.mjs --threshold 75
|
||||
```
|
||||
|
||||
Recommended ratchet sequence (order is `statements-lines / branches / functions`):
|
||||
|
||||
1. 55/60/55
|
||||
2. 60/62/58
|
||||
3. 65/64/62
|
||||
4. 70/66/66
|
||||
5. 75/70/72
|
||||
5. 75/70/72 <-- current gate (75/70/75)
|
||||
6. 80/75/78
|
||||
7. 85/80/84
|
||||
8. 90/85/88
|
||||
|
||||
Order is `statements-lines / branches / functions`.
|
||||
Next ratchet target is `80/75/78` once branch coverage holds above 78% for two consecutive runs.
|
||||
|
||||
## Known gap
|
||||
|
||||
|
||||
@@ -7,6 +7,11 @@
|
||||
- [Quick Run](#quick-run)
|
||||
- [With Environment File](#with-environment-file)
|
||||
- [Docker Compose](#docker-compose)
|
||||
- [Available Profiles](#available-profiles)
|
||||
- [Redis Sidecar](#redis-sidecar)
|
||||
- [Production Compose](#production-compose)
|
||||
- [Dockerfile Stages](#dockerfile-stages)
|
||||
- [Critical Environment Variables](#critical-environment-variables)
|
||||
- [Docker Compose with Caddy (HTTPS)](#docker-compose-with-caddy-https-auto-tls)
|
||||
- [Cloudflare Quick Tunnel](#cloudflare-quick-tunnel)
|
||||
- [Image Tags](#image-tags)
|
||||
@@ -50,8 +55,114 @@ docker compose --profile base up -d
|
||||
|
||||
# CLI profile (Claude Code, Codex, OpenClaw built-in)
|
||||
docker compose --profile cli up -d
|
||||
|
||||
# Host profile (Linux-first; mounts host CLI binaries read-only)
|
||||
docker compose --profile host up -d
|
||||
|
||||
# Combine CLI + CLIProxyAPI sidecar
|
||||
docker compose --profile cli --profile cliproxyapi up -d
|
||||
```
|
||||
|
||||
## Available Profiles
|
||||
|
||||
OmniRoute ships four Compose profiles. Pick the one that matches your environment.
|
||||
|
||||
| Profile | Service | When to use | Command |
|
||||
| ---------------- | ---------------- | --------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------- |
|
||||
| `base` (default) | `omniroute-base` | Headless server / minimal runtime, no provider CLIs bundled | `docker compose --profile base up -d` |
|
||||
| `cli` | `omniroute-cli` | Agentic workflows that call `omniroute providers/setup/doctor` and bundled CLIs (Codex, Claude Code, Droid, OpenClaw) | `docker compose --profile cli up -d` |
|
||||
| `host` | `omniroute-host` | Linux hosts that want `network_mode`-like access to host CLIs by mounting `~/.local/bin`, `~/.codex`, `~/.claude`, etc. read-only | `docker compose --profile host up -d` |
|
||||
| `cliproxyapi` | `cliproxyapi` | Run the [CLIProxyAPI](https://github.com/router-for-me/CLIProxyAPI) sidecar on port `8317` for upstream CLI proxying | `docker compose --profile cliproxyapi up -d` |
|
||||
|
||||
> Multiple profiles can be combined: `docker compose --profile cli --profile cliproxyapi up -d`.
|
||||
|
||||
## Redis Sidecar
|
||||
|
||||
OmniRoute relies on Redis to back the distributed rate limiter and shared cache. The `redis` service is **always defined** in `docker-compose.yml` (it has no profile gate) and starts alongside any other profile.
|
||||
|
||||
| Detail | Value |
|
||||
| -------------------- | --------------------------------- |
|
||||
| Image | `redis:7-alpine` |
|
||||
| Container name | `omniroute-redis` |
|
||||
| Internal port | `6379` |
|
||||
| Host port (override) | `REDIS_PORT` (defaults to `6379`) |
|
||||
| Volume | `omniroute-redis-data` → `/data` |
|
||||
| Healthcheck | `redis-cli ping` (10s interval) |
|
||||
|
||||
Related environment variables:
|
||||
|
||||
- `REDIS_URL` — connection string injected into the app (`redis://redis:6379` by default).
|
||||
- `REDIS_PORT` — host-side port mapping for the Redis container.
|
||||
|
||||
**Disabling Redis** is not recommended (rate limiter will degrade to in-memory fallback). If you must, either remove/comment the `redis:` service block in `docker-compose.yml` or scale it to zero:
|
||||
|
||||
```bash
|
||||
docker compose up -d --scale redis=0
|
||||
```
|
||||
|
||||
## Production Compose
|
||||
|
||||
For an isolated production snapshot running alongside dev, use `docker-compose.prod.yml`.
|
||||
|
||||
| Detail | Value |
|
||||
| ---------------------- | ---------------------------------------------------------------------------------- |
|
||||
| File | `docker-compose.prod.yml` |
|
||||
| Default dashboard port | `PROD_DASHBOARD_PORT=20130` (mapped to internal `${DASHBOARD_PORT:-20128}`) |
|
||||
| Default API port | `PROD_API_PORT=20131` |
|
||||
| Image | `omniroute:prod` (built from `runner-cli` target) |
|
||||
| Redis container | `omniroute-redis-prod` (`redis:8.6.2`, dedicated `redis-prod-data` volume) |
|
||||
| Data volume | `omniroute-prod-data` (named, persisted across rebuilds) |
|
||||
| Healthchecks | `node healthcheck.mjs` + `redis-cli ping`, with `depends_on` gated on Redis health |
|
||||
|
||||
How to use:
|
||||
|
||||
```bash
|
||||
# Build & start the production stack
|
||||
docker compose -f docker-compose.prod.yml up -d --build
|
||||
|
||||
# Stream logs
|
||||
docker compose -f docker-compose.prod.yml logs -f
|
||||
|
||||
# Tear down (keep volumes)
|
||||
docker compose -f docker-compose.prod.yml down
|
||||
```
|
||||
|
||||
The prod stack runs in parallel with the dev compose (different container names, ports, and volumes), so you can keep iterating locally while production stays up.
|
||||
|
||||
## Dockerfile Stages
|
||||
|
||||
The repository ships a multi-stage Dockerfile (`Dockerfile`). Three stages are exposed; pick the right `target` for your use case.
|
||||
|
||||
| Stage | Base image | Purpose |
|
||||
| ------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `builder` | `node:24.15.0-trixie-slim` | Installs deps (`npm ci --legacy-peer-deps`) and runs `npm run build -- --webpack` |
|
||||
| `runner-base` | `node:24.15.0-trixie-slim` | Production runtime with the Next.js standalone output. **No provider CLIs bundled.** |
|
||||
| `runner-cli` | `runner-base` | Adds `git`, `docker.io`, `docker-compose` and global CLIs: `@openai/codex`, `@anthropic-ai/claude-code`, `droid`, `openclaw`. **Pick this for agentic workflows.** |
|
||||
|
||||
Build a specific target manually:
|
||||
|
||||
```bash
|
||||
docker build --target runner-base -t omniroute:base .
|
||||
docker build --target runner-cli -t omniroute:cli .
|
||||
```
|
||||
|
||||
Defaults exported by `runner-base`: `PORT=20128`, `HOSTNAME=0.0.0.0`, `NODE_OPTIONS=--max-old-space-size=256`, `DATA_DIR=/app/data`, `OMNIROUTE_MIGRATIONS_DIR=/app/migrations`.
|
||||
|
||||
## Critical Environment Variables
|
||||
|
||||
Beyond the defaults documented in [ENVIRONMENT.md](ENVIRONMENT.md), the following variables matter most when running under Docker:
|
||||
|
||||
| Variable | Purpose | Default |
|
||||
| ----------------------------- | --------------------------------------------------------------------------------------------------- | ------------------------- |
|
||||
| `OMNIROUTE_WS_BRIDGE_SECRET` | Shared secret for the WebSocket bridge. **Required in production** — set to a strong random string. | unset (must be provided) |
|
||||
| `REDIS_URL` | Connection string for the rate limiter / cache backend | `redis://redis:6379` |
|
||||
| `REDIS_PORT` | Host-side port for the bundled Redis container | `6379` |
|
||||
| `AUTO_UPDATE_HOST_REPO_DIR` | Host path mounted into `cli` profile at `/workspace/omniroute` for self-update workflows | `.` (current directory) |
|
||||
| `OMNIROUTE_MEMORY_MB` | Node heap ceiling (`NODE_OPTIONS=--max-old-space-size`) baked into the image | `256` (set in Dockerfile) |
|
||||
| `DASHBOARD_PORT` / `API_PORT` | Override exposed ports for dashboard (20128) and API (20129) | `20128` / `20129` |
|
||||
| `PROD_DASHBOARD_PORT` | Host-side dashboard port for `docker-compose.prod.yml` | `20130` |
|
||||
| `CLIPROXYAPI_PORT` | Host-side port for the `cliproxyapi` sidecar | `8317` |
|
||||
|
||||
## Docker Compose with Caddy (HTTPS Auto-TLS)
|
||||
|
||||
OmniRoute can be securely exposed using Caddy's automatic SSL provisioning. Ensure your domain's DNS A record points to your server's IP.
|
||||
@@ -101,9 +212,9 @@ Endpoint tunnel panels (Cloudflare, Tailscale, ngrok) can be shown or hidden fro
|
||||
| Image | Tag | Size | Description |
|
||||
| ------------------------ | -------- | ------ | --------------------- |
|
||||
| `diegosouzapw/omniroute` | `latest` | ~250MB | Latest stable release |
|
||||
| `diegosouzapw/omniroute` | `3.7.8` | ~250MB | Current version |
|
||||
| `diegosouzapw/omniroute` | `3.8.0` | ~250MB | Current version |
|
||||
|
||||
Multi-platform: AMD64 + ARM64 native (Apple Silicon, AWS Graviton, Raspberry Pi).
|
||||
Multi-platform manifest: `linux/amd64` + `linux/arm64` native (Apple Silicon, AWS Graviton, Raspberry Pi). Docker selects the matching architecture automatically; pass `--platform linux/amd64` if you need to force AMD64 emulation on ARM hosts.
|
||||
|
||||
## Important Notes
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
- [21. Proxy Health](#21-proxy-health)
|
||||
- [22. Debugging](#22-debugging)
|
||||
- [23. GitHub Integration](#23-github-integration)
|
||||
- [24. Skills Sandbox (v3.8.0+)](#24-skills-sandbox-v380)
|
||||
- [Deployment Scenarios](#deployment-scenarios)
|
||||
- [Audit: Removed / Dead Variables](#audit-removed--dead-variables)
|
||||
|
||||
@@ -39,19 +40,21 @@
|
||||
|
||||
These **must** be set before the first run. Without them, the application will either refuse to start or operate with insecure defaults.
|
||||
|
||||
| Variable | Required | Default | Source File | Description |
|
||||
| ------------------ | -------- | -------- | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `JWT_SECRET` | **Yes** | _(none)_ | `src/lib/auth` | Signs/verifies all dashboard session cookies (JWT). Generate with `openssl rand -base64 48`. |
|
||||
| `API_KEY_SECRET` | **Yes** | _(none)_ | `src/lib/db/apiKeys.ts` | AES encryption key for API key values at rest in SQLite. Generate with `openssl rand -hex 32`. |
|
||||
| `INITIAL_PASSWORD` | **Yes** | `123456` | Bootstrap script | Sets the initial admin dashboard password. **Change before first use.** After login, change via Dashboard → Settings → Security. |
|
||||
| Variable | Required | Default | Source File | Description |
|
||||
| ---------------------------- | -------------------- | ---------- | -------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `JWT_SECRET` | **Yes** | _(none)_ | `src/lib/auth` | Signs/verifies all dashboard session cookies (JWT). Generate with `openssl rand -base64 48`. |
|
||||
| `API_KEY_SECRET` | **Yes** | _(none)_ | `src/lib/db/apiKeys.ts` | AES encryption key for API key values at rest in SQLite. Generate with `openssl rand -hex 32`. |
|
||||
| `INITIAL_PASSWORD` | **Yes** | `CHANGEME` | Bootstrap script | Sets the initial admin dashboard password (matches `.env.example` default — kept obviously insecure to force a change). **Change before first use.** After login, change via Dashboard → Settings → Security. |
|
||||
| `OMNIROUTE_WS_BRIDGE_SECRET` | **Yes** (production) | _(unset)_ | `src/app/api/internal/codex-responses-ws/route.ts` | Shared secret for the internal Codex Responses WebSocket bridge. Authenticates bridge requests between the Electron/browser WS relay and OmniRoute. ⚠️ **REQUIRED in production — when unset, all WS bridge requests are rejected.** Generate with `openssl rand -base64 32`. |
|
||||
|
||||
### Generation Commands
|
||||
|
||||
```bash
|
||||
# Generate all three secrets at once:
|
||||
# Generate all four secrets at once:
|
||||
echo "JWT_SECRET=$(openssl rand -base64 48)"
|
||||
echo "API_KEY_SECRET=$(openssl rand -hex 32)"
|
||||
echo "INITIAL_PASSWORD=$(openssl rand -base64 16)"
|
||||
echo "OMNIROUTE_WS_BRIDGE_SECRET=$(openssl rand -base64 32)"
|
||||
```
|
||||
|
||||
> [!CAUTION]
|
||||
@@ -63,14 +66,19 @@ echo "INITIAL_PASSWORD=$(openssl rand -base64 16)"
|
||||
|
||||
OmniRoute uses **SQLite** (via `better-sqlite3`) for all persistence. These variables control data location, encryption, and lifecycle.
|
||||
|
||||
| Variable | Default | Source File | Description |
|
||||
| -------------------------------- | -------------------- | ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
|
||||
| `DATA_DIR` | `~/.omniroute/` | `src/lib/db/core.ts` | Root directory for SQLite DB, backups, and data files. Override for Docker volumes or custom paths. |
|
||||
| `STORAGE_ENCRYPTION_KEY` | _(empty = disabled)_ | `src/lib/db/encryption.ts` | AES key for full SQLite database encryption at rest. Generate with `openssl rand -hex 32`. |
|
||||
| `STORAGE_ENCRYPTION_KEY_VERSION` | `v1` | `scripts/bootstrap-env.mjs`, `electron/main.js` | Version label for the encryption key. Increment when performing key rotation to support decryption of old backups. |
|
||||
| `DISABLE_SQLITE_AUTO_BACKUP` | `false` | `src/lib/db/backup.ts` | When `true`, skips the automatic database backup that runs before migrations on every startup. |
|
||||
| `OMNIROUTE_CRYPT_KEY` | _(unset)_ | `src/lib/db/encryption.ts` | **Legacy alias** for `STORAGE_ENCRYPTION_KEY`. Accepted as a fallback when the primary variable is absent. |
|
||||
| `OMNIROUTE_API_KEY_BASE64` | _(unset)_ | `src/lib/db/encryption.ts` | **Legacy alias** (Base64-encoded form) accepted as a fallback. Decoded automatically before use. |
|
||||
| Variable | Default | Source File | Description |
|
||||
| -------------------------------------- | -------------------- | ----------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `DATA_DIR` | `~/.omniroute/` | `src/lib/db/core.ts` | Root directory for SQLite DB, backups, and data files. Override for Docker volumes or custom paths. |
|
||||
| `STORAGE_ENCRYPTION_KEY` | _(empty = disabled)_ | `src/lib/db/encryption.ts` | AES key for full SQLite database encryption at rest. Generate with `openssl rand -hex 32`. |
|
||||
| `STORAGE_ENCRYPTION_KEY_VERSION` | `v1` | `scripts/bootstrap-env.mjs`, `electron/main.js` | Version label for the encryption key. Increment when performing key rotation to support decryption of old backups. |
|
||||
| `DISABLE_SQLITE_AUTO_BACKUP` | `false` | `src/lib/db/backup.ts` | When `true`, skips the automatic database backup that runs before migrations on every startup. |
|
||||
| `OMNIROUTE_CRYPT_KEY` | _(unset)_ | `src/lib/db/encryption.ts` | **Legacy alias** for `STORAGE_ENCRYPTION_KEY`. Accepted as a fallback when the primary variable is absent. |
|
||||
| `OMNIROUTE_API_KEY_BASE64` | _(unset)_ | `src/lib/db/encryption.ts` | **Legacy alias** (Base64-encoded form) accepted as a fallback. Decoded automatically before use. |
|
||||
| `OMNIROUTE_DB_HEALTHCHECK_INTERVAL_MS` | _(unset)_ | `src/lib/db/core.ts` | Override the periodic SQLite healthcheck interval (ms). When unset, defaults are derived from `NODE_ENV`. |
|
||||
| `OMNIROUTE_FORCE_DB_HEALTHCHECK` | `0` | `src/lib/db/core.ts` | Set to `1` to force the DB healthcheck loop on, even when it would normally be skipped (e.g., short-lived tasks). |
|
||||
| `OMNIROUTE_MIGRATIONS_DIR` | _(auto-detect)_ | `src/lib/db/migrationRunner.ts` | Override the directory that the migration runner scans. Useful when shipping bundled migrations in custom builds. |
|
||||
| `OMNIROUTE_SPEND_FLUSH_INTERVAL_MS` | _(default in code)_ | `src/lib/spend/batchWriter.ts` | Flush interval (ms) for the batched spend/cost writer. Lower values reduce write coalescing; higher values reduce DB contention. |
|
||||
| `OMNIROUTE_SPEND_MAX_BUFFER_SIZE` | _(default in code)_ | `src/lib/spend/batchWriter.ts` | Max buffered spend entries before a forced flush. Raise on high-QPS deployments; lower when bounded memory matters more. |
|
||||
|
||||
### Scenarios
|
||||
|
||||
@@ -85,16 +93,17 @@ OmniRoute uses **SQLite** (via `better-sqlite3`) for all persistence. These vari
|
||||
|
||||
## 3. Network & Ports
|
||||
|
||||
| Variable | Default | Source File | Description |
|
||||
| --------------------- | ------------ | -------------------------- | -------------------------------------------------------------------------------------- |
|
||||
| `PORT` | `20128` | `src/lib/runtime/ports.ts` | Primary port for both Dashboard UI and API endpoints (single-port mode). |
|
||||
| `API_PORT` | _(unset)_ | `src/lib/runtime/ports.ts` | When set, serves the `/v1/*` proxy API on this separate port. |
|
||||
| `API_HOST` | `0.0.0.0` | `src/lib/runtime/ports.ts` | Bind address for the API port. |
|
||||
| `DASHBOARD_PORT` | _(unset)_ | `src/lib/runtime/ports.ts` | When set, serves the Dashboard UI on this separate port. |
|
||||
| `PROD_DASHBOARD_PORT` | `20130` | `docker-compose.prod.yml` | Host-side published port for the Dashboard in Docker production mode. |
|
||||
| `PROD_API_PORT` | `20131` | `docker-compose.prod.yml` | Host-side published port for the API in Docker production mode. |
|
||||
| `OMNIROUTE_PORT` | _(unset)_ | `src/lib/runtime/ports.ts` | Takes precedence over `PORT` when running inside Electron or other wrappers. |
|
||||
| `NODE_ENV` | `production` | Next.js core | Controls logging verbosity, caching, error detail exposure, and Next.js optimizations. |
|
||||
| Variable | Default | Source File | Description |
|
||||
| ------------------------- | ------------------------------- | --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `PORT` | `20128` | `src/lib/runtime/ports.ts` | Primary port for both Dashboard UI and API endpoints (single-port mode). |
|
||||
| `API_PORT` | _(unset)_ | `src/lib/runtime/ports.ts` | When set, serves the `/v1/*` proxy API on this separate port. |
|
||||
| `API_HOST` | `0.0.0.0` | `src/lib/runtime/ports.ts` | Bind address for the API port. |
|
||||
| `DASHBOARD_PORT` | _(unset)_ | `src/lib/runtime/ports.ts` | When set, serves the Dashboard UI on this separate port. |
|
||||
| `PROD_DASHBOARD_PORT` | `20130` | `docker-compose.prod.yml` | Host-side published port for the Dashboard in Docker production mode. |
|
||||
| `PROD_API_PORT` | `20131` | `docker-compose.prod.yml` | Host-side published port for the API in Docker production mode. |
|
||||
| `OMNIROUTE_PORT` | _(unset)_ | `src/lib/runtime/ports.ts` | Takes precedence over `PORT` when running inside Electron or other wrappers. |
|
||||
| `NODE_ENV` | `production` | Next.js core | Controls logging verbosity, caching, error detail exposure, and Next.js optimizations. |
|
||||
| `OMNIROUTE_USE_TURBOPACK` | `1` (default in `.env.example`) | `package.json` / Next.js 16 | Toggles the Next.js 16 Turbopack bundler in `npm run dev` and `npm run build`. Set to `0` on Windows or when running into native binding incompatibilities. |
|
||||
|
||||
### Port Modes
|
||||
|
||||
@@ -124,16 +133,17 @@ OmniRoute uses **SQLite** (via `better-sqlite3`) for all persistence. These vari
|
||||
|
||||
## 4. Security & Authentication
|
||||
|
||||
| Variable | Default | Source File | Description |
|
||||
| ----------------------------- | --------------------- | ---------------------------------------- | --------------------------------------------------------------------------------------------------------- |
|
||||
| `MACHINE_ID_SALT` | `endpoint-proxy-salt` | `src/lib/auth` | Salt combined with hardware identifiers for machine fingerprinting. Change per-deployment for isolation. |
|
||||
| `AUTH_COOKIE_SECURE` | `false` | `src/lib/auth` | Sets the `Secure` flag on session cookies. **Must be `true`** when running behind HTTPS. |
|
||||
| `REQUIRE_API_KEY` | `false` | API middleware | When `true`, all `/v1/*` proxy requests must include a valid API key. |
|
||||
| `ALLOW_API_KEY_REVEAL` | `false` | Dashboard providers page | Allows revealing full API key values in the Dashboard UI. Security risk on shared instances. |
|
||||
| `NO_LOG_API_KEY_IDS` | _(empty)_ | `src/lib/compliance/index.ts` | Comma-separated API key IDs that bypass request logging (GDPR compliance). |
|
||||
| `MAX_BODY_SIZE_BYTES` | `10485760` (10 MB) | `src/shared/middleware/bodySizeGuard.ts` | Maximum allowed request body size. Rejects payloads exceeding this limit. |
|
||||
| `CORS_ORIGIN` | `*` | Next.js middleware | CORS `Access-Control-Allow-Origin` value. Restrict for production. |
|
||||
| `OUTBOUND_SSRF_GUARD_ENABLED` | `true` | `src/shared/network/outboundUrlGuard.ts` | Block provider calls targeting private/loopback/link-local IP ranges. Disable only in isolated test envs. |
|
||||
| Variable | Default | Source File | Description |
|
||||
| --------------------------------------- | --------------------- | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `MACHINE_ID_SALT` | `endpoint-proxy-salt` | `src/lib/auth` | Salt combined with hardware identifiers for machine fingerprinting. Change per-deployment for isolation. |
|
||||
| `AUTH_COOKIE_SECURE` | `false` | `src/lib/auth` | Sets the `Secure` flag on session cookies. **Must be `true`** when running behind HTTPS. |
|
||||
| `REQUIRE_API_KEY` | `false` | API middleware | When `true`, all `/v1/*` proxy requests must include a valid API key. |
|
||||
| `ALLOW_API_KEY_REVEAL` | `false` | Dashboard providers page | Allows revealing full API key values in the Dashboard UI. Security risk on shared instances. |
|
||||
| `NO_LOG_API_KEY_IDS` | _(empty)_ | `src/lib/compliance/index.ts` | Comma-separated API key IDs that bypass request logging (GDPR compliance). |
|
||||
| `MAX_BODY_SIZE_BYTES` | `10485760` (10 MB) | `src/shared/middleware/bodySizeGuard.ts` | Maximum allowed request body size. Rejects payloads exceeding this limit. |
|
||||
| `CORS_ORIGIN` | `*` | Next.js middleware | CORS `Access-Control-Allow-Origin` value. Restrict for production. |
|
||||
| `OUTBOUND_SSRF_GUARD_ENABLED` | `true` | `src/shared/network/outboundUrlGuard.ts` | Block provider calls targeting private/loopback/link-local IP ranges. Disable only in isolated test envs. |
|
||||
| `OMNIROUTE_ALLOW_PRIVATE_PROVIDER_URLS` | `false` | `src/shared/network/outboundUrlGuard.ts` | Allow provider URLs pointing to private/local networks (localhost, 192.168.x.x, 10.x.x.x, etc.). **REQUIRED for self-hosted providers** (LM Studio, Ollama, vLLM, Llamafile, Triton, SearXNG). When `false`, the dashboard rejects validation of local URLs. |
|
||||
|
||||
### Hardening Checklist
|
||||
|
||||
@@ -188,14 +198,17 @@ OmniRoute provides a two-layer defense: request-side injection scanning and resp
|
||||
|
||||
## 7. URLs & Cloud Sync
|
||||
|
||||
| Variable | Default | Source File | Description |
|
||||
| ----------------------- | ------------------------ | ------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
|
||||
| `BASE_URL` | `http://localhost:20128` | `src/lib/cloudSync.ts` | Server-side URL for internal sync jobs to call `/api/sync/cloud`. |
|
||||
| `CLOUD_URL` | _(empty)_ | `src/lib/cloudSync.ts` | Cloud relay endpoint URL (premium feature). |
|
||||
| `CLOUD_SYNC_TIMEOUT_MS` | `12000` | `src/lib/cloudSync.ts` | HTTP timeout for cloud sync requests. |
|
||||
| `NEXT_PUBLIC_BASE_URL` | `http://localhost:20128` | OAuth, Dashboard, sync | Public-facing URL for OAuth redirect_uri, Dashboard links. **Must match your public URL behind reverse proxy.** |
|
||||
| `NEXT_PUBLIC_CLOUD_URL` | _(empty)_ | Client-side | Client-side mirror of `CLOUD_URL`. |
|
||||
| `NEXT_PUBLIC_APP_URL` | _(unset)_ | `src/shared/services/cloudSyncScheduler.ts` | Legacy fallback for `NEXT_PUBLIC_BASE_URL`. |
|
||||
| Variable | Default | Source File | Description |
|
||||
| --------------------------------------- | ------------------------ | ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `BASE_URL` | `http://localhost:20128` | `src/lib/cloudSync.ts` | Server-side URL for internal sync jobs to call `/api/sync/cloud`. |
|
||||
| `CLOUD_URL` | _(empty)_ | `src/lib/cloudSync.ts` | Cloud relay endpoint URL (premium feature). |
|
||||
| `CLOUD_SYNC_TIMEOUT_MS` | `12000` | `src/lib/cloudSync.ts` | HTTP timeout for cloud sync requests. |
|
||||
| `NEXT_PUBLIC_BASE_URL` | `http://localhost:20128` | OAuth, Dashboard, sync | Public-facing URL for OAuth redirect_uri, Dashboard links. **Must match your public URL behind reverse proxy.** |
|
||||
| `NEXT_PUBLIC_CLOUD_URL` | _(empty)_ | Client-side | Client-side mirror of `CLOUD_URL`. |
|
||||
| `NEXT_PUBLIC_APP_URL` | _(unset)_ | `src/shared/services/cloudSyncScheduler.ts` | Legacy fallback for `NEXT_PUBLIC_BASE_URL`. |
|
||||
| `OMNIROUTE_PUBLIC_BASE_URL` | _(unset)_ | `open-sse/executors/chatgpt-web.ts` | Browser-facing OmniRoute origin used for image URLs in API responses (e.g., `/v1/chatgpt-web/image/<id>`). Set this when OpenWebUI or another relay reaches OmniRoute by an internal URL but the user's browser must fetch images from a LAN, tunnel, or public origin. Do **not** include `/v1`. |
|
||||
| `OMNIROUTE_CGPT_WEB_IMAGE_TIMEOUT_MS` | `180000` (3 min) | `open-sse/executors/chatgpt-web.ts` | Max wait time for an async chatgpt-web image to land via the celsius WebSocket. Increase during upstream queue-deep windows. |
|
||||
| `OMNIROUTE_CGPT_WEB_IMAGE_CACHE_MAX_MB` | `256` | `open-sse/services/chatgptImageCache.ts` | Total in-memory byte budget (MB) for the chatgpt-web image cache serving `/v1/chatgpt-web/image/<id>`. Lower on memory-constrained hosts; raise if image generation is heavy and clients race the 30-minute TTL. |
|
||||
|
||||
> [!IMPORTANT]
|
||||
> When deploying behind a reverse proxy (nginx, Caddy), `NEXT_PUBLIC_BASE_URL` **must** be set to your public URL (e.g., `https://omniroute.example.com`). Without this, OAuth callbacks will fail because the redirect_uri won't match.
|
||||
@@ -230,20 +243,21 @@ Route upstream LLM provider calls through an HTTP or SOCKS5 proxy for egress con
|
||||
|
||||
Controls how OmniRoute discovers and launches CLI sidecars (Claude Code, Codex, etc.).
|
||||
|
||||
| Variable | Default | Source File | Description |
|
||||
| ------------------------- | ---------- | ----------------------------------- | -------------------------------------------------------------------------- |
|
||||
| `CLI_MODE` | `auto` | `src/shared/services/cliRuntime.ts` | `auto` = search system PATH; `manual` = use explicit paths only. |
|
||||
| `CLI_EXTRA_PATHS` | _(unset)_ | `src/shared/services/cliRuntime.ts` | Additional PATH entries for CLI binary discovery (colon-separated). |
|
||||
| `CLI_CONFIG_HOME` | _(unset)_ | `src/shared/services/cliRuntime.ts` | Override home directory for reading CLI configs (`~/.claude`, `~/.codex`). |
|
||||
| `CLI_ALLOW_CONFIG_WRITES` | `false` | `src/shared/services/cliRuntime.ts` | Allow OmniRoute to write CLI config files (token refresh, session data). |
|
||||
| `CLI_CLAUDE_BIN` | `claude` | `src/shared/services/cliRuntime.ts` | Custom path to Claude CLI binary. |
|
||||
| `CLI_CODEX_BIN` | `codex` | `src/shared/services/cliRuntime.ts` | Custom path to Codex CLI binary. |
|
||||
| `CLI_DROID_BIN` | `droid` | `src/shared/services/cliRuntime.ts` | Custom path to Droid CLI binary. |
|
||||
| `CLI_OPENCLAW_BIN` | `openclaw` | `src/shared/services/cliRuntime.ts` | Custom path to OpenClaw CLI binary. |
|
||||
| `CLI_CURSOR_BIN` | `agent` | `src/shared/services/cliRuntime.ts` | Custom path to Cursor agent binary. |
|
||||
| `CLI_CLINE_BIN` | `cline` | `src/shared/services/cliRuntime.ts` | Custom path to Cline CLI binary. |
|
||||
| `CLI_CONTINUE_BIN` | `cn` | `src/shared/services/cliRuntime.ts` | Custom path to Continue CLI binary. |
|
||||
| `CLI_QODER_BIN` | `qoder` | `src/shared/services/cliRuntime.ts` | Custom path to Qoder CLI binary. |
|
||||
| Variable | Default | Source File | Description |
|
||||
| ------------------------- | ---------- | ----------------------------------- | ---------------------------------------------------------------------------------- |
|
||||
| `CLI_MODE` | `auto` | `src/shared/services/cliRuntime.ts` | `auto` = search system PATH; `manual` = use explicit paths only. |
|
||||
| `CLI_EXTRA_PATHS` | _(unset)_ | `src/shared/services/cliRuntime.ts` | Additional PATH entries for CLI binary discovery (colon-separated). |
|
||||
| `CLI_CONFIG_HOME` | _(unset)_ | `src/shared/services/cliRuntime.ts` | Override home directory for reading CLI configs (`~/.claude`, `~/.codex`). |
|
||||
| `CLI_ALLOW_CONFIG_WRITES` | `false` | `src/shared/services/cliRuntime.ts` | Allow OmniRoute to write CLI config files (token refresh, session data). |
|
||||
| `CLI_CLAUDE_BIN` | `claude` | `src/shared/services/cliRuntime.ts` | Custom path to Claude CLI binary. |
|
||||
| `CLI_CODEX_BIN` | `codex` | `src/shared/services/cliRuntime.ts` | Custom path to Codex CLI binary. |
|
||||
| `CLI_DROID_BIN` | `droid` | `src/shared/services/cliRuntime.ts` | Custom path to Droid CLI binary. |
|
||||
| `CLI_OPENCLAW_BIN` | `openclaw` | `src/shared/services/cliRuntime.ts` | Custom path to OpenClaw CLI binary. |
|
||||
| `CLI_CURSOR_BIN` | `agent` | `src/shared/services/cliRuntime.ts` | Custom path to Cursor agent binary. |
|
||||
| `CLI_CLINE_BIN` | `cline` | `src/shared/services/cliRuntime.ts` | Custom path to Cline CLI binary. |
|
||||
| `CLI_CONTINUE_BIN` | `cn` | `src/shared/services/cliRuntime.ts` | Custom path to Continue CLI binary. |
|
||||
| `CLI_QODER_BIN` | `qoder` | `src/shared/services/cliRuntime.ts` | Custom path to Qoder CLI binary. |
|
||||
| `CLI_DEVIN_BIN` | `devin` | `open-sse/executors/devin-cli.ts` | Custom path to the Devin CLI binary (v3.8.0). Used by the Windsurf/Devin executor. |
|
||||
|
||||
### Docker Example
|
||||
|
||||
@@ -290,28 +304,34 @@ CLI_CLAUDE_BIN=/host-cli/bin/claude
|
||||
|
||||
Built-in credentials for **localhost development**. For remote deployments, register your own at each provider's developer console.
|
||||
|
||||
| Variable | Provider | Notes |
|
||||
| --------------------------------- | ----------------------- | --------------------------------------------------------------------------------- |
|
||||
| `CLAUDE_OAUTH_CLIENT_ID` | Claude Code (Anthropic) | Public client — no secret needed. |
|
||||
| `CLAUDE_CODE_REDIRECT_URI` | Claude Code | Override redirect URI. Default: `https://platform.claude.com/oauth/code/callback` |
|
||||
| `CODEX_OAUTH_CLIENT_ID` | Codex / OpenAI | Public client. |
|
||||
| `GEMINI_OAUTH_CLIENT_ID` | Gemini (Google) | Requires matching `_SECRET`. |
|
||||
| `GEMINI_OAUTH_CLIENT_SECRET` | Gemini (Google) | — |
|
||||
| `GEMINI_CLI_OAUTH_CLIENT_ID` | Gemini CLI | Usually same as Gemini. |
|
||||
| `GEMINI_CLI_OAUTH_CLIENT_SECRET` | Gemini CLI | — |
|
||||
| `QWEN_OAUTH_CLIENT_ID` | Qwen (Alibaba) | Public client. |
|
||||
| `KIMI_CODING_OAUTH_CLIENT_ID` | Kimi Coding (Moonshot) | Public client. |
|
||||
| `ANTIGRAVITY_OAUTH_CLIENT_ID` | Antigravity (Google) | Requires matching `_SECRET`. |
|
||||
| `ANTIGRAVITY_OAUTH_CLIENT_SECRET` | Antigravity (Google) | — |
|
||||
| `GITHUB_OAUTH_CLIENT_ID` | GitHub Copilot | Public client. |
|
||||
| `QODER_OAUTH_CLIENT_SECRET` | Qoder | — |
|
||||
| `QODER_OAUTH_AUTHORIZE_URL` | Qoder | Set to enable Qoder OAuth. |
|
||||
| `QODER_OAUTH_TOKEN_URL` | Qoder | — |
|
||||
| `QODER_OAUTH_USERINFO_URL` | Qoder | — |
|
||||
| `QODER_OAUTH_CLIENT_ID` | Qoder | — |
|
||||
| `QODER_PERSONAL_ACCESS_TOKEN` | Qoder | Direct API key fallback (bypasses OAuth). |
|
||||
| `QODER_CLI_WORKSPACE` | Qoder | Workspace ID for Qoder CLI. |
|
||||
| `OMNIROUTE_QODER_WORKSPACE` | Qoder | Alias for `QODER_CLI_WORKSPACE`. |
|
||||
| Variable | Provider | Notes |
|
||||
| --------------------------------- | ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `CLAUDE_OAUTH_CLIENT_ID` | Claude Code (Anthropic) | Public client — no secret needed. |
|
||||
| `CLAUDE_CODE_REDIRECT_URI` | Claude Code | Override redirect URI. Default: `https://platform.claude.com/oauth/code/callback` |
|
||||
| `CODEX_OAUTH_CLIENT_ID` | Codex / OpenAI | Public client. |
|
||||
| `GEMINI_OAUTH_CLIENT_ID` | Gemini (Google) | Requires matching `_SECRET`. |
|
||||
| `GEMINI_OAUTH_CLIENT_SECRET` | Gemini (Google) | — |
|
||||
| `GEMINI_CLI_OAUTH_CLIENT_ID` | Gemini CLI | Usually same as Gemini. |
|
||||
| `GEMINI_CLI_OAUTH_CLIENT_SECRET` | Gemini CLI | — |
|
||||
| `QWEN_OAUTH_CLIENT_ID` | Qwen (Alibaba) | Public client. |
|
||||
| `KIMI_CODING_OAUTH_CLIENT_ID` | Kimi Coding (Moonshot) | Public client. |
|
||||
| `ANTIGRAVITY_OAUTH_CLIENT_ID` | Antigravity (Google) | Requires matching `_SECRET`. |
|
||||
| `ANTIGRAVITY_OAUTH_CLIENT_SECRET` | Antigravity (Google) | — |
|
||||
| `GITHUB_OAUTH_CLIENT_ID` | GitHub Copilot | Public client. |
|
||||
| `WINDSURF_FIREBASE_API_KEY` | Windsurf / Devin (v3.8) | Public Firebase Web API key used by Windsurf's Secure Token Service to refresh short-lived browser-flow tokens. Client-side credential (not a secret). Long-lived import tokens skip this entirely. Source: extracted from Devin CLI binary. |
|
||||
| `WINDSURF_API_KEY` | Windsurf / Devin (v3.8) | API key fallback used by `open-sse/executors/devin-cli.ts` when no per-connection credential is available. Optional. |
|
||||
| `CLI_DEVIN_BIN` | Devin CLI (v3.8) | Custom path to the Devin CLI binary (`devin`). Resolved by `open-sse/executors/devin-cli.ts`. |
|
||||
| `GITLAB_DUO_OAUTH_CLIENT_ID` | GitLab Duo (v3.8) | OAuth client ID for GitLab Duo. Register an app at `https://gitlab.com/-/profile/applications` with redirect URI `<NEXT_PUBLIC_BASE_URL>/callback` and scopes `api, read_user, openid, profile, email`. Falls back to `GITLAB_OAUTH_CLIENT_ID`. |
|
||||
| `GITLAB_DUO_OAUTH_CLIENT_SECRET` | GitLab Duo (v3.8) | OAuth client secret for GitLab Duo. Optional — PKCE flow does not require a secret. Falls back to `GITLAB_OAUTH_CLIENT_SECRET`. |
|
||||
| `GITLAB_DUO_BASE_URL` | GitLab Duo (v3.8) | Override GitLab base URL (self-hosted GitLab). Defaults to `https://gitlab.com`. Falls back to `GITLAB_BASE_URL`. |
|
||||
| `QODER_OAUTH_CLIENT_SECRET` | Qoder | — |
|
||||
| `QODER_OAUTH_AUTHORIZE_URL` | Qoder | Set to enable Qoder OAuth. |
|
||||
| `QODER_OAUTH_TOKEN_URL` | Qoder | — |
|
||||
| `QODER_OAUTH_USERINFO_URL` | Qoder | — |
|
||||
| `QODER_OAUTH_CLIENT_ID` | Qoder | — |
|
||||
| `QODER_PERSONAL_ACCESS_TOKEN` | Qoder | Direct API key fallback (bypasses OAuth). |
|
||||
| `QODER_CLI_WORKSPACE` | Qoder | Workspace ID for Qoder CLI. |
|
||||
| `OMNIROUTE_QODER_WORKSPACE` | Qoder | Alias for `QODER_CLI_WORKSPACE`. |
|
||||
|
||||
> [!WARNING]
|
||||
> **Google OAuth** (Antigravity, Gemini CLI) credentials **only work on localhost**. For remote servers:
|
||||
@@ -335,15 +355,15 @@ process.env[`${PROVIDER_ID}_USER_AGENT`]
|
||||
|
||||
| Variable | Default Value | When to Update |
|
||||
| ------------------------ | --------------------------------------------- | ------------------------------------------------------------- |
|
||||
| `CLAUDE_USER_AGENT` | `claude-cli/2.1.121 (external, cli)` | When Anthropic releases a new CLI version |
|
||||
| `CODEX_USER_AGENT` | `codex-cli/0.125.0 (Windows 10.0.26100; x64)` | When OpenAI updates the Codex CLI |
|
||||
| `CODEX_CLIENT_VERSION` | `0.125.0` | Override Codex client version independently of full UA string |
|
||||
| `CLAUDE_USER_AGENT` | `claude-cli/2.1.137 (external, cli)` | When Anthropic releases a new CLI version |
|
||||
| `CODEX_USER_AGENT` | `codex-cli/0.130.0 (Windows 10.0.26200; x64)` | When OpenAI updates the Codex CLI |
|
||||
| `CODEX_CLIENT_VERSION` | `0.130.0` | Override Codex client version independently of full UA string |
|
||||
| `GITHUB_USER_AGENT` | `GitHubCopilotChat/0.45.1` | When GitHub Copilot Chat updates |
|
||||
| `ANTIGRAVITY_USER_AGENT` | `antigravity/1.107.0 darwin/arm64` | When Antigravity IDE updates |
|
||||
| `ANTIGRAVITY_USER_AGENT` | `antigravity/1.23.2 darwin/arm64` | When Antigravity IDE updates |
|
||||
| `KIRO_USER_AGENT` | `AWS-SDK-JS/3.0.0 kiro-ide/1.0.0` | When Kiro IDE updates |
|
||||
| `QODER_USER_AGENT` | `Qoder-Cli` | When Qoder CLI updates |
|
||||
| `QWEN_USER_AGENT` | `QwenCode/0.15.3 (linux; x64)` | When Qwen Code updates |
|
||||
| `CURSOR_USER_AGENT` | `connect-es/1.6.1` | When Cursor updates |
|
||||
| `QWEN_USER_AGENT` | `QwenCode/0.15.9 (linux; x64)` | When Qwen Code updates |
|
||||
| `CURSOR_USER_AGENT` | `Cursor/3.3` | When Cursor updates |
|
||||
| `GEMINI_CLI_USER_AGENT` | `google-api-nodejs-client/10.3.0` | When Google API client updates |
|
||||
|
||||
> [!TIP]
|
||||
@@ -562,13 +582,14 @@ Anthropic-compatible provider instead.
|
||||
|
||||
## 21. Proxy Health
|
||||
|
||||
| Variable | Default | Source File | Description |
|
||||
| ---------------------------- | ---------------- | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
|
||||
| `PROXY_FAST_FAIL_TIMEOUT_MS` | `2000` | `src/lib/proxyHealth.ts` | Fast-fail health check timeout. |
|
||||
| `PROXY_HEALTH_CACHE_TTL_MS` | `30000` | `src/lib/proxyHealth.ts` | Health check result cache TTL. |
|
||||
| `RATE_LIMIT_MAX_WAIT_MS` | `120000` (2 min) | `open-sse/services/rateLimitManager.ts` | Max time to wait on a 429 before failing the request. |
|
||||
| `REQUEST_RETRY` | `2` | `src/sse/services/cooldownAwareRetry.ts` | Number of automatic retries on model-scoped cooldown responses before returning error to client. |
|
||||
| `MAX_RETRY_INTERVAL_SEC` | `30` | `src/sse/services/cooldownAwareRetry.ts` | Max backoff interval (seconds) between cooldown retries. Capped by this value regardless of upstream `Retry-After`. |
|
||||
| Variable | Default | Source File | Description |
|
||||
| ---------------------------- | ---------------- | ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `PROXY_FAST_FAIL_TIMEOUT_MS` | `2000` | `src/lib/proxyHealth.ts` | Fast-fail health check timeout. |
|
||||
| `PROXY_HEALTH_CACHE_TTL_MS` | `30000` | `src/lib/proxyHealth.ts` | Health check result cache TTL. |
|
||||
| `RATE_LIMIT_MAX_WAIT_MS` | `120000` (2 min) | `open-sse/services/rateLimitManager.ts` | Max time to wait on a 429 before failing the request. |
|
||||
| `RATE_LIMIT_AUTO_ENABLE` | `false` | `open-sse/services/rateLimitManager.ts` | When `true`/`1`, automatically engages the rate-limit manager on first observed upstream 429 (without requiring manual toggle). Accepts `true`/`1`/`yes`. |
|
||||
| `REQUEST_RETRY` | `2` | `src/sse/services/cooldownAwareRetry.ts` | Number of automatic retries on model-scoped cooldown responses before returning error to client. |
|
||||
| `MAX_RETRY_INTERVAL_SEC` | `30` | `src/sse/services/cooldownAwareRetry.ts` | Max backoff interval (seconds) between cooldown retries. Capped by this value regardless of upstream `Retry-After`. |
|
||||
|
||||
---
|
||||
|
||||
@@ -657,6 +678,26 @@ CLI_COMPAT_ALL=1
|
||||
|
||||
---
|
||||
|
||||
## 24. Skills Sandbox (v3.8.0+)
|
||||
|
||||
Limits and safety knobs applied when the Skills framework (`src/lib/skills/`) executes user-defined automations in a sandboxed environment.
|
||||
|
||||
| Variable | Default | Source File | Description |
|
||||
| --------------------------------- | --------------------------------------------- | ---------------------------- | ------------------------------------------------------------------------------------------------------------------ |
|
||||
| `SKILLS_SANDBOX_TIMEOUT_MS` | `10000` (10 s) | `src/lib/skills/builtins.ts` | Per-execution wall-clock timeout for sandboxed skill code. Hard cap; anything longer is killed. |
|
||||
| `SKILLS_EXECUTION_TIMEOUT_MS` | _(falls back to `SKILLS_SANDBOX_TIMEOUT_MS`)_ | `src/lib/skills/` | High-level skill orchestration timeout. Set higher than `SKILLS_SANDBOX_TIMEOUT_MS` to allow multi-step workflows. |
|
||||
| `SKILLS_MAX_FILE_BYTES` | `1048576` (1 MB) | `src/lib/skills/builtins.ts` | Max bytes a skill may read from any single sandboxed file. |
|
||||
| `SKILLS_MAX_HTTP_RESPONSE_BYTES` | `256000` (250 KB) | `src/lib/skills/builtins.ts` | Max bytes captured from any single HTTP response inside a skill. |
|
||||
| `SKILLS_MAX_SANDBOX_OUTPUT_CHARS` | `100000` | `src/lib/skills/builtins.ts` | Hard cap on stdout/stderr characters returned from a sandbox invocation. |
|
||||
| `SKILLS_SANDBOX_NETWORK_ENABLED` | `false` | `src/lib/skills/builtins.ts` | Set `1`/`true` to allow outbound network from inside the sandbox. Defaults to **isolated** for safety. |
|
||||
| `SKILLS_ALLOWED_SANDBOX_IMAGES` | _(empty)_ | `src/lib/skills/builtins.ts` | Comma-separated allowlist of container images permitted for sandbox execution. Empty means built-in default only. |
|
||||
| `SKILLS_SANDBOX_DOCKER_IMAGE` | _(built-in default)_ | `src/lib/skills/` | Container image used when spawning a Docker-backed sandbox. Override to pin a custom hardened base image. |
|
||||
|
||||
> [!CAUTION]
|
||||
> Enabling `SKILLS_SANDBOX_NETWORK_ENABLED=true` opens an egress path from arbitrary skill code. Pair with `OUTBOUND_SSRF_GUARD_ENABLED=true` and a strict `CORS_ORIGIN`/proxy policy in shared deployments.
|
||||
|
||||
---
|
||||
|
||||
## Audit: Removed / Dead Variables
|
||||
|
||||
The following variables appeared in previous versions of `.env.example` but have **no runtime references** in the current codebase. They have been removed:
|
||||
|
||||
@@ -85,7 +85,7 @@ flyctl version
|
||||
### 4.1 获取代码并进入目录
|
||||
|
||||
```powershell
|
||||
git clone https://github.com/xiaoge1688/OmniRoute.git
|
||||
git clone https://github.com/diegosouzapw/OmniRoute.git
|
||||
cd OmniRoute
|
||||
```
|
||||
|
||||
@@ -139,6 +139,7 @@ flyctl deploy
|
||||
- `JWT_SECRET`
|
||||
- `MACHINE_ID_SALT`
|
||||
- `NEXT_PUBLIC_BASE_URL`
|
||||
- `OMNIROUTE_WS_BRIDGE_SECRET` (生产环境必需 / required in production / obrigatório em produção — 用于 WebSocket 桥接鉴权 / used for WebSocket bridge authentication)
|
||||
- `STORAGE_ENCRYPTION_KEY`
|
||||
|
||||
### 5.2 关于 `INITIAL_PASSWORD`
|
||||
@@ -162,20 +163,21 @@ flyctl deploy
|
||||
|
||||
建议放入 Fly Secrets:
|
||||
|
||||
| 变量名 | 是否推荐 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `API_KEY_SECRET` | 必需 | API Key 生成与校验使用 |
|
||||
| `JWT_SECRET` | 必需 | 登录态和 JWT 签名使用 |
|
||||
| `STORAGE_ENCRYPTION_KEY` | 强烈推荐 | 加密存储敏感连接信息 |
|
||||
| `MACHINE_ID_SALT` | 推荐 | 生成稳定机器标识 |
|
||||
| `INITIAL_PASSWORD` | 可选 | 首次部署时直接指定后台初始密码 |
|
||||
| OAuth/API 私密凭证 | 按需 | 各类外部平台鉴权配置 |
|
||||
| 变量名 | 是否推荐 | 说明 |
|
||||
| ---------------------------- | --------------------------------- | ----------------------------------------------------------------------------------------- |
|
||||
| `API_KEY_SECRET` | 必需 | API Key 生成与校验使用 |
|
||||
| `JWT_SECRET` | 必需 | 登录态和 JWT 签名使用 |
|
||||
| `OMNIROUTE_WS_BRIDGE_SECRET` | 生产必需 (required / obrigatório) | WebSocket 桥接鉴权密钥 (WebSocket bridge auth / chave de autenticação da ponte WebSocket) |
|
||||
| `STORAGE_ENCRYPTION_KEY` | 强烈推荐 | 加密存储敏感连接信息 |
|
||||
| `MACHINE_ID_SALT` | 推荐 | 生成稳定机器标识 |
|
||||
| `INITIAL_PASSWORD` | 可选 | 首次部署时直接指定后台初始密码 |
|
||||
| OAuth/API 私密凭证 | 按需 | 各类外部平台鉴权配置 |
|
||||
|
||||
### 6.2 当前项目推荐值
|
||||
|
||||
| 变量名 | 推荐值 |
|
||||
| --- | --- |
|
||||
| `DATA_DIR` | `/data` |
|
||||
| 变量名 | 推荐值 |
|
||||
| ---------------------- | --------------------------- |
|
||||
| `DATA_DIR` | `/data` |
|
||||
| `NEXT_PUBLIC_BASE_URL` | `https://omniroute.fly.dev` |
|
||||
|
||||
说明:
|
||||
@@ -183,6 +185,34 @@ flyctl deploy
|
||||
- `DATA_DIR=/data` 非常关键,必须与 Fly Volume 挂载点一致
|
||||
- `NEXT_PUBLIC_BASE_URL` 用于调度器和前端回调等场景
|
||||
|
||||
### 6.3 OAuth 回调地址配置 (OAuth callback URL / URL de callback OAuth)
|
||||
|
||||
如果你需要在 Fly.io 部署上启用 OAuth 登录类的 provider(例如 Antigravity、Gemini、Cursor 等),必须确保以下两点:
|
||||
(If you need to enable OAuth-based providers — e.g. Antigravity, Gemini, Cursor — on the Fly.io deployment, make sure of the following two points. / Se precisar habilitar providers via OAuth — p.ex. Antigravity, Gemini, Cursor — na implantação Fly.io, garanta os dois pontos abaixo.)
|
||||
|
||||
1. **设置 `NEXT_PUBLIC_BASE_URL` 指向你公开的 HTTPS 域名 (set `NEXT_PUBLIC_BASE_URL` to the public HTTPS domain / defina `NEXT_PUBLIC_BASE_URL` para o domínio HTTPS público)**
|
||||
|
||||
```powershell
|
||||
flyctl secrets set NEXT_PUBLIC_BASE_URL=https://omniroute.fly.dev -a omniroute
|
||||
```
|
||||
|
||||
如果你使用了自定义域名 (if using a custom domain / se usar um domínio personalizado),请替换为对应域名 (e.g. `https://omniroute.yourdomain.com`)。
|
||||
|
||||
2. **在 provider 控制台配置回调 URL (configure the callback URL on the provider console / configure a URL de callback no painel do provider)**
|
||||
|
||||
通常格式为 (typical format / formato típico):
|
||||
|
||||
```text
|
||||
<NEXT_PUBLIC_BASE_URL>/api/oauth/<provider>/callback
|
||||
```
|
||||
|
||||
例如 (e.g. / p.ex.):
|
||||
- `https://omniroute.fly.dev/api/oauth/gemini/callback`
|
||||
- `https://omniroute.fly.dev/api/oauth/antigravity/callback`
|
||||
- `https://omniroute.fly.dev/api/oauth/cursor/callback`
|
||||
|
||||
如果 `NEXT_PUBLIC_BASE_URL` 与 provider 控制台中注册的回调 URL 不一致,OAuth 流程会在浏览器回跳阶段失败 (mismatch between `NEXT_PUBLIC_BASE_URL` and the registered callback URL will cause OAuth to fail at the browser redirect step / divergência entre `NEXT_PUBLIC_BASE_URL` e a URL de callback registrada quebra o OAuth no redirect do navegador)。
|
||||
|
||||
---
|
||||
|
||||
## 7. 一键设置参数
|
||||
@@ -199,17 +229,29 @@ $apiKeySecret = [Convert]::ToHexString((1..32 | ForEach-Object { Get-Random -Min
|
||||
$jwtSecret = [Convert]::ToHexString((1..64 | ForEach-Object { Get-Random -Minimum 0 -Maximum 256 })).ToLower()
|
||||
$machineIdSalt = [Convert]::ToHexString((1..32 | ForEach-Object { Get-Random -Minimum 0 -Maximum 256 })).ToLower()
|
||||
$storageKey = [Convert]::ToHexString((1..32 | ForEach-Object { Get-Random -Minimum 0 -Maximum 256 })).ToLower()
|
||||
$wsBridgeSecret = [Convert]::ToHexString((1..32 | ForEach-Object { Get-Random -Minimum 0 -Maximum 256 })).ToLower()
|
||||
|
||||
flyctl secrets set `
|
||||
API_KEY_SECRET=$apiKeySecret `
|
||||
JWT_SECRET=$jwtSecret `
|
||||
MACHINE_ID_SALT=$machineIdSalt `
|
||||
STORAGE_ENCRYPTION_KEY=$storageKey `
|
||||
OMNIROUTE_WS_BRIDGE_SECRET=$wsBridgeSecret `
|
||||
DATA_DIR=/data `
|
||||
NEXT_PUBLIC_BASE_URL=https://omniroute.fly.dev `
|
||||
-a omniroute
|
||||
```
|
||||
|
||||
在 Linux / macOS 上,也可以直接用 `openssl rand -hex 32` 生成 (on Linux / macOS, you can also use `openssl rand -hex 32` / em Linux / macOS, também é possível usar `openssl rand -hex 32`):
|
||||
|
||||
```bash
|
||||
flyctl secrets set OMNIROUTE_WS_BRIDGE_SECRET=$(openssl rand -hex 32) -a omniroute
|
||||
```
|
||||
|
||||
说明 (notes / observações):
|
||||
|
||||
- `OMNIROUTE_WS_BRIDGE_SECRET` 在生产环境必需,缺失会导致 WebSocket 桥接握手失败 (required in production; missing it breaks WebSocket bridge handshake / obrigatório em produção; sem ele o handshake da ponte WebSocket falha)
|
||||
|
||||
如果你还要加初始密码:
|
||||
|
||||
```powershell
|
||||
@@ -282,6 +324,8 @@ git describe --tags --always
|
||||
git show --no-patch --oneline v3.4.7
|
||||
```
|
||||
|
||||
> 注 (note / nota):当前项目版本为 `v3.8.0` (current project version is `v3.8.0` / a versão atual do projeto é `v3.8.0`)。下文中的 `v3.4.7` 仅为历史示例 (the `v3.4.7` references below are kept as historical examples only / as referências a `v3.4.7` abaixo são apenas exemplos históricos);实际发布时请使用 `:latest` 或当前版本标签 (e.g. `:v3.8.0`) (use `:latest` or the current version tag — e.g. `:v3.8.0` — for actual releases / use `:latest` ou a tag da versão atual — p.ex. `:v3.8.0` — em releases reais)。
|
||||
|
||||
如果你想合并上游最新 `main`,并强制保留 fork 当前的 `fly.toml`,可按下面流程执行:
|
||||
|
||||
```powershell
|
||||
@@ -319,7 +363,7 @@ git merge-base --is-ancestor v3.4.7 upstream/main
|
||||
6. `flyctl status -a omniroute`
|
||||
7. `flyctl logs --no-tail -a omniroute`
|
||||
|
||||
这就是当前项目升级到 `v3.4.7` 时使用的实际流程。
|
||||
这就是当前项目升级到 `v3.4.7` 时使用的实际流程 (示例为历史版本,当前实际版本是 `v3.8.0` / example refers to a historical version; the current actual version is `v3.8.0` / o exemplo refere-se a uma versão histórica; a versão atual é `v3.8.0`)。
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -47,15 +47,42 @@ The [AUR package](https://aur.archlinux.org/packages/omniroute-bin) installs Omn
|
||||
### From Source
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
npm install
|
||||
PORT=20128 DASHBOARD_PORT=20129 NEXT_PUBLIC_BASE_URL=http://localhost:20129 npm run dev
|
||||
```
|
||||
|
||||
> **Note:** `npm install` auto-generates `.env` from `.env.example` on first run. Subsequent installs will not overwrite an existing `.env`, so customizations are preserved. To re-seed, delete `.env` before re-running.
|
||||
|
||||
### Docker
|
||||
|
||||
See the [Docker Guide](DOCKER_GUIDE.md) for complete Docker setup including Compose profiles and Caddy HTTPS.
|
||||
|
||||
### Desktop App (Electron)
|
||||
|
||||
OmniRoute ships a desktop wrapper built on Electron 41 + electron-builder 26.10. Available scripts (workspace root):
|
||||
|
||||
```bash
|
||||
npm run electron:dev # Run desktop with hot-reload
|
||||
npm run electron:build # Build for current OS (auto-detected)
|
||||
npm run electron:build:win # Windows installer (NSIS + portable)
|
||||
npm run electron:build:mac # macOS (dmg + zip, arm64+x64)
|
||||
npm run electron:build:linux # Linux (AppImage + deb + rpm)
|
||||
npm run electron:smoke:packaged # Smoke-test packaged build
|
||||
```
|
||||
|
||||
Releases of the desktop installers are attached to GitHub Releases. For the full Electron deep-dive (signing, IPC bridge, distros), see [`ELECTRON_GUIDE.md`](./ELECTRON_GUIDE.md) _(criado em fase posterior)_.
|
||||
|
||||
### Headless server (CI/automation)
|
||||
|
||||
For unattended setups (Docker, Kubernetes, CI), use:
|
||||
|
||||
```bash
|
||||
omniroute setup --non-interactive
|
||||
omniroute providers test-batch
|
||||
```
|
||||
|
||||
Combined with env vars (`INITIAL_PASSWORD`, `OMNIROUTE_WS_BRIDGE_SECRET`, etc.), this lets you spin up an OmniRoute instance fully scriptable.
|
||||
|
||||
### CLI Options
|
||||
|
||||
| Command | Description |
|
||||
@@ -253,7 +280,7 @@ For Void Linux users, you can build a native package using `xbps-src`. Save this
|
||||
```bash
|
||||
# Template file for 'omniroute'
|
||||
pkgname=omniroute
|
||||
version=3.4.1
|
||||
version=3.8.0
|
||||
revision=1
|
||||
hostmakedepends="nodejs python3 make"
|
||||
depends="openssl"
|
||||
@@ -262,7 +289,9 @@ maintainer="zenobit <zenobit@disroot.org>"
|
||||
license="MIT"
|
||||
homepage="https://github.com/diegosouzapw/OmniRoute"
|
||||
distfiles="https://github.com/diegosouzapw/OmniRoute/archive/refs/tags/v${version}.tar.gz"
|
||||
checksum=009400afee90a9f32599d8fe734145cfd84098140b7287990183dde45ae2245b
|
||||
# Regenerate the checksum for each release with:
|
||||
# curl -L -o /tmp/omniroute.tar.gz "https://github.com/diegosouzapw/OmniRoute/archive/refs/tags/v${version}.tar.gz" && sha256sum /tmp/omniroute.tar.gz
|
||||
checksum=PLACEHOLDER_REGENERATE_PER_RELEASE
|
||||
system_accounts="_omniroute"
|
||||
omniroute_homedir="/var/lib/omniroute"
|
||||
export NODE_ENV=production
|
||||
|
||||
@@ -14,7 +14,7 @@ Common problems and solutions for OmniRoute.
|
||||
| Dashboard opens on wrong port | Set `PORT=20128` and `NEXT_PUBLIC_BASE_URL=http://localhost:20128` |
|
||||
| No logs written to disk | Set `APP_LOG_TO_FILE=true` and verify call log capture is enabled |
|
||||
| EACCES: permission denied | Set `DATA_DIR=/path/to/writable/dir` to override `~/.omniroute` |
|
||||
| Routing strategy not saving | Update to v1.4.11+ (Zod schema fix for settings persistence) |
|
||||
| Routing strategy not saving | Update to the latest v3.x release (Zod schema fix for settings persistence shipped in earlier versions) |
|
||||
| Login crash / blank page | Check Node.js version — see [Node.js Compatibility](#nodejs-compatibility) below |
|
||||
| `dlopen` / `slice is not valid mach-o file` (macOS) | Run `cd $(npm root -g)/omniroute/app && npm rebuild better-sqlite3 && omniroute` — see [macOS native module rebuild](#macos-native-module-rebuild) below |
|
||||
| Proxy "fetch failed" | Ensure proxy config is set at the correct level — see [Proxy Issues](#proxy-issues) below |
|
||||
@@ -46,7 +46,7 @@ Common problems and solutions for OmniRoute.
|
||||
3. Reinstall OmniRoute: `npm install -g omniroute`
|
||||
4. Restart: `omniroute`
|
||||
|
||||
> **Supported secure versions:** `>=20.20.2 <21`, `>=22.22.2 <23`, or `>=24.0.0 <25`. Node.js 24.x LTS (Krypton) is fully supported.
|
||||
> **Supported secure versions:** `>=20.20.2 <21`, `>=22.22.2 <23`, or `>=24.0.0 <27`. Node.js 24.x LTS (Krypton) and Node.js 26 are fully supported.
|
||||
|
||||
### macOS: `dlopen` / "slice is not valid mach-o file"
|
||||
|
||||
@@ -72,7 +72,7 @@ npm rebuild better-sqlite3
|
||||
omniroute
|
||||
```
|
||||
|
||||
> **Note:** This recompiles the native binding against your local Node.js version and CPU architecture, resolving the binary mismatch. The officially supported range is **`>=20.20.2 <21`, `>=22.22.2 <23`, or `>=24.0.0 <25`** (`engines` field in `package.json`). Node.js 24.x LTS (Krypton) is fully supported with `better-sqlite3` v12.x.
|
||||
> **Note:** This recompiles the native binding against your local Node.js version and CPU architecture, resolving the binary mismatch. The officially supported range is **`>=20.20.2 <21`, `>=22.22.2 <23`, or `>=24.0.0 <27`** (`engines` field in `package.json`). Node.js 24.x LTS (Krypton) and Node.js 26 are fully supported with `better-sqlite3` v12.x.
|
||||
|
||||
---
|
||||
|
||||
@@ -268,10 +268,10 @@ Use **Dashboard → Translator** to debug format translation issues:
|
||||
- **Thinking tags not appearing** — Check if the target provider supports thinking and the thinking budget setting
|
||||
- **Tool calls dropping** — Some format translations may strip unsupported fields; verify in Playground mode
|
||||
- **System prompt missing** — Claude and Gemini handle system prompts differently; check translation output
|
||||
- **SDK returns raw string instead of object** — Fixed in v1.1.0: response sanitizer now strips non-standard fields (`x_groq`, `usage_breakdown`, etc.) that cause OpenAI SDK Pydantic validation failures
|
||||
- **GLM/ERNIE rejects `system` role** — Fixed in v1.1.0: role normalizer automatically merges system messages into user messages for incompatible models
|
||||
- **`developer` role not recognized** — Fixed in v1.1.0: automatically converted to `system` for non-OpenAI providers
|
||||
- **`json_schema` not working with Gemini** — Fixed in v1.1.0: `response_format` is now converted to Gemini's `responseMimeType` + `responseSchema`
|
||||
- **SDK returns raw string instead of object** — Resolved in v1.x; response sanitizer strips non-standard fields (`x_groq`, `usage_breakdown`, etc.) that cause OpenAI SDK Pydantic validation failures. If you still see this on v3.x+, please file an issue.
|
||||
- **GLM/ERNIE rejects `system` role** — Resolved in v1.x; role normalizer automatically merges system messages into user messages for incompatible models. If you still see this on v3.x+, please file an issue.
|
||||
- **`developer` role not recognized** — Resolved in v1.x; automatically converted to `system` for non-OpenAI providers. If you still see this on v3.x+, please file an issue.
|
||||
- **`json_schema` not working with Gemini** — Resolved in v1.x; `response_format` is now converted to Gemini's `responseMimeType` + `responseSchema`. If you still see this on v3.x+, please file an issue.
|
||||
|
||||
---
|
||||
|
||||
@@ -332,6 +332,118 @@ You can ignore this section if you do not run RAG or agent pipelines behind Omni
|
||||
|
||||
---
|
||||
|
||||
## v3.8.0 Known Issues
|
||||
|
||||
Issues specific to the v3.8.0 release and their current workarounds. If a fix lands in a later patch, the entry will be updated or removed.
|
||||
|
||||
### Windsurf OAuth flow fails with 401
|
||||
|
||||
**Symptoms:**
|
||||
|
||||
- "401 unauthorized" while completing the Windsurf OAuth flow from the dashboard
|
||||
- Windsurf provider card stays in "needs reconnection" state after the callback
|
||||
|
||||
**Causes:**
|
||||
|
||||
- `WINDSURF_FIREBASE_API_KEY` env var missing or empty
|
||||
- `WINDSURF_API_KEY` misconfigured or pointing at a stale token
|
||||
- Local firewall/proxy blocking the OAuth callback
|
||||
|
||||
**Fix:**
|
||||
|
||||
1. Verify both `WINDSURF_FIREBASE_API_KEY` and `WINDSURF_API_KEY` are set in `.env`
|
||||
2. Restart OmniRoute so the new env values are picked up
|
||||
3. Re-run the OAuth flow from **Dashboard → Providers → Windsurf → Reconnect**
|
||||
|
||||
### Devin CLI auth failures
|
||||
|
||||
**Symptoms:**
|
||||
|
||||
- "Devin CLI not found" or "auth failed" when invoking Devin-backed tools
|
||||
- CLI runtime check reports `installed=false`
|
||||
|
||||
**Causes:**
|
||||
|
||||
- `CLI_DEVIN_BIN` points to a path that does not exist
|
||||
- Devin CLI is not installed on the host
|
||||
|
||||
**Fix:**
|
||||
|
||||
1. Install the Devin CLI for your platform
|
||||
2. Set `CLI_DEVIN_BIN=/usr/local/bin/devin` (or the real path) in `.env`
|
||||
3. Restart OmniRoute and re-test from **Dashboard → CLI Tools**
|
||||
|
||||
### Model cooldown stuck (manual reset)
|
||||
|
||||
**Symptoms:**
|
||||
|
||||
- A model stays listed in cooldown even after the expiration time has passed
|
||||
- Requests still skip the model in combo routing despite the timestamp being in the past
|
||||
|
||||
**Manual reset:**
|
||||
|
||||
- **Dashboard:** **Settings → Model Cooldowns** → click **Re-enable** on the affected card
|
||||
- **API:** `DELETE /api/resilience/model-cooldowns` with management auth headers
|
||||
|
||||
### Command Code provider connection fails with 403
|
||||
|
||||
**Symptoms:**
|
||||
|
||||
- 403 when testing the Command Code provider connection
|
||||
- The provider card shows "unauthorized" after a fresh add
|
||||
|
||||
**Cause:** The OAuth flow did not complete (callback not received or token not persisted).
|
||||
|
||||
**Fix:**
|
||||
|
||||
- Run `omniroute providers` from the CLI to re-trigger the OAuth flow, or
|
||||
- Re-run OAuth from **Dashboard → Providers → Command Code → Reconnect**
|
||||
|
||||
### ModelScope returns aggressive 429 cooldowns
|
||||
|
||||
**Symptoms:**
|
||||
|
||||
- Very short or immediate cooldowns on ModelScope after a small burst of requests
|
||||
- Combo routing skips ModelScope earlier than expected
|
||||
|
||||
**Cause:** ModelScope emits provider-specific `Retry-After` headers. v3.8.0 ships dedicated handling for those headers, so older versions misread them as generic rate-limit hints.
|
||||
|
||||
**Fix:**
|
||||
|
||||
- Ensure you are on v3.8.0 or later
|
||||
- Verify the `useUpstream429BreakerHints` toggle is enabled under **Settings → Resilience**
|
||||
|
||||
### OMNIROUTE_WS_BRIDGE_SECRET missing in production
|
||||
|
||||
**Symptoms:**
|
||||
|
||||
- 401 on every Codex/Responses WebSocket bridge request when running on a remote production host
|
||||
- WebSocket bridge handshake closes immediately after connect
|
||||
|
||||
**Cause:** The `OMNIROUTE_WS_BRIDGE_SECRET` env var is missing from the production environment.
|
||||
|
||||
**Fix:**
|
||||
|
||||
1. Generate a random secret: `openssl rand -hex 32`
|
||||
2. Set `OMNIROUTE_WS_BRIDGE_SECRET=<random-secret>` in the production server env (and any client that talks to the bridge)
|
||||
3. Restart OmniRoute
|
||||
|
||||
### Responses API: background mode degraded to synchronous
|
||||
|
||||
**Symptoms:**
|
||||
|
||||
- Warning logged: `background mode degraded to synchronous`
|
||||
- A `background: true` request returns a normal synchronous response instead of a background job handle
|
||||
|
||||
**Cause:** v3.8.0 intentionally degrades `background: true` on the Responses API to synchronous execution while emitting a warning. Full async background execution is a future deliverable.
|
||||
|
||||
**Fix:**
|
||||
|
||||
- Adjust the client to call without `background`, or
|
||||
- Wait for a later release that ships full async background mode (track the changelog)
|
||||
|
||||
---
|
||||
|
||||
## Still Stuck?
|
||||
|
||||
- **GitHub Issues**: [github.com/diegosouzapw/OmniRoute/issues](https://github.com/diegosouzapw/OmniRoute/issues)
|
||||
|
||||
@@ -54,7 +54,7 @@ apt install -y ca-certificates curl gnupg
|
||||
install -m 0755 -d /etc/apt/keyrings
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||||
chmod a+r /etc/apt/keyrings/docker.gpg
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $ (. /etc/os-release && echo “$VERSION_CODENAME”) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $ (. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
apt update
|
||||
apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
||||
```
|
||||
@@ -91,7 +91,7 @@ mkdir -p /opt/omniroute
|
||||
### 2.2 Create environment variables file
|
||||
|
||||
```bash
|
||||
cat > /opt/omniroute/.env << ‘EOF’
|
||||
cat > /opt/omniroute/.env << 'EOF'
|
||||
# === Security ===
|
||||
JWT_SECRET=CHANGE-TO-A-UNIQUE-64-CHAR-SECRET-KEY
|
||||
INITIAL_PASSWORD=YourSecurePassword123!
|
||||
@@ -99,13 +99,13 @@ API_KEY_SECRET=REPLACE-WITH-ANOTHER-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY=REPLACE-WITH-THIRD-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY_VERSION=v1
|
||||
MACHINE_ID_SALT=CHANGE-TO-A-UNIQUE-SALT
|
||||
OMNIROUTE_WS_BRIDGE_SECRET=REPLACE-WITH-WS-BRIDGE-SECRET # REQUIRED em produção: usado pelo Codex Responses WS bridge
|
||||
|
||||
# === App ===
|
||||
PORT=20128
|
||||
NODE_ENV=production
|
||||
HOSTNAME=0.0.0.0
|
||||
DATA_DIR=/app/data
|
||||
STORAGE_DRIVER=sqlite
|
||||
APP_LOG_TO_FILE=true
|
||||
AUTH_COOKIE_SECURE=false
|
||||
REQUIRE_API_KEY=false
|
||||
@@ -173,7 +173,7 @@ chmod 600 /etc/nginx/ssl/origin.key
|
||||
### 3.2 Nginx Configuration
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/sites-available/omniroute << ‘NGINX’
|
||||
cat > /etc/nginx/sites-available/omniroute << 'NGINX'
|
||||
# Default server — blocks direct access via IP
|
||||
server {
|
||||
listen 80 default_server;
|
||||
@@ -208,7 +208,7 @@ server {
|
||||
# WebSocket support
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection “upgrade”;
|
||||
proxy_set_header Connection "upgrade";
|
||||
|
||||
# SSE (Server-Sent Events) — streaming AI responses
|
||||
proxy_buffering off;
|
||||
@@ -315,7 +315,7 @@ docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
```bash
|
||||
docker stop omniroute
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine sh -c “rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /”
|
||||
alpine sh -c "rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /"
|
||||
docker start omniroute
|
||||
```
|
||||
|
||||
@@ -326,7 +326,7 @@ docker start omniroute
|
||||
### Restrict nginx to Cloudflare IPs
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/cloudflare-ips.conf << ‘CF’
|
||||
cat > /etc/nginx/cloudflare-ips.conf << 'CF'
|
||||
# Cloudflare IPv4 ranges — update periodically
|
||||
# https://www.cloudflare.com/ips-v4/
|
||||
set_real_ip_from 173.245.48.0/20;
|
||||
|
||||
@@ -1433,22 +1433,6 @@ paths:
|
||||
"200":
|
||||
description: Translation history entries
|
||||
|
||||
/api/translator/load:
|
||||
get:
|
||||
tags: [Translator]
|
||||
summary: Load saved translation template
|
||||
responses:
|
||||
"200":
|
||||
description: Template data
|
||||
|
||||
/api/translator/save:
|
||||
post:
|
||||
tags: [Translator]
|
||||
summary: Save translation template
|
||||
responses:
|
||||
"200":
|
||||
description: Template saved
|
||||
|
||||
# ─── CLI Tools ─────────────────────────────────────────────────
|
||||
|
||||
/api/cli-tools/backups:
|
||||
@@ -2269,6 +2253,73 @@ paths:
|
||||
"200":
|
||||
description: Generated content
|
||||
|
||||
# ─── 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
|
||||
|
||||
components:
|
||||
securitySchemes:
|
||||
BearerAuth:
|
||||
@@ -2476,13 +2527,35 @@ components:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required: [role, content]
|
||||
required: [role]
|
||||
properties:
|
||||
role:
|
||||
type: string
|
||||
enum: [system, user, assistant]
|
||||
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
|
||||
@@ -2492,6 +2565,65 @@ components:
|
||||
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
|
||||
@@ -2637,7 +2769,21 @@ components:
|
||||
type: string
|
||||
strategy:
|
||||
type: string
|
||||
enum: [priority, weighted, round-robin, random, least-used, cost-optimized]
|
||||
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
|
||||
|
||||
@@ -95,7 +95,7 @@
|
||||
"test:protocols:e2e": "node scripts/run-protocol-clients-tests.mjs",
|
||||
"test:vitest": "vitest run --config vitest.mcp.config.ts",
|
||||
"test:ecosystem": "node scripts/run-ecosystem-tests.mjs",
|
||||
"test:coverage": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true c8 --output-dir coverage --exclude=tests/** --exclude=**/*.test.* --reporter=text-summary --reporter=html --reporter=json-summary --reporter=lcov --check-coverage --statements 60 --lines 60 --functions 60 --branches 60 node --import tsx/esm --test --test-concurrency=1 tests/unit/*.test.ts",
|
||||
"test:coverage": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true c8 --output-dir coverage --exclude=tests/** --exclude=**/*.test.* --reporter=text-summary --reporter=html --reporter=json-summary --reporter=lcov --check-coverage --statements 75 --lines 75 --functions 75 --branches 70 node --import tsx/esm --test --test-concurrency=1 tests/unit/*.test.ts",
|
||||
"test:coverage:legacy": "c8 --output-dir coverage --exclude=open-sse --check-coverage --lines 50 --functions 50 --branches 50 node --import tsx/esm --test tests/unit/*.test.ts",
|
||||
"coverage:report": "c8 report --output-dir coverage --exclude=tests/** --exclude=**/*.test.* --reporter=text --reporter=text-summary --reporter=html --reporter=json-summary --reporter=lcov",
|
||||
"coverage:summary": "node scripts/test-report-summary.mjs --input coverage/coverage-summary.json --output coverage/coverage-report.md --threshold 60",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { getAgent } from "@/lib/cloudAgent/registry";
|
||||
import type { CloudAgentTaskRow } from "@/lib/cloudAgent/db";
|
||||
import {
|
||||
createCloudAgentTaskTable,
|
||||
insertCloudAgentTask,
|
||||
@@ -19,8 +20,6 @@ import pino from "pino";
|
||||
|
||||
const logger = pino({ name: "cloud-agents-api" });
|
||||
|
||||
createCloudAgentTaskTable();
|
||||
|
||||
function getLimit(value: string | null): number {
|
||||
const parsed = Number.parseInt(value || "50", 10);
|
||||
if (!Number.isFinite(parsed)) return 50;
|
||||
@@ -36,12 +35,14 @@ export async function GET(request: NextRequest) {
|
||||
const authError = await requireCloudAgentManagementAuth(request);
|
||||
if (authError) return authError;
|
||||
|
||||
createCloudAgentTaskTable();
|
||||
|
||||
const { searchParams } = new URL(request.url);
|
||||
const providerId = searchParams.get("provider");
|
||||
const status = searchParams.get("status");
|
||||
const limit = getLimit(searchParams.get("limit"));
|
||||
|
||||
let tasks;
|
||||
let tasks: CloudAgentTaskRow[];
|
||||
if (providerId) {
|
||||
tasks = getCloudAgentTasksByProvider(providerId, limit);
|
||||
} else if (status) {
|
||||
@@ -107,6 +108,7 @@ export async function POST(request: NextRequest) {
|
||||
credentials
|
||||
);
|
||||
|
||||
createCloudAgentTaskTable();
|
||||
insertCloudAgentTask({
|
||||
id: task.id,
|
||||
provider_id: task.providerId,
|
||||
@@ -152,6 +154,8 @@ export async function DELETE(request: NextRequest) {
|
||||
const authError = await requireCloudAgentManagementAuth(request);
|
||||
if (authError) return authError;
|
||||
|
||||
createCloudAgentTaskTable();
|
||||
|
||||
const { searchParams } = new URL(request.url);
|
||||
const taskId = searchParams.get("id");
|
||||
|
||||
|
||||
@@ -85,13 +85,13 @@ export const CloudAgentActivitySchema = z.object({
|
||||
type: z.enum(["plan", "command", "code_change", "message", "error", "completion"]),
|
||||
content: z.string(),
|
||||
timestamp: z.string().datetime(),
|
||||
metadata: z.record(z.unknown()).optional(),
|
||||
metadata: z.record(z.string(), z.unknown()).optional(),
|
||||
});
|
||||
|
||||
export const CloudAgentTaskOptionsSchema = z.object({
|
||||
autoCreatePr: z.boolean().optional(),
|
||||
planApprovalRequired: z.boolean().optional(),
|
||||
environment: z.record(z.string()).optional(),
|
||||
environment: z.record(z.string(), z.string()).optional(),
|
||||
});
|
||||
|
||||
export const CreateCloudAgentTaskSchema = z.object({
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
const { CLOUD_AGENT_STATUS, CloudAgentStatusSchema, CloudAgentTask } =
|
||||
const { CLOUD_AGENT_STATUS, CloudAgentStatusSchema } =
|
||||
await import("../../src/lib/cloudAgent/types.ts");
|
||||
const { CreateCloudAgentTaskSchema, UpdateCloudAgentTaskSchema } =
|
||||
await import("../../src/lib/cloudAgent/types.ts");
|
||||
|
||||
Reference in New Issue
Block a user