mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-06 23:32:12 +03:00
fix(ci): clear base-reds on release/v3.8.50 (migration collision + 4 masked gates) (#9600)
Validated in local merge-train (diegosouzapw batch)
This commit is contained in:
committed by
GitHub
parent
ba0a0751c4
commit
f2e36ad0ce
@@ -524,7 +524,7 @@
|
||||
"_rebaseline_pr1043_minimax_tts": "Upstream port decolua/9router#1043 (toanalien) own growth: audioSpeech.ts 965->1061 (+96). Adds MiniMax T2A v2 TTS dispatch (handleMinimaxSpeech + hexToBytes helper) — provider entry was already in audioRegistry (format: minimax-tts) but no handler existed, falling through to the OpenAI-compatible default that fails (T2A has custom shape + hex-encoded audio + base_resp envelope). New branch sits next to the other inline provider branches (xiaomi-mimo, coqui, tortoise, aws-polly) — extracting would just create indirection. Covered by tests/unit/minimax-tts-1043.test.ts (3 tests, GREEN: success, base_resp error, invalid-hex).",
|
||||
"_rebaseline_pr4592_exclude_exhausted_auto": "Reconcile #4592 already-merged growth: combo.ts 2991->3036 (+45, terminal-status quota-cutoff exclusion in buildAutoCandidates + opt-in gate). Fast-gate PR->release does not run check:file-size.",
|
||||
"open-sse/executors/antigravity.ts": 1528,
|
||||
"open-sse/executors/base.ts": 1635,
|
||||
"open-sse/executors/base.ts": 1640,
|
||||
"open-sse/executors/chatgpt-web.ts": 3241,
|
||||
"open-sse/executors/codex.ts": 1562,
|
||||
"open-sse/executors/cursor.ts": 1560,
|
||||
@@ -539,7 +539,7 @@
|
||||
"open-sse/mcp-server/schemas/tools.ts": 1505,
|
||||
"open-sse/mcp-server/server.ts": 1411,
|
||||
"open-sse/mcp-server/tools/advancedTools.ts": 1120,
|
||||
"open-sse/services/accountFallback.ts": 1966,
|
||||
"open-sse/services/accountFallback.ts": 1972,
|
||||
"open-sse/services/adobeFireflyClient.ts": 2385,
|
||||
"open-sse/services/claudeCodeCompatible.ts": 1202,
|
||||
"open-sse/services/combo.ts": 3648,
|
||||
@@ -558,7 +558,7 @@
|
||||
"src/app/(dashboard)/dashboard/endpoint/EndpointPageClient.tsx": 2615,
|
||||
"src/app/(dashboard)/dashboard/health/page.tsx": 1165,
|
||||
"src/app/(dashboard)/dashboard/providers/[id]/components/modals/EditConnectionModal.tsx": 1316,
|
||||
"src/app/(dashboard)/dashboard/providers/page.tsx": 1928,
|
||||
"src/app/(dashboard)/dashboard/providers/page.tsx": 1944,
|
||||
"src/app/(dashboard)/dashboard/runtime/RuntimePageClient.tsx": 1201,
|
||||
"src/app/(dashboard)/dashboard/settings/components/PricingTab.tsx": 1019,
|
||||
"src/app/(dashboard)/dashboard/settings/components/ProxyRegistryManager.tsx": 1464,
|
||||
@@ -573,7 +573,7 @@
|
||||
"src/lib/tokenHealthCheck.ts": 1021,
|
||||
"src/lib/db/apiKeys.ts": 1529,
|
||||
"src/lib/db/core.ts": 1637,
|
||||
"src/lib/db/migrationRunner.ts": 1077,
|
||||
"src/lib/db/migrationRunner.ts": 1084,
|
||||
"src/lib/db/models.ts": 1097,
|
||||
"src/lib/db/providers.ts": 1034,
|
||||
"src/lib/memory/retrieval.ts": 1073,
|
||||
@@ -605,5 +605,6 @@
|
||||
"_rebaseline_2026_08_02_v3850_agentrouter_responses": "Release v3.8.50 AgentRouter/Codex compatibility reconciliation. open-sse/executors/base.ts 1562->1578: #9190 wires AgentRouter's selected Claude/OpenAI/Responses protocol through the existing executor URL, auth, identity-header and fingerprint chokepoints; the reusable alternate resolver remains outside base.ts. open-sse/utils/stream.ts 2887->2889: #9213 evaluates Responses ID and usage normalization independently so response.completed always receives finite usage.total_tokens instead of short-circuiting after an ID rewrite. tests/unit/chatcore-translation-paths.test.ts 2769->2776: #9191 updates the existing Claude-Code bridge assertions for the dynamic AgentRouter wire image. PR #9224 offsets its own chatCore growth by extracting the AgentRouter protocol decisions into chatCore/agentRouterProtocol.ts, leaving chatCore below its frozen ceiling. Covered by agentrouter executor/chatCore protocol tests, chatcore translation-path tests, and responses-commentary-passthrough tests.",
|
||||
"_rebaseline_2026_08_05_9323_agentrouter_waf_retry": "PR #9323 (fix(agentrouter): retry on 400 content-blocked + burst guard) own growth: open-sse/executors/base.ts 1578->1623 (check-file-size.mjs conta via split(\"\\n\").length; wc -l ve 1622). As +45 linhas sao o WAF_RETRY_CONFIG + o burst guard via gateOutboundRequest() para o WAF do agentrouter.org, com comentarios explicando o porque de cada mitigacao e cobertos por tests/unit/base-executor-waf-retry.test.ts e tests/unit/wafRateLimit.test.ts. Crescimento funcional legitimo, nao inchaco.",
|
||||
"_rebaseline_2026_08_05_9529_own_growth": "PR #9529 own growth (base release/v3.8.50 medida EXATAMENTE nos frozen antigos, entao o modo base-relative #8522 nao cobre): open-sse/services/rateLimitManager.ts 1060->1105 (+45: helper applyLimiterSettings() que re-arma o heartbeat do reservoir apos updateSettings — fix do bug Bottleneck 2.19.5 que congelava a fila weighted; TDD em tests/unit/ratelimit-reservoir-refresh.test.ts); tests/integration/chat-pipeline.test.ts 1592->1598 (+6: User-Agent do codex derivado de getCodexClientVersion() em vez de literal pinado — teste-irmao alinhado ao contrato); tests/unit/provider-validation-specialty.test.ts 2980->2985 (+5: cobertura NOVA claude-web 429 -> valid:false, alinhamento #9406); open-sse/translator/response/openai-responses.ts 1174->1204 (+30: buildResponsesReasoningSummaryDelta MOVIDA do leaf pureHelpers.ts para o host — a funcao do #9500 muta stream state e violava o contrato do leaf puro; o LOC total do par host+leaf nao cresceu, o pureHelpers encolheu o mesmo tanto). Crescimento por fix de producao + cobertura adicional + realocacao arquitetural, nao inchaco.",
|
||||
"_rebaseline_2026_08_06_v3850_inherited_drift_reconcile": "Reconciliacao 2026-08-06 do drift ACUMULADO da release/v3.8.50 apos o lote de merges de 08-05/06: 13 arquivos acima do frozen no tip puro 8180b49ce1 (medidos pelo proprio gate). O modo PR base-relative (#8522) deixa PRs inocentes passarem, e os rebaselines individuais dos PRs se perderam nas resolucoes sucessivas de conflito deste hot-file — o drift so aparece no modo absoluto (nightly/local). Crescimentos funcionais dos PRs mergeados: #9024 topology click-nav src/app/(dashboard)/dashboard/HomePageClient.tsx; #9324 OpenRouter enrich src/app/(dashboard)/dashboard/providers/page.tsx; #9329 quota card ordering src/app/(dashboard)/dashboard/usage/components/ProviderLimits/index.tsx; #9193 context-window suffixes src/sse/handlers/chat.ts; #9332 nested Claude server tool ids open-sse/executors/base.ts; #9228 strip orphaned tool outputs open-sse/executors/codex.ts; #9236 nvidia tool-name normalize open-sse/executors/default.ts; #9314 nested tool_call validation open-sse/executors/kiro.ts; #9260 caller identity REST hops open-sse/mcp-server/server.ts; #8934 cache breakpoints tests tests/unit/chatcore-translation-paths.test.ts; #9193 suffix tests tests/unit/combo-routing-engine.test.ts; #9196 reasoning-on-tool-finish tests tests/unit/sse-auth.test.ts; #9163 GPT-5.6 Max reasoning tests tests/unit/translator-openai-to-kiro.test.ts. default.ts e kiro.ts entram no frozen (estavam sem entrada, acima do cap 1000). Atualizacao pos-medicao (a base avancou durante o ciclo do PR): src/sse/handlers/chat.ts 1857->1877 (#9184 affinity EOF evict) e open-sse/executors/default.ts 1027->1042 (#9005 Kimi K3 tool-name backfill)."
|
||||
"_rebaseline_2026_08_06_v3850_inherited_drift_reconcile": "Reconciliacao 2026-08-06 do drift ACUMULADO da release/v3.8.50 apos o lote de merges de 08-05/06: 13 arquivos acima do frozen no tip puro 8180b49ce1 (medidos pelo proprio gate). O modo PR base-relative (#8522) deixa PRs inocentes passarem, e os rebaselines individuais dos PRs se perderam nas resolucoes sucessivas de conflito deste hot-file — o drift so aparece no modo absoluto (nightly/local). Crescimentos funcionais dos PRs mergeados: #9024 topology click-nav src/app/(dashboard)/dashboard/HomePageClient.tsx; #9324 OpenRouter enrich src/app/(dashboard)/dashboard/providers/page.tsx; #9329 quota card ordering src/app/(dashboard)/dashboard/usage/components/ProviderLimits/index.tsx; #9193 context-window suffixes src/sse/handlers/chat.ts; #9332 nested Claude server tool ids open-sse/executors/base.ts; #9228 strip orphaned tool outputs open-sse/executors/codex.ts; #9236 nvidia tool-name normalize open-sse/executors/default.ts; #9314 nested tool_call validation open-sse/executors/kiro.ts; #9260 caller identity REST hops open-sse/mcp-server/server.ts; #8934 cache breakpoints tests tests/unit/chatcore-translation-paths.test.ts; #9193 suffix tests tests/unit/combo-routing-engine.test.ts; #9196 reasoning-on-tool-finish tests tests/unit/sse-auth.test.ts; #9163 GPT-5.6 Max reasoning tests tests/unit/translator-openai-to-kiro.test.ts. default.ts e kiro.ts entram no frozen (estavam sem entrada, acima do cap 1000). Atualizacao pos-medicao (a base avancou durante o ciclo do PR): src/sse/handlers/chat.ts 1857->1877 (#9184 affinity EOF evict) e open-sse/executors/default.ts 1027->1042 (#9005 Kimi K3 tool-name backfill).",
|
||||
"_rebaseline_2026_08_06b_v3850_sweepreds_drift": "Segunda reconciliacao de 2026-08-06 (/sweep-reds sobre o tip puro 2ddbbc61a6): 3 arquivos voltaram a passar do frozen apos os merges do mesmo dia, com atribuicao 1:1 por commit. (1) src/app/(dashboard)/dashboard/providers/page.tsx 1928->1944 e (2) open-sse/executors/base.ts 1635->1640, ambos do #9515 (feat(radar): flag-gated signed free-model catalog overlay, commit e7f6b1d130) — o overlay do Radar entra por wiring nos chokepoints ja existentes (a resolucao/verificacao do catalogo assinado mora fora destes dois arquivos); +16 e +5 linhas liquidas nao sao extraiveis sem inventar um leaf por callsite. (3) open-sse/services/accountFallback.ts 1966->1972 do #8704 (commit c4527f97bd), +6 linhas de dados em CREDITS_EXHAUSTED_SIGNALS ('has been exhausted', fixes #8631). src/sse/handlers/chat.ts 1880>1877 tambem estava violando e NAO entra aqui de proposito: e drenado por encolhimento na PR #9598, sem rebaseline. Crescimento proprio DESTA PR: src/lib/db/migrationRunner.ts 1077->1084 (+7) — o guard retroativo em isSchemaAlreadyApplied para os arquivos renumerados 137/138, exigido pela propria mensagem de erro de colisao do runner (ambas as migracoes sao ALTER TABLE ADD COLUMN puro, nao idempotente). Dois `case` + dois `return hasColumn(...)` + 3 linhas de comentario dentro do switch existente; nao extraivel."
|
||||
}
|
||||
|
||||
@@ -82,9 +82,10 @@
|
||||
"tightenSlack": 10
|
||||
},
|
||||
"openapiCoverage.pct": {
|
||||
"value": 38,
|
||||
"value": 39.2,
|
||||
"direction": "up",
|
||||
"eps": 0.5,
|
||||
"_tighten_2026_08_06_v3850_sweepreds": "38.0 -> 39.2 (aperto EXIGIDO pelo step 'Require-tighten (blocking)', que estava vermelho em ~60 PRs abertas de release/v3.8.50 — base-red herdado, nao defeito das PRs). A cobertura melhorou no ciclo porque as rotas novas entraram documentadas. 39.2 = valor medido pelo CI Quality Ratchet no run 31088889488; o tip puro 2ddbbc61a6 mede 39.3 localmente (npm run check:openapi-coverage: 247/628 rotas), entao 39.2 e o valor conservador dos dois. Aperto = gate mais ESTRITO, nunca mascaramento.",
|
||||
"_tighten_2026_07_04_v3844_release": "36.9 -> 39.3 (aperto exigido pelo --require-tighten no PR de release #5925). A cobertura OpenAPI melhorou no ciclo (9 rotas documentadas em 8fb020676 + as rotas novas de #5939/#5817/#6034/#5998 documentadas junto das features). 39.3 = valor medido pelo CI Quality Ratchet no run 28708141003 (tip 00c55afcb).",
|
||||
"_rebaseline_2026_06_28_v3839_release": "37.8 -> 36.9 (-0.9, beyond the 0.5 eps). v3.8.39 cycle drift surfaced ONLY on the release PR (the openapi-coverage ratchet does NOT run on PR->release fast-gates). The cycle added API/internal routes (antigravity paste-credentials onboarding, CCR ranged/grep/stats retrieve params, mcp 404 session handling) faster than docs/openapi.yaml coverage; documenting LOCAL_ONLY/internal onboarding routes in the PUBLIC spec would be gaming (same precedent as _rebaseline_2026_06_18_v3828_cycle_close). Measured by CI collect-metrics (run 28317145160) = 36.9. My release-finalize tree touches no routes (only the openapi.yaml version bump). Raising coverage by documenting public routes is tracked as follow-up doc debt.",
|
||||
"_rebaseline_2026_06_23_v3834_release": "38.4 -> 37.8 (-0.6, beyond the 0.5 eps so it failed the ratchet). v3.8.34 cycle drift: contributor PRs added API routes (e.g. quota/usage/opencode-go endpoints) faster than openapi.yaml coverage; the openapi-coverage ratchet does NOT run on PR->release fast-gates so it surfaced only on the release PR. Verified my release-finalize working tree touches no routes / openapi paths (only version bump in openapi.yaml). Measured by CI quality:collect (run 28000387577) = 37.8. Raising coverage by documenting the new routes is tracked as follow-up doc debt.",
|
||||
|
||||
@@ -223,6 +223,7 @@ import { recordCost } from "@/domain/costRules";
|
||||
import { calculateCost } from "@/lib/usage/costCalculator";
|
||||
import {
|
||||
buildClaudePassthroughToolNameMap,
|
||||
mergeResponseToolNameMap,
|
||||
normalizeOpenAIToolFinishReasons,
|
||||
restoreNonStreamingToolNames,
|
||||
} from "./chatCore/passthroughToolNames.ts";
|
||||
|
||||
@@ -192,12 +192,17 @@ describe("TierResolver", () => {
|
||||
});
|
||||
|
||||
it("uses cache for repeated models", () => {
|
||||
classifyTiers([
|
||||
clearTierCache();
|
||||
const results = classifyTiers([
|
||||
{ provider: "openai", model: "gpt-4o" },
|
||||
{ provider: "openai", model: "gpt-4o" },
|
||||
]);
|
||||
// If cache works, second call should be instant; test passes if no error
|
||||
expect(true).toBe(true);
|
||||
// Observable effect of the cache: the duplicate resolves to the same tier and only
|
||||
// ONE entry is memoized (getTierStats counts cache entries, not classify calls).
|
||||
assert.equal(results.length, 2);
|
||||
assert.equal(results[0].tier, results[1].tier);
|
||||
const stats = getTierStats();
|
||||
assert.equal(stats.free + stats.cheap + stats.premium, 1);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import { handlePipelineChat, type PipelineStep } from "../pipeline.ts";
|
||||
import type { resolveComboSetupConfig } from "../comboConfig.ts";
|
||||
import { clampComboDepth, MAX_GLOBAL_ATTEMPTS, resolveDelayMs } from "./comboPredicates.ts";
|
||||
import { resolveComboRuntimeUnits, resolveComboTargets } from "./comboStructure.ts";
|
||||
import { isComboModelVisible } from "./comboVisibility.ts";
|
||||
import { buildFusionHandleSingleModel, extractFusionPanelSpec } from "./fusionPanel.ts";
|
||||
import {
|
||||
clampStickyWeightedTargetLimit,
|
||||
@@ -340,12 +341,20 @@ export async function tryFusionDispatch(args: {
|
||||
runCombo: RunCombo;
|
||||
}): Promise<Response | null> {
|
||||
const { cfg, combo, config, strategy, log } = args;
|
||||
const judgeModel = typeof cfg.judgeModel === "string" ? cfg.judgeModel : undefined;
|
||||
const configuredJudge = typeof cfg.judgeModel === "string" ? cfg.judgeModel : undefined;
|
||||
// The panel is filtered for hidden models by resolveComboTargets, but the
|
||||
// explicit judge is a bare string that never passes through it (#8878). Drop a
|
||||
// hidden judge so fusion falls back to a surviving panel member instead of
|
||||
// dispatching a model the operator hid.
|
||||
const judgeModel =
|
||||
configuredJudge && !isComboModelVisible(configuredJudge, null, args.hiddenModelsByProvider)
|
||||
? undefined
|
||||
: configuredJudge;
|
||||
const fusionTuning =
|
||||
cfg.fusionTuning && typeof cfg.fusionTuning === "object"
|
||||
? (cfg.fusionTuning as FusionTuning)
|
||||
: undefined;
|
||||
if (strategy !== "fusion" && (judgeModel || fusionTuning)) {
|
||||
if (strategy !== "fusion" && (configuredJudge || fusionTuning)) {
|
||||
log.warn(
|
||||
"COMBO",
|
||||
`Combo "${combo.name}" sets config.judgeModel/fusionTuning but strategy is "${strategy}" — these fields are only consumed by the fusion strategy and will be ignored (#6455)`
|
||||
@@ -353,16 +362,30 @@ export async function tryFusionDispatch(args: {
|
||||
}
|
||||
if (strategy !== "fusion") return null;
|
||||
|
||||
const { panel: fusionModels, comboRefUnits } = extractFusionPanelSpec(
|
||||
resolveComboTargets(
|
||||
combo,
|
||||
args.allCombos,
|
||||
clampComboDepth(config.maxComboDepth),
|
||||
args.hiddenModelsByProvider
|
||||
).map((target) => target.modelStr),
|
||||
const resolvedFusionTargets = resolveComboTargets(
|
||||
combo,
|
||||
args.allCombos,
|
||||
clampComboDepth(config.maxComboDepth),
|
||||
args.hiddenModelsByProvider
|
||||
);
|
||||
// extractFusionPanelSpec only understands model strings / combo refs, so the
|
||||
// resolved targets have to be flattened before it runs. Keep them indexed so
|
||||
// the panel can be rehydrated below — dispatching the bare strings strips
|
||||
// `providerId` and every panel member loses its provider identity (#8878).
|
||||
const resolvedByModelStr = new Map<string, (typeof resolvedFusionTargets)[number]>();
|
||||
for (const target of resolvedFusionTargets) {
|
||||
if (!resolvedByModelStr.has(target.modelStr)) resolvedByModelStr.set(target.modelStr, target);
|
||||
}
|
||||
const { panel: fusionPanel, comboRefUnits } = extractFusionPanelSpec(
|
||||
resolvedFusionTargets.map((target) => target.modelStr),
|
||||
combo.name,
|
||||
null
|
||||
);
|
||||
// A panel entry naming a combo ref stays a string (it is a combo name, not a
|
||||
// model); everything else regains its resolved target.
|
||||
const fusionModels = fusionPanel.map((entry) =>
|
||||
comboRefUnits.has(entry) ? entry : (resolvedByModelStr.get(entry) ?? entry)
|
||||
);
|
||||
// Untyped like the existing `nestingContext` further down — `nesting` is
|
||||
// already `ComboNestingContext | null` per HandleComboChatOptions, no new
|
||||
// import needed.
|
||||
|
||||
@@ -126,6 +126,9 @@ export const PACK_ARTIFACT_ROOT_ALLOWED_EXACT_PATHS: string[] = [
|
||||
// #7802: imported by scripts/build/postinstall.mjs to repair tls-client-node's
|
||||
// native binary (chatgpt-web/claude-web/grok-web/lmarena/perplexity-web transport).
|
||||
"scripts/build/fixTlsClientNodeBinary.mjs",
|
||||
// #8859: imported by scripts/build/postinstall.mjs to repair playwright-core's
|
||||
// browser resolution on Termux/Android (no glibc, no bundled browsers).
|
||||
"scripts/build/fixPlaywrightAndroid.mjs",
|
||||
// #5227: imported at runtime by bin/cli/commands/serve.mjs (heap auto-calibration).
|
||||
"scripts/build/runtime-env.mjs",
|
||||
"scripts/build/sync-env.mjs",
|
||||
|
||||
@@ -116,6 +116,19 @@ curl -X POST https://localhost:20128/api/services/9router/auto-start \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### POST /api/services/9router/auto-restart-adopted
|
||||
|
||||
Toggle 9Router auto-restart-when-adopted
|
||||
|
||||
When enabled, an externally-adopted (not OmniRoute-spawned) 9Router process is restarted under OmniRoute's own supervisor on the next health-check cycle instead of being left as adopted-only. **LOCAL_ONLY** — loopback only.
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/services/9router/auto-restart-adopted \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### POST /api/services/cliproxy/install
|
||||
|
||||
Install CLIProxyAPI from npm
|
||||
@@ -205,6 +218,19 @@ curl -X POST https://localhost:20128/api/services/cliproxy/auto-start \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### POST /api/services/cliproxy/auto-restart-adopted
|
||||
|
||||
Toggle CLIProxyAPI auto-restart-when-adopted
|
||||
|
||||
When enabled, an externally-adopted (not OmniRoute-spawned) CLIProxyAPI process is restarted under OmniRoute's own supervisor on the next health-check cycle instead of being left as adopted-only. **LOCAL_ONLY** — loopback only.
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/services/cliproxy/auto-restart-adopted \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### POST /api/services/mux/install
|
||||
|
||||
Install Mux from npm
|
||||
@@ -294,6 +320,19 @@ curl -X POST https://localhost:20128/api/services/mux/auto-start \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### POST /api/services/mux/auto-restart-adopted
|
||||
|
||||
Toggle Mux auto-restart-when-adopted
|
||||
|
||||
When enabled, an externally-adopted (not OmniRoute-spawned) Mux process is restarted under OmniRoute's own supervisor on the next health-check cycle instead of being left as adopted-only. **LOCAL_ONLY** — loopback only.
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/services/mux/auto-restart-adopted \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### POST /api/services/bifrost/install
|
||||
|
||||
Install Bifrost
|
||||
@@ -383,6 +422,193 @@ curl -X POST https://localhost:20128/api/services/bifrost/auto-start \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### POST /api/services/bifrost/auto-restart-adopted
|
||||
|
||||
Toggle Bifrost auto-restart-when-adopted
|
||||
|
||||
When enabled, an externally-adopted (not OmniRoute-spawned) Bifrost process is restarted under OmniRoute's own supervisor on the next health-check cycle instead of being left as adopted-only. **LOCAL_ONLY** — loopback only.
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/services/bifrost/auto-restart-adopted \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### POST /api/services/dario/install
|
||||
|
||||
Install Dario from npm
|
||||
|
||||
Installs the `@askalf/dario` npm package (Claude-account-pool proxy) under DATA_DIR/services/dario/. Uses execFile (no shell interpolation — hard rule #13). **LOCAL_ONLY** — loopback only.
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/services/dario/install \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### POST /api/services/dario/start
|
||||
|
||||
Start Dario
|
||||
|
||||
Spawns the Dario process. Idempotent if already running. **LOCAL_ONLY** — loopback only.
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/services/dario/start \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### POST /api/services/dario/stop
|
||||
|
||||
Stop Dario
|
||||
|
||||
Gracefully stops Dario. Idempotent — returns a stopped status even if no supervisor is currently tracking the process. **LOCAL_ONLY** — loopback only.
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/services/dario/stop \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### POST /api/services/dario/restart
|
||||
|
||||
Restart Dario
|
||||
|
||||
Equivalent to stop() then start() under the operation lock. **LOCAL_ONLY** — loopback only.
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/services/dario/restart \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### POST /api/services/dario/update
|
||||
|
||||
Update Dario to a newer npm version
|
||||
|
||||
Stops the service (if running), installs the newer npm version, then restarts it if it was running before the update. **LOCAL_ONLY** — loopback only.
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/services/dario/update \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### GET /api/services/dario/status
|
||||
|
||||
Get Dario status
|
||||
|
||||
Returns combined live supervisor state and DB metadata, including the auto-start / auto-restart-adopted flags and whether an update is available. **LOCAL_ONLY** — loopback only.
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/services/dario/status \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### POST /api/services/dario/auto-start
|
||||
|
||||
Toggle Dario auto-start
|
||||
|
||||
When enabled, Dario starts automatically on the next OmniRoute boot. **LOCAL_ONLY** — loopback only.
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/services/dario/auto-start \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### POST /api/services/dario/auto-restart-adopted
|
||||
|
||||
Toggle Dario auto-restart-when-adopted
|
||||
|
||||
When enabled, an externally-adopted (not OmniRoute-spawned) Dario process is restarted under OmniRoute's own supervisor on the next health-check cycle instead of being left as adopted-only. **LOCAL_ONLY** — loopback only.
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/services/dario/auto-restart-adopted \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### POST /api/services/dario/admin/login-start
|
||||
|
||||
Start a Dario account-pool login (device-code style)
|
||||
|
||||
Forwards to the running Dario instance's `POST /admin/login/start` using the stored admin token. The operator opens the returned `authorize_url`, approves in their own Claude account, then posts the displayed code to `/admin/login-complete`. **LOCAL_ONLY** — loopback only.
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/services/dario/admin/login-start \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### POST /api/services/dario/admin/login-complete
|
||||
|
||||
Complete a Dario account-pool login
|
||||
|
||||
Forwards to the running Dario instance's `POST /admin/login/complete`. On success the account becomes routable immediately (Dario hot-reloads its pool). **LOCAL_ONLY** — loopback only.
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/services/dario/admin/login-complete \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### GET /api/services/dario/admin/accounts
|
||||
|
||||
List Dario account-pool accounts
|
||||
|
||||
Forwards to the running Dario instance's `GET /admin/accounts`. **LOCAL_ONLY** — loopback only.
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/services/dario/admin/accounts \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### DELETE /api/services/dario/admin/accounts
|
||||
|
||||
Remove a Dario account-pool account
|
||||
|
||||
Forwards to the running Dario instance's `DELETE /admin/accounts/<alias>`. The alias is taken from a `?alias=` query param or a `{ alias }` JSON body. **LOCAL_ONLY** — loopback only.
|
||||
|
||||
```bash
|
||||
curl -X DELETE https://localhost:20128/api/services/dario/admin/accounts \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### GET /api/services/dario/admin/import-from-omniroute
|
||||
|
||||
List OmniRoute claude connections eligible for Dario import
|
||||
|
||||
Returns eligible OmniRoute `claude` OAuth provider connections (metadata only — id/name/email/org tier, never tokens) so the UI can offer a picker when more than one exists. **LOCAL_ONLY** — loopback only.
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/services/dario/admin/import-from-omniroute \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### POST /api/services/dario/admin/import-from-omniroute
|
||||
|
||||
Import an OmniRoute claude connection's OAuth tokens into Dario
|
||||
|
||||
Writes the source connection's access/refresh token pair directly into Dario's own account-file store (`~/.dario/accounts/<alias>.json`), reusing the shared Claude Code OAuth client_id, then restarts the Dario supervisor so it picks up the new account. **LOCAL_ONLY** — loopback only.
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/services/dario/admin/import-from-omniroute \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### GET /api/services/{name}/logs
|
||||
|
||||
Stream service logs via SSE
|
||||
|
||||
@@ -11537,7 +11537,10 @@
|
||||
"noSavedProxiesError": "No saved proxies found. Add proxies in Settings → Proxy first.",
|
||||
"updateProviderFailed": "Failed to update provider",
|
||||
"providerEnabled": "{provider} enabled",
|
||||
"providerDisabled": "{provider} disabled"
|
||||
"providerDisabled": "{provider} disabled",
|
||||
"providerAdded": "{provider} added",
|
||||
"add": "Add",
|
||||
"manualApiKey": "Use a manual API key"
|
||||
},
|
||||
"gamification": {
|
||||
"leaderboardScopes": {
|
||||
|
||||
@@ -5963,7 +5963,8 @@
|
||||
"kimi-coding": "Conecte o Kimi Coding com o fluxo OAuth existente.",
|
||||
"kiro": "Nível gratuito: 50 créditos/mês (~25K–100K tokens). ⚠️ Os Termos de Serviço (ToS) do Kiro proíbem o uso de proxy/harness de terceiros.",
|
||||
"codex": "Conecte o OpenAI Codex com o fluxo OAuth existente.",
|
||||
"qwen": "Conecte o Qwen Code com o fluxo OAuth existente."
|
||||
"qwen": "Conecte o Qwen Code com o fluxo OAuth existente.",
|
||||
"unorouter": "Crie uma chave de API em https://unorouter.ai e cole aqui como Bearer token."
|
||||
},
|
||||
"passthroughModelsDescription": "{provider} aceita IDs de modelo nativos do provedor. Importe de /models ou adicione IDs personalizados para roteamento.",
|
||||
"bedrockModelsDescription": "Os modelos Amazon Bedrock têm escopo definido por região da AWS. Importe de /models ou adicione IDs de modelo Bedrock habilitados na região selecionada.",
|
||||
@@ -10858,7 +10859,9 @@
|
||||
"goNow": "Ir agora",
|
||||
"message": "O MITM Proxy agora vive no AgentBridge.",
|
||||
"title": "Esta página foi movida"
|
||||
}
|
||||
},
|
||||
"noMappingsDesc": "Nenhum mapeamento de modelo configurado ainda. Adicione mapeamentos para rotear as requisições do agente pelo OmniRoute.",
|
||||
"addMapping": "Adicionar mapeamento"
|
||||
},
|
||||
"providerStats": {
|
||||
"unknownError": "Erro desconhecido",
|
||||
@@ -11534,7 +11537,10 @@
|
||||
"noSavedProxiesError": "Nenhum proxy salvo encontrado. Adicione proxies em Configurações → Proxy primeiro.",
|
||||
"updateProviderFailed": "Falha ao atualizar provedor",
|
||||
"providerEnabled": "{provider} ativado",
|
||||
"providerDisabled": "{provider} desativado"
|
||||
"providerDisabled": "{provider} desativado",
|
||||
"providerAdded": "{provider} adicionado",
|
||||
"add": "Adicionar",
|
||||
"manualApiKey": "Usar uma chave de API manual"
|
||||
},
|
||||
"gamification": {
|
||||
"leaderboardScopes": {
|
||||
|
||||
@@ -5955,7 +5955,8 @@
|
||||
"kimi-coding": "Kết nối Kimi Coding bằng luồng OAuth hiện có.",
|
||||
"kiro": "Gói miễn phí: 50 tín dụng/tháng (khoảng 25–100 nghìn token). ⚠️ Điều khoản Kiro cấm sử dụng proxy/harness của bên thứ ba.",
|
||||
"codex": "Kết nối OpenAI Codex bằng luồng OAuth hiện có.",
|
||||
"qwen": "Kết nối Qwen Code bằng luồng OAuth hiện có."
|
||||
"qwen": "Kết nối Qwen Code bằng luồng OAuth hiện có.",
|
||||
"unorouter": "Tạo khóa API tại https://unorouter.ai, sau đó dán vào đây dưới dạng Bearer token."
|
||||
},
|
||||
"passthroughModelsDescription": "{provider} chấp nhận ID mô hình gốc của nhà cung cấp. Nhập từ /models hoặc thêm ID tùy chỉnh để định tuyến.",
|
||||
"bedrockModelsDescription": "Các mô hình Amazon Bedrock được giới hạn theo vùng AWS. Nhập từ /models hoặc thêm ID mô hình Bedrock được bật trong vùng đã chọn.",
|
||||
@@ -10850,7 +10851,9 @@
|
||||
"goNow": "Đi ngay",
|
||||
"message": "MITM Proxy hiện nằm trong AgentBridge.",
|
||||
"title": "Trang này đã được di chuyển"
|
||||
}
|
||||
},
|
||||
"noMappingsDesc": "Chưa cấu hình ánh xạ mô hình nào. Hãy thêm ánh xạ để định tuyến yêu cầu của agent qua OmniRoute.",
|
||||
"addMapping": "Thêm ánh xạ"
|
||||
},
|
||||
"providerStats": {
|
||||
"unknownError": "Lỗi không xác định",
|
||||
@@ -11526,7 +11529,10 @@
|
||||
"noSavedProxiesError": "Không tìm thấy proxy đã lưu. Hãy thêm proxy trong Cài đặt → Proxy trước.",
|
||||
"updateProviderFailed": "Không thể cập nhật nhà cung cấp",
|
||||
"providerEnabled": "Đã bật {provider}",
|
||||
"providerDisabled": "Đã tắt {provider}"
|
||||
"providerDisabled": "Đã tắt {provider}",
|
||||
"providerAdded": "Đã thêm {provider}",
|
||||
"add": "Thêm",
|
||||
"manualApiKey": "Dùng khóa API thủ công"
|
||||
},
|
||||
"gamification": {
|
||||
"leaderboardScopes": {
|
||||
|
||||
@@ -465,6 +465,13 @@ function isSchemaAlreadyApplied(
|
||||
// exists the rebuild ran — skip re-executing the rename/copy/drop, which
|
||||
// would fail on the missing proxy_assignments_pre117 table.
|
||||
return hasColumn(db, "proxy_assignments", "position");
|
||||
// Retroactive guard for the 135/136 renumber (#8523 landed onto slots already taken
|
||||
// by #8908/#9515): a DB that ran these under the old numbers already has the column,
|
||||
// and a bare ALTER TABLE ADD COLUMN would throw on the re-run under the new number.
|
||||
case "137":
|
||||
return hasColumn(db, "version_manager", "auto_restart_adopted");
|
||||
case "138":
|
||||
return hasColumn(db, "upstream_proxy_config", "fallback_backend");
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
-- Migration 135: Auto-restart-adopted toggle for embedded services
|
||||
-- Migration 137: Auto-restart-adopted toggle for embedded services
|
||||
--
|
||||
-- When a supervisor's probeBeforeSpawn finds a healthy instance already
|
||||
-- listening on its port, it adopts that process instead of spawning a new
|
||||
@@ -1,4 +1,4 @@
|
||||
-- Migration 136: Dario failover backend selection for upstream_proxy_config
|
||||
-- Migration 138: Dario failover backend selection for upstream_proxy_config
|
||||
--
|
||||
-- Adds `fallback_backend` so a provider whose mode is 'fallback' can choose
|
||||
-- WHICH embedded proxy handles the retry leg — CLIProxyAPI (the historical,
|
||||
@@ -168,19 +168,6 @@ export async function update(): Promise<InstallResult> {
|
||||
return install("latest");
|
||||
}
|
||||
|
||||
export async function uninstall(): Promise<void> {
|
||||
const nmDir = path.join(getDarioInstallDir(), "node_modules");
|
||||
if (fs.existsSync(nmDir)) {
|
||||
fs.rmSync(nmDir, { recursive: true, force: true });
|
||||
}
|
||||
await upsertVersionManagerTool({
|
||||
tool: "dario",
|
||||
status: "not_installed",
|
||||
installedVersion: null,
|
||||
binaryPath: null,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Build spawn args for ServiceSupervisor.start().
|
||||
*
|
||||
|
||||
@@ -30,13 +30,13 @@ const {
|
||||
isControlPlaneProxyDirectFallbackEnabled,
|
||||
} = await import("../../src/shared/utils/featureFlags.ts");
|
||||
|
||||
const EXPECTED_FEATURE_FLAG_COUNT = 44;
|
||||
const EXPECTED_FEATURE_FLAG_COUNT = 45;
|
||||
|
||||
// ──────────────────────────────────────────────────────
|
||||
// Test group 1 — Flag definitions registry
|
||||
// ──────────────────────────────────────────────────────
|
||||
describe("featureFlagDefinitions", () => {
|
||||
it("has exactly 44 flag definitions", () => {
|
||||
it("has exactly 45 flag definitions", () => {
|
||||
assert.strictEqual(FEATURE_FLAG_DEFINITIONS.length, EXPECTED_FEATURE_FLAG_COUNT);
|
||||
});
|
||||
|
||||
@@ -332,7 +332,7 @@ describe("resolveFeatureFlag", () => {
|
||||
});
|
||||
|
||||
describe("resolveAllFeatureFlags", () => {
|
||||
it("returns all 44 flags", () => {
|
||||
it("returns all 45 flags", () => {
|
||||
const all = resolveAllFeatureFlags();
|
||||
assert.strictEqual(all.length, EXPECTED_FEATURE_FLAG_COUNT);
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Characterization of the providers.ts catalog split (god-file decomposition): the host became a
|
||||
// barrel that re-exports 10 data catalogs now living under constants/providers/*, and APIKEY is
|
||||
// merged from 6 semantic family files (apikey/<family>.ts). Locks: the public surface (every catalog
|
||||
// + helpers still exported), the spread-merge integrity (196 APIKEY entries, no loss/dup), and that
|
||||
// + helpers still exported), the spread-merge integrity (197 APIKEY entries, no loss/dup), and that
|
||||
// load-time Zod validation still runs. Pure-data move → behavior must be identical.
|
||||
// Count was 171 before obsolete provider removals (PR #6675: glhf/kluster/cablyai/inclusionai etc.,
|
||||
// 171->167) plus #6126 (ClinePass dual-auth): the API-key-only APIKEY_PROVIDERS_GATEWAYS entry was
|
||||
@@ -17,7 +17,7 @@
|
||||
// sarvam+plamo in regional) to 193, then #8170 (inception/typhoon — inception in frontier-labs,
|
||||
// typhoon in regional) to 195, then Firecrawl dual search+fetch under SEARCH_PROVIDERS.firecrawl
|
||||
// (removed specialty-media duplicate) to 194, #8861 (Xiaomi MiMo Token Plan, regional) to 195, and
|
||||
// the Cheaper Inference gateway (OSS-sponsor reseller, gateways family) to 196.
|
||||
// the Cheaper Inference gateway (OSS-sponsor reseller, gateways family) to 197 (UnoRouter, #9009).
|
||||
import { test } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
@@ -46,12 +46,12 @@ test("barrel still exports every catalog + key helpers", () => {
|
||||
}
|
||||
});
|
||||
|
||||
test("APIKEY_PROVIDERS merges the 6 family files into 196 entries (no loss / no dup)", async () => {
|
||||
test("APIKEY_PROVIDERS merges the 6 family files into 197 entries (no loss / no dup)", async () => {
|
||||
const keys = Object.keys((P as Record<string, object>).APIKEY_PROVIDERS);
|
||||
assert.equal(keys.length, 196);
|
||||
assert.equal(new Set(keys).size, 196, "duplicate keys after spread-merge");
|
||||
assert.equal(keys.length, 197);
|
||||
assert.equal(new Set(keys).size, 197, "duplicate keys after spread-merge");
|
||||
// the merged object's entry-count equals the sum of the 6 semantic family files; families are a
|
||||
// strict partition (every provider in exactly one), so the sum must be exactly 196.
|
||||
// strict partition (every provider in exactly one), so the sum must be exactly 197.
|
||||
const families: [string, string][] = [
|
||||
["gateways", "APIKEY_PROVIDERS_GATEWAYS"],
|
||||
["frontier-labs", "APIKEY_PROVIDERS_FRONTIER"],
|
||||
@@ -71,7 +71,7 @@ test("APIKEY_PROVIDERS merges the 6 family files into 196 entries (no loss / no
|
||||
seen.add(k);
|
||||
}
|
||||
}
|
||||
assert.equal(famTotal, 196, "families must partition all 196 providers");
|
||||
assert.equal(famTotal, 197, "families must partition all 197 providers");
|
||||
});
|
||||
|
||||
test("AI_PROVIDERS Proxy aggregates all sections; lookups resolve", () => {
|
||||
|
||||
@@ -40,6 +40,9 @@ test("runtime env helpers normalize runtime ports and conflicting color flags",
|
||||
PORT: "22128",
|
||||
DASHBOARD_PORT: "22128",
|
||||
API_PORT: "21128",
|
||||
// #8999 (docker entrypoint moved to /app) made withRuntimePortEnv also pin
|
||||
// the bind address, defaulting to 0.0.0.0 when OMNIROUTE_HOSTNAME is unset.
|
||||
HOSTNAME: "0.0.0.0",
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -12,12 +12,12 @@ test("costs section exists in SIDEBAR_SECTIONS", () => {
|
||||
assert.ok(section, "costs section must exist");
|
||||
});
|
||||
|
||||
test("costs section has exactly 5 items in the correct order", () => {
|
||||
test("costs section has exactly 6 items in the correct order", () => {
|
||||
const section = findSection("costs");
|
||||
assert.ok(section, "costs section must exist");
|
||||
|
||||
const items = sidebarVisibility.getSectionItems(section);
|
||||
assert.equal(items.length, 5, "costs section must have 5 items");
|
||||
assert.equal(items.length, 6, "costs section must have 6 items");
|
||||
|
||||
const itemIds = items.map((i) => i.id);
|
||||
assert.deepEqual(itemIds, [
|
||||
@@ -26,6 +26,7 @@ test("costs section has exactly 5 items in the correct order", () => {
|
||||
"costs-budget",
|
||||
"costs-free-tiers",
|
||||
"free-provider-rankings",
|
||||
"radar",
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -42,6 +43,7 @@ test("costs section items have correct hrefs", () => {
|
||||
{ id: "costs-budget", href: "/dashboard/costs/budget" },
|
||||
{ id: "costs-free-tiers", href: "/dashboard/free-tiers" },
|
||||
{ id: "free-provider-rankings", href: "/dashboard/free-provider-rankings" },
|
||||
{ id: "radar", href: "/dashboard/radar" },
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
@@ -13,7 +13,10 @@ test("Dockerfile's --ignore-scripts npm ci is compensated for tls-client-node's
|
||||
|
||||
assert.match(
|
||||
dockerfile,
|
||||
/npm ci --no-audit --no-fund --legacy-peer-deps --ignore-scripts/,
|
||||
// Flag-order tolerant on purpose: the assertion is about the --ignore-scripts
|
||||
// PRECONDITION, not the exact flag list. #9185 inserted --include=optional
|
||||
// (LLMLingua optional deps) and broke the literal pin without touching intent.
|
||||
/npm ci(?: --[\w-]+(?:=[\w-]+)?)* --ignore-scripts/,
|
||||
"expected the builder stage to install with --ignore-scripts (precondition of #7802)"
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user