mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-19 13:23:50 +03:00
* fix(cli): escalate the readiness probe timeout so a slow health response is not a phantom boot failure `omniroute serve` reported "Server did not respond within 60s" over servers that were up and serving traffic. Every probe of /api/monitoring/health was aborted at a fixed 2s, and a timed-out probe is classified "hanging", which never counts toward readiness (#6800). So whenever the first health response takes longer than 2s the poll can never succeed: each abort discards the in-flight request before the route finishes (its own 1s payload cache is never populated either), and 500ms later the next probe restarts the same work into the same ceiling, for the whole 60s budget. Reproduced by the new test: against a health route that answers 200 in 3.2s, the old poller ran 12 probes over 30s and reported ready=false every time. The per-probe timeout now escalates after each hang (2s, 4s, 8s, 15s), clamped to the time left in the budget so the caller's total timeout still holds. Only a hang escalates, so #6800's guarantee is unchanged: a socket that accepts TCP and never answers still resolves false. waitForServer also reports each probe outcome to an optional onOutcome callback, and the readiness-timeout diagnostic uses it to say whether the port was accepting connections, which separates "up and still warming" from "never bound the port". Same failure family as #10508, which fixed it by taking a DNS lookup out of the 2s budget rather than by widening it. The heavy /api/monitoring/health route is what makes that budget tight in the first place (its own docstring points high-frequency pollers at /api/health/ping, which is what the Electron readiness poller uses); switching the CLI probe route is a larger change, left as a follow-up. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CJf2dxEpiwZqyZujWk57T2 * chore(changelog): link the readiness-probe fix to PR 12484 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CJf2dxEpiwZqyZujWk57T2 --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
bin/cli — OmniRoute CLI internals
This directory contains the CLI runtime, helpers, and commands for the omniroute binary.
Structure
bin/cli/
├── CONVENTIONS.md ← normative design rules (read this first)
├── README.md ← this file
├── program.mjs ← Commander setup — global flags, registerCommands()
├── api.mjs ← apiFetch() — all HTTP calls + retry/backoff
├── runtime.mjs ← withRuntime() — server-first / DB-fallback
├── i18n.mjs ← t() — i18n helper + locale detection
├── output.mjs ← emit() — table/json/jsonl/csv + printSuccess/printError
├── io.mjs ← ask() / askSecret() — interactive prompts
├── data-dir.mjs ← resolveDataDir() / resolveStoragePath()
├── sqlite.mjs ← openOmniRouteDb() — DB bootstrap
├── encryption.mjs ← encrypt/decrypt credentials
├── provider-catalog.mjs ← static provider catalog
├── provider-store.mjs ← DB CRUD for provider_connections
├── provider-test.mjs ← testProviderApiKey()
├── settings-store.mjs ← DB CRUD for key_value settings
├── locales/
│ ├── en.json ← English strings (source of truth, 42 locales)
│ ├── pt-BR.json ← Portuguese (Brazil) — fully translated
│ └── {locale}.json ← 41 additional locales (ar, az, de, es, fr, ja, zh-CN, …)
├── scripts/
│ └── generate-locales.mjs ← scaffold new locale files from config/i18n.json
└── commands/
├── setup.mjs
├── doctor.mjs
├── providers.mjs
├── config.mjs ← includes `config lang get/set/list`
├── status.mjs
├── logs.mjs
└── update.mjs
Key helpers
apiFetch(path, opts) — api.mjs
All HTTP calls to the OmniRoute server must go through this wrapper.
import { apiFetch } from "./api.mjs";
const res = await apiFetch("/api/health");
if (!res.ok) await res.assertOk(); // throws ApiError with mapped exit code
const data = await res.json();
Options:
baseUrl— override base URL (default:OMNIROUTE_BASE_URLenv orlocalhost:20128)apiKey— override API key (default:OMNIROUTE_API_KEY)method,body,headers— standard fetch optionstimeout— per-attempt ms (default:30000)retry—falseto disable (default: enabled)retryMax— total attempts (default:3)verbose— log retry attempts to stderr
withRuntime(fn, opts) — runtime.mjs
Provides server-first / DB-fallback transparently.
import { withRuntime } from "./runtime.mjs";
await withRuntime(async (ctx) => {
if (ctx.kind === "http") {
const res = await ctx.api("/v1/providers");
return res.json();
}
return ctx.db.prepare("SELECT * FROM provider_connections").all();
});
opts.requireServer = true— throwsServerOfflineError(exit 3) if offlineopts.preferDb = true— always use DB (skip server check)
t(key, vars) — i18n.mjs
Internationalized strings. Catalog loaded from locales/{locale}.json.
import { t } from "./i18n.mjs";
console.log(t("common.serverOffline"));
console.log(t("setup.testFailed", { error: err.message }));
Locale detection order: OMNIROUTE_LANG → LC_ALL → LC_MESSAGES → LANG → en.
emit(data, opts) — output.mjs
Format-aware output. Reads opts.output to select table/json/jsonl/csv.
import { emit, printError, EXIT_CODES } from "./output.mjs";
emit(providers, { output: opts.output ?? "table" });
printError("Something went wrong");
process.exit(EXIT_CODES.SERVER_OFFLINE);
Locale selection
The CLI displays text in the user's language. Detection order:
--lang <code>flag on the command lineOMNIROUTE_LANGenvironment variable- System env:
LC_ALL→LC_MESSAGES→LANG - Fallback:
en
Set permanently:
omniroute config lang set pt-BR # saves to ~/.omniroute/.env
omniroute config lang list # show all 42 available locales
omniroute config lang get # show currently active locale
One-time override:
omniroute --lang de providers list # run in German, not persisted
OMNIROUTE_LANG=ja omniroute status # same effect via env
Adding a new locale: add entry to config/i18n.json, then run:
node bin/cli/scripts/generate-locales.mjs
Adding a new command
- Create
bin/cli/commands/your-command.mjs - Export
registerYourCommand(program)following the Commander pattern - Register in
bin/cli/commands/registry.mjs - Add strings to
locales/en.jsonandlocales/pt-BR.json - Write test in
tests/unit/cli-your-command.test.ts
See CONVENTIONS.md for exit codes, flag naming, output format, and destructive-action rules.