mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-13 18:52:18 +03:00
fix(ci): clear base-reds on release/v3.8.50 (round 4)
Drains the HARD failures reported by Release-Green run 31693210948 on issue #9985 (ESLint errors: 2) plus the merge-integrity red every open PR is inheriting. - ESLint error 1: @omniroute/opencode-plugin/src/index.ts had a stray extra '});' (introduced by #9316) that broke parsing with 'unexpected file in NFT list' on the build path. - ESLint error 2: cli-env-inline-comment-10100.test.ts used new Function to extract parseEnvValue from the bin entrypoint (no-new-func, Hard Rule #3). Extracted the helper to bin/cli/utils/parseEnvValue.mjs and import it from both the entrypoint and the test (same behavior, no eval). - open-sse-typecheck (Fast Quality Gates): open-sse/utils/stream.ts imported sseCommentsEnabled twice (#9378) causing TS2300 Duplicate identifier; removed the duplicate import. - Merge integrity (changelog + generated skills): skills/omni-settings/SKILL.md was edited manually by #10169 without updating the generator source, so check:agent-skills-sync failed on every PR (Generated: 1). Moved the curated thinking-budget content into a <!-- skill:custom-start --> block (the documented preservation mechanism), which the generator now keeps in sync. Refs #9985
This commit is contained in:
@@ -5478,7 +5478,6 @@ export function createOmniRouteConfigHook(
|
||||
rawConnections: localRawConnections,
|
||||
expiresAt: now() + resolved.modelCacheTtl,
|
||||
});
|
||||
});
|
||||
|
||||
// Startup diagnostics (file-based) — fires at startup via config hook
|
||||
if (resolved.features?.startupDebug === true) {
|
||||
|
||||
21
bin/cli/utils/parseEnvValue.mjs
Normal file
21
bin/cli/utils/parseEnvValue.mjs
Normal file
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Parse a `.env` value with dotenv-compatible comment handling.
|
||||
*
|
||||
* Without this, `KEY=value # note` stored the comment text as part of the
|
||||
* value. The shipped .env ships exactly such a line for QUOTA_STORE_DRIVER, and
|
||||
* consumers compare it with `===`, so annotating a variable inline silently
|
||||
* disabled it (#10100).
|
||||
*
|
||||
* Quoted values are returned verbatim — a `#` inside quotes is data. For
|
||||
* unquoted values a `#` *preceded by whitespace* starts a comment, so
|
||||
* `pass#word` is preserved.
|
||||
*/
|
||||
export function parseEnvValue(raw) {
|
||||
const value = String(raw).trim();
|
||||
|
||||
const quoted = value.match(/^(['"])([\s\S]*)\1\s*(?:#.*)?$/);
|
||||
if (quoted) return quoted[2];
|
||||
|
||||
const commentIdx = value.search(/\s#/);
|
||||
return (commentIdx === -1 ? value : value.slice(0, commentIdx)).trim();
|
||||
}
|
||||
@@ -23,6 +23,7 @@ import { getNodeRuntimeSupport, getNodeRuntimeWarning } from "./nodeRuntimeSuppo
|
||||
import { getDefaultDataDir } from "./cli/data-dir.mjs";
|
||||
import { shouldProvisionStorageKey } from "./cli/utils/storageKeyProvision.mjs";
|
||||
import { isVersionFastPath } from "./cli/utils/versionFastPath.mjs";
|
||||
import { parseEnvValue } from "./cli/utils/parseEnvValue.mjs";
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
@@ -93,28 +94,6 @@ function migrateElectronServerEnv(dataDir) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a `.env` value with dotenv-compatible comment handling.
|
||||
*
|
||||
* Without this, `KEY=value # note` stored the comment text as part of the
|
||||
* value. The shipped .env ships exactly such a line for QUOTA_STORE_DRIVER, and
|
||||
* consumers compare it with `===`, so annotating a variable inline silently
|
||||
* disabled it (#10100).
|
||||
*
|
||||
* Quoted values are returned verbatim — a `#` inside quotes is data. For
|
||||
* unquoted values a `#` *preceded by whitespace* starts a comment, so
|
||||
* `pass#word` is preserved.
|
||||
*/
|
||||
function parseEnvValue(raw) {
|
||||
const value = String(raw).trim();
|
||||
|
||||
const quoted = value.match(/^(['"])([\s\S]*)\1\s*(?:#.*)?$/);
|
||||
if (quoted) return quoted[2];
|
||||
|
||||
const commentIdx = value.search(/\s#/);
|
||||
return (commentIdx === -1 ? value : value.slice(0, commentIdx)).trim();
|
||||
}
|
||||
|
||||
function loadEnvFile() {
|
||||
const envPaths = [];
|
||||
const loadedEnvPaths = [];
|
||||
|
||||
@@ -74,7 +74,6 @@ import {
|
||||
hasUnsupportedReasoningSignal,
|
||||
} from "./reasoningFields.ts";
|
||||
import { applyThinkTag, flushThink, initThinkState } from "./thinkTagParser.ts";
|
||||
import { sseCommentsEnabled } from "./sseHeartbeat.ts";
|
||||
import {
|
||||
caseInsensitiveToolNameLookup,
|
||||
restoreOpenAIToolNames,
|
||||
|
||||
@@ -319,15 +319,7 @@ curl -X PUT https://localhost:20128/api/settings/system-prompt \
|
||||
|
||||
Get thinking budget configuration
|
||||
|
||||
Returns proxy-level thinking/reasoning **request rewrite** settings:
|
||||
|
||||
| Field | Meaning |
|
||||
|-------|---------|
|
||||
| `mode` | `passthrough` (leave client reasoning alone — **required for Codex visible thinking**), `auto` (**strips** all client thinking fields), `custom`, `adaptive` |
|
||||
| `customBudget` | Fixed budget when `mode=custom` |
|
||||
| `effortLevel` | Base effort when `mode=adaptive` |
|
||||
|
||||
**Not** compression and **not** “decrypt encrypted reasoning”. Full guide: `docs/guides/THINKING_BUDGET.md`.
|
||||
Returns the current thinking/reasoning budget settings for AI models.
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/settings/thinking-budget \
|
||||
@@ -338,17 +330,13 @@ curl https://localhost:20128/api/settings/thinking-budget \
|
||||
|
||||
Update thinking budget configuration
|
||||
|
||||
Example — keep client-controlled reasoning (Codex/Desktop):
|
||||
|
||||
```bash
|
||||
curl -X PUT https://localhost:20128/api/settings/thinking-budget \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN" \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"mode":"passthrough","customBudget":10240,"effortLevel":"medium"}'
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
Warning: `mode=auto` deletes `reasoning` / `reasoning_effort` / Claude `thinking` from the outbound body before upstream. That can empty thinking panels even when the client requested Ultra + summary.
|
||||
|
||||
### GET /api/tags
|
||||
|
||||
List Ollama-compatible model tags
|
||||
@@ -398,3 +386,30 @@ curl -X POST https://localhost:20128/api/settings/purge-usage-history \
|
||||
## Payloads
|
||||
|
||||
See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/openapi.yaml` for detailed request/response schemas.
|
||||
|
||||
<!-- skill:custom-start -->
|
||||
<!-- Thinking budget behavior (preserved curated content — #10169) -->
|
||||
|
||||
### GET /api/settings/thinking-budget — behavior
|
||||
|
||||
Returns proxy-level thinking/reasoning **request rewrite** settings:
|
||||
|
||||
| Field | Meaning |
|
||||
|-------|---------|
|
||||
| `mode` | `passthrough` (leave client reasoning alone — **required for Codex visible thinking**), `auto` (**strips** all client thinking fields), `custom`, `adaptive` |
|
||||
| `customBudget` | Fixed budget when `mode=custom` |
|
||||
| `effortLevel` | Base effort when `mode=adaptive` |
|
||||
|
||||
**Not** compression and **not** "decrypt encrypted reasoning". Full guide: `docs/guides/THINKING_BUDGET.md`.
|
||||
|
||||
Example — keep client-controlled reasoning (Codex/Desktop):
|
||||
|
||||
```bash
|
||||
curl -X PUT https://localhost:20128/api/settings/thinking-budget \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"mode":"passthrough","customBudget":10240,"effortLevel":"medium"}'
|
||||
```
|
||||
|
||||
Warning: `mode=auto` deletes `reasoning` / `reasoning_effort` / Claude `thinking` from the outbound body before upstream. That can empty thinking panels even when the client requested Ultra + summary.
|
||||
<!-- skill:custom-end -->
|
||||
|
||||
@@ -2,6 +2,7 @@ import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { parseEnvValue } from "../../bin/cli/utils/parseEnvValue.mjs";
|
||||
|
||||
// #10100 — the .env loader kept inline comments inside values, so the shipped
|
||||
// `QUOTA_STORE_DRIVER=sqlite # sqlite | redis` line produced the literal value
|
||||
@@ -9,36 +10,6 @@ import path from "node:path";
|
||||
// user annotating `QUOTA_STORE_DRIVER=redis # ...` silently got SQLite with no
|
||||
// warning (the existing warning lives inside the `redis` branch).
|
||||
|
||||
const LOADER = path.resolve("bin/omniroute.mjs");
|
||||
|
||||
/**
|
||||
* The loader is a CLI entrypoint with side effects on import, so exercise the
|
||||
* pure helper by extracting it from source rather than importing the module.
|
||||
*/
|
||||
function loadParseEnvValue(): (raw: string) => string {
|
||||
const source = fs.readFileSync(LOADER, "utf8");
|
||||
const start = source.indexOf("function parseEnvValue(");
|
||||
assert.ok(start > -1, "parseEnvValue should exist in bin/omniroute.mjs");
|
||||
// Walk to the end of the function body.
|
||||
let depth = 0;
|
||||
let end = start;
|
||||
for (let i = source.indexOf("{", start); i < source.length; i++) {
|
||||
if (source[i] === "{") depth++;
|
||||
else if (source[i] === "}") {
|
||||
depth--;
|
||||
if (depth === 0) {
|
||||
end = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return new Function(`${source.slice(start, end)}; return parseEnvValue;`)() as (
|
||||
raw: string
|
||||
) => string;
|
||||
}
|
||||
|
||||
const parseEnvValue = loadParseEnvValue();
|
||||
|
||||
test("an unquoted inline comment is stripped", () => {
|
||||
assert.equal(parseEnvValue("sqlite # sqlite | redis"), "sqlite");
|
||||
assert.equal(parseEnvValue("redis # sqlite | redis"), "redis");
|
||||
|
||||
Reference in New Issue
Block a user