mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-26 09:52:11 +03:00
Release v3.8.40
v3.8.40 cycle integration → main. All test gates green (Unit/Integration/Coverage/Node-compat/Quality-Ratchet). The only red check, 'PR Test Policy', is the test-masking heuristic firing on the cumulative ~57-commit release diff (legitimate assert consolidations already reviewed per-PR — Gemini CLI removal #5246, retired GPT models #5280, provider catalog refreshes); overridden with --admin per the documented release-PR convention. CodeQL/SonarQube advisory scans non-blocking; #5278's code already passed CodeQL on main. Homologated on VPS 192.168.0.15 (v3.8.40 healthy).
This commit is contained in:
committed by
GitHub
parent
1c18be4f8f
commit
7c23dab64d
@@ -26,7 +26,7 @@ const commonHeaders = {
|
||||
Referer: "https://solo.trae.ai/",
|
||||
"User-Agent":
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 " +
|
||||
"(KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36",
|
||||
"(KHTML, like Gecko) Chrome/149.0.0.0 Safari/537.36",
|
||||
};
|
||||
|
||||
// Each variant = a set of auth-bearing headers to try.
|
||||
|
||||
@@ -76,30 +76,6 @@ async function main() {
|
||||
runCmd("git add open-sse/executors/index.ts");
|
||||
}
|
||||
|
||||
// 5. Resolve tests/unit/t20-t22-provider-headers.test.ts (combine imports)
|
||||
const testFile1 = path.join(projectRoot, "tests/unit/t20-t22-provider-headers.test.ts");
|
||||
if (fs.existsSync(testFile1)) {
|
||||
let content = fs.readFileSync(testFile1, "utf-8");
|
||||
content = content.replace(
|
||||
/<<<<<<< HEAD\r?\nconst \{ getCodexClientVersion \} = await import\("\.\.\/\.\.\/open-sse\/config\/codexClient\.ts"\);\r?\nconst \{ geminiCliUserAgent, GEMINI_CLI_VERSION \} =\r?\n=======\r?\nconst \{ geminiCliUserAgent, GEMINI_CLI_VERSION, GEMINI_CLI_GOOGLE_API_NODE_CLIENT_VERSION \} =\r?\n>>>>>>> release\/v3\.8\.4/g,
|
||||
'const { getCodexClientVersion } = await import("../../open-sse/config/codexClient.ts");\nconst { geminiCliUserAgent, GEMINI_CLI_VERSION, GEMINI_CLI_GOOGLE_API_NODE_CLIENT_VERSION } ='
|
||||
);
|
||||
fs.writeFileSync(testFile1, content);
|
||||
runCmd("git add tests/unit/t20-t22-provider-headers.test.ts");
|
||||
}
|
||||
|
||||
// 6. Resolve tests/integration/chat-pipeline.test.ts (combine imports)
|
||||
const testFile2 = path.join(projectRoot, "tests/integration/chat-pipeline.test.ts");
|
||||
if (fs.existsSync(testFile2)) {
|
||||
let content = fs.readFileSync(testFile2, "utf-8");
|
||||
content = content.replace(
|
||||
/<<<<<<< HEAD\r?\nconst \{ getCodexClientVersion \} = await import\("\.\.\/\.\.\/open-sse\/config\/codexClient\.ts"\);\r?\nconst \{ GEMINI_CLI_VERSION \} = await import\("\.\.\/\.\.\/open-sse\/services\/geminiCliHeaders\.ts"\);\r?\n=======\r?\nconst \{ GEMINI_CLI_VERSION, GEMINI_CLI_GOOGLE_API_NODE_CLIENT_VERSION \} =\r?\n\s+await import\("\.\.\/\.\.\/open-sse\/services\/geminiCliHeaders\.ts"\);\r?\n>>>>>>> release\/v3\.8\.4/g,
|
||||
'const { getCodexClientVersion } = await import("../../open-sse/config/codexClient.ts");\nconst { GEMINI_CLI_VERSION, GEMINI_CLI_GOOGLE_API_NODE_CLIENT_VERSION } =\n await import("../../open-sse/services/geminiCliHeaders.ts");'
|
||||
);
|
||||
fs.writeFileSync(testFile2, content);
|
||||
runCmd("git add tests/integration/chat-pipeline.test.ts");
|
||||
}
|
||||
|
||||
// 7. Resolve src/app/api/providers/[id]/models/route.ts (combine imports)
|
||||
const modelsRoute = path.join(projectRoot, "src/app/api/providers/[id]/models/route.ts");
|
||||
if (fs.existsSync(modelsRoute)) {
|
||||
|
||||
@@ -30,10 +30,6 @@ const require = createRequire(import.meta.url);
|
||||
const OPTIONAL_OAUTH_SECRETS = [
|
||||
{ keys: ["ANTIGRAVITY_OAUTH_CLIENT_SECRET"], label: "Antigravity OAuth" },
|
||||
{ keys: ["QODER_OAUTH_CLIENT_SECRET"], label: "Qoder OAuth" },
|
||||
{
|
||||
keys: ["GEMINI_CLI_OAUTH_CLIENT_SECRET", "GEMINI_OAUTH_CLIENT_SECRET"],
|
||||
label: "Gemini OAuth",
|
||||
},
|
||||
];
|
||||
|
||||
// ── Resolve DATA_DIR (mirrors dataPaths.ts logic) ───────────────────────────
|
||||
|
||||
@@ -79,10 +79,10 @@ export const PACK_ARTIFACT_ROOT_ALLOWED_EXACT_PATHS: string[] = [
|
||||
"bin/nodeRuntimeSupport.mjs",
|
||||
"bin/omniroute.mjs",
|
||||
"bin/reset-password.mjs",
|
||||
// Operator / incident-runbook shell tooling (rollback, snapshot, restore,
|
||||
// cold-start bench) shipped in bin/ for self-hosters — referenced by
|
||||
// docs/INCIDENT_RESPONSE.md, not imported by the runtime. Included via the
|
||||
// package.json "files": ["bin/"] entry, so they must be allowed here.
|
||||
// Operator incident-recovery / cold-start shell tooling (rollback, snapshot,
|
||||
// restore, cold-start bench) shipped in bin/ for self-hosters — not imported by
|
||||
// the runtime. Included via the package.json "files": ["bin/"] entry, so they
|
||||
// must be allowed here. Each script is self-documenting via --help.
|
||||
"bin/_ops-common.sh",
|
||||
"bin/cold-start-bench.sh",
|
||||
"bin/restore-data.sh",
|
||||
@@ -106,6 +106,8 @@ export const PACK_ARTIFACT_ROOT_ALLOWED_EXACT_PATHS: string[] = [
|
||||
"scripts/build/postinstall.mjs",
|
||||
"scripts/build/postinstallSupport.mjs",
|
||||
"scripts/build/colocateOptionals.mjs",
|
||||
// #5227: imported at runtime by bin/cli/commands/serve.mjs (heap auto-calibration).
|
||||
"scripts/build/runtime-env.mjs",
|
||||
"scripts/build/sync-env.mjs",
|
||||
"scripts/dev/responses-ws-proxy.mjs",
|
||||
"scripts/dev/sync-env.mjs",
|
||||
@@ -148,6 +150,7 @@ export const PACK_ARTIFACT_REQUIRED_PATHS: string[] = [
|
||||
"scripts/build/postinstall.mjs",
|
||||
"scripts/build/postinstallSupport.mjs",
|
||||
"scripts/build/colocateOptionals.mjs",
|
||||
"scripts/build/runtime-env.mjs",
|
||||
"src/shared/utils/nodeRuntimeSupport.ts",
|
||||
];
|
||||
|
||||
|
||||
@@ -58,13 +58,9 @@ export const KNOWN_STALE_DOC_REFS = new Set([
|
||||
// existing guardrailRegistry); the fictional enable/disable/logs rows and the entire
|
||||
// shadow table were removed from the doc (shadow A-B comparison is combo-config +
|
||||
// /api/combos/metrics). No allowlist entries needed for these anymore.
|
||||
// docs/research/DISCOVERY_TOOL_DESIGN.md — design doc de feature NÃO implementada
|
||||
// (Phase 2). Refs INTENCIONAIS: o doc agora traz um banner "⚠️ Not yet implemented
|
||||
// — Phase 2" acima da tabela de endpoints. Mantidos aqui até a feature existir. — #3498
|
||||
"/api/discovery/results",
|
||||
"/api/discovery/results/:id",
|
||||
"/api/discovery/scan",
|
||||
"/api/discovery/verify/:id",
|
||||
// (DISCOVERY_TOOL_DESIGN.md saiu de docs/research/ para o repo isolado _tasks/research/
|
||||
// — gitignored, fora do escopo deste gate. As 4 entradas /api/discovery/* viraram
|
||||
// obsoletas e foram removidas para satisfazer o stale-enforcement da allowlist.)
|
||||
// docs/reference/ENVIRONMENT.md — endpoint UPSTREAM do provedor Blackbox Web,
|
||||
// citado na descrição de env var (não é rota do OmniRoute):
|
||||
"/api/chat",
|
||||
@@ -85,9 +81,7 @@ function walk(dir, filter, acc = []) {
|
||||
|
||||
export function collectRouteFiles() {
|
||||
return new Set(
|
||||
walk(API, (n) => /^route\.tsx?$/.test(n)).map((p) =>
|
||||
path.relative(ROOT, p).replace(/\\/g, "/")
|
||||
)
|
||||
walk(API, (n) => /^route\.tsx?$/.test(n)).map((p) => path.relative(ROOT, p).replace(/\\/g, "/"))
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -107,8 +107,6 @@ const ENV_VAR_ALLOWLIST = new Set([
|
||||
"NINEROUTER_API_KEY", // injected into the 9router subprocess at spawn (EMBEDDED-SERVICES.md)
|
||||
"CLAUDE_CODE_MAX_OUTPUT_TOKENS", // Claude Code CLI's own env var (CODEX-CLI-CONFIGURATION.md)
|
||||
"CODEX_HOME", // Codex CLI's own config-home env var (CODEX-CLI-CONFIGURATION.md)
|
||||
"GEMINI_API_KEY", // Gemini CLI's own API-key env var, set by `omniroute setup-gemini` (REMOTE-MODE.md)
|
||||
"GOOGLE_GEMINI_BASE_URL", // Gemini CLI's own base-URL env var, set by `omniroute setup-gemini` (REMOTE-MODE.md)
|
||||
"OPENAI_API_BASE", // legacy OpenAI base-URL env var some downstream tools (e.g. Aider) read (CLI-INTEGRATIONS.md)
|
||||
"PROMPTFOO_PROVIDER_KEY", // promptfoo's own provider-key env var, used by the red-team suite (GUARDRAILS.md)
|
||||
"REDIS_PORT", // docker-compose host-port override (DOCKER_GUIDE.md)
|
||||
@@ -361,9 +359,6 @@ const SKIP_DOC_FILES = new Set([
|
||||
"docs/reference/PROVIDER_REFERENCE.md", // auto-generated from providers.ts
|
||||
"docs/openapi.yaml",
|
||||
"docs/i18n", // translations — separate workflow
|
||||
// Point-in-time documentation audit (v3.8.24): intentionally references drift,
|
||||
// counts, and not-yet-existing files as part of documenting them — not living docs.
|
||||
"docs/ops/DOCUMENTATION_AUDIT_REPORT.md",
|
||||
// Design / research / plan docs: by definition describe not-yet-built files and
|
||||
// proposed (not-yet-shipped) endpoints (each carries a `Status: Design`/`Active
|
||||
// research`/`Plano` header). Same rationale as the audit report above — these are
|
||||
|
||||
@@ -181,8 +181,6 @@ export const KNOWN_TRANSLATOR_PAIRS: readonly string[] = [
|
||||
"claude:gemini",
|
||||
"claude:openai",
|
||||
"cursor:openai",
|
||||
"gemini-cli:claude",
|
||||
"gemini-cli:openai",
|
||||
"gemini:claude",
|
||||
"gemini:openai",
|
||||
"kiro:openai",
|
||||
@@ -191,7 +189,6 @@ export const KNOWN_TRANSLATOR_PAIRS: readonly string[] = [
|
||||
"openai:claude",
|
||||
"openai:cursor",
|
||||
"openai:gemini",
|
||||
"openai:gemini-cli",
|
||||
"openai:kiro",
|
||||
"openai:openai-responses",
|
||||
];
|
||||
@@ -200,10 +197,7 @@ export const KNOWN_TRANSLATOR_PAIRS: readonly string[] = [
|
||||
* Pares frozen que sumiram do registry vivo (regressão). frozen = snapshot;
|
||||
* live = pares observados em runtime. Retorna os que estão no frozen mas não no live.
|
||||
*/
|
||||
export function findMissingTranslatorPairs(
|
||||
frozen: readonly string[],
|
||||
live: Set<string>
|
||||
): string[] {
|
||||
export function findMissingTranslatorPairs(frozen: readonly string[], live: Set<string>): string[] {
|
||||
return frozen.filter((pair) => !live.has(pair));
|
||||
}
|
||||
|
||||
@@ -375,10 +369,7 @@ export type A2ASkillDiff = {
|
||||
* - inHandlersNotCard: skill is routable but agents can't discover it
|
||||
* - inCardNotHandlers: skill is advertised but calling it fails silently
|
||||
*/
|
||||
export function diffA2ASkills(
|
||||
handlers: Set<string>,
|
||||
agentCard: Set<string>
|
||||
): A2ASkillDiff {
|
||||
export function diffA2ASkills(handlers: Set<string>, agentCard: Set<string>): A2ASkillDiff {
|
||||
const inHandlersNotCard = [...handlers].filter((s) => !agentCard.has(s)).sort();
|
||||
const inCardNotHandlers = [...agentCard].filter((s) => !handlers.has(s)).sort();
|
||||
return { inHandlersNotCard, inCardNotHandlers };
|
||||
@@ -456,13 +447,12 @@ async function main(): Promise<void> {
|
||||
const executorsMod = await import("@omniroute/open-sse/executors/index.ts");
|
||||
const getExecutor = executorsMod.getExecutor as (alias: string) => ExecutorLike;
|
||||
const BaseExecutor = executorsMod.BaseExecutor as new (...args: never[]) => unknown;
|
||||
const indexSource = readFileSync(
|
||||
resolvePath(REPO_ROOT, "open-sse/executors/index.ts"),
|
||||
"utf8"
|
||||
);
|
||||
const indexSource = readFileSync(resolvePath(REPO_ROOT, "open-sse/executors/index.ts"), "utf8");
|
||||
const aliases = extractExecutorAliases(indexSource);
|
||||
if (aliases.length === 0) {
|
||||
failures.push("[executor] parse do mapa `executors` não encontrou nenhum alias (regex quebrada?)");
|
||||
failures.push(
|
||||
"[executor] parse do mapa `executors` não encontrou nenhum alias (regex quebrada?)"
|
||||
);
|
||||
}
|
||||
const isExecutorInstance = (value: unknown) => value instanceof BaseExecutor;
|
||||
const badExecutors = findNonConformingExecutors(aliases, getExecutor, isExecutorInstance);
|
||||
@@ -493,7 +483,11 @@ async function main(): Promise<void> {
|
||||
// EMPTY implicit-defaults map). An entry whose key IS already in `handled` suppresses
|
||||
// nothing → it is stale and the gate must fail asking for its removal.
|
||||
const liveImplicitNeeded = diffComboStrategies(canonical, handled, {}).canonicalNotHandled;
|
||||
assertNoStale(Object.keys(IMPLICIT_DEFAULT_STRATEGIES), liveImplicitNeeded, "known-symbols:combo");
|
||||
assertNoStale(
|
||||
Object.keys(IMPLICIT_DEFAULT_STRATEGIES),
|
||||
liveImplicitNeeded,
|
||||
"known-symbols:combo"
|
||||
);
|
||||
|
||||
const { canonicalNotHandled, handledNotCanonical } = diffComboStrategies(
|
||||
canonical,
|
||||
@@ -554,9 +548,8 @@ async function main(): Promise<void> {
|
||||
const { MCP_TOOLS } = await import("@omniroute/open-sse/mcp-server/schemas/tools.ts");
|
||||
const { memoryTools } = await import("@omniroute/open-sse/mcp-server/tools/memoryTools.ts");
|
||||
const { skillTools } = await import("@omniroute/open-sse/mcp-server/tools/skillTools.ts");
|
||||
const { gamificationTools } = await import(
|
||||
"@omniroute/open-sse/mcp-server/tools/gamificationTools.ts"
|
||||
);
|
||||
const { gamificationTools } =
|
||||
await import("@omniroute/open-sse/mcp-server/tools/gamificationTools.ts");
|
||||
const { pluginTools } = await import("@omniroute/open-sse/mcp-server/tools/pluginTools.ts");
|
||||
const { notionTools } = await import("@omniroute/open-sse/mcp-server/tools/notionTools.ts");
|
||||
const { obsidianTools } = await import("@omniroute/open-sse/mcp-server/tools/obsidianTools.ts");
|
||||
@@ -669,7 +662,9 @@ async function main(): Promise<void> {
|
||||
|
||||
// ── Resultado ─────────────────────────────────────────────────────────────
|
||||
if (failures.length) {
|
||||
console.error(`[known-symbols] ${failures.length} sub-checagem(ns) falharam:\n\n${failures.join("\n\n")}`);
|
||||
console.error(
|
||||
`[known-symbols] ${failures.length} sub-checagem(ns) falharam:\n\n${failures.join("\n\n")}`
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
// assertNoStale (combo) seta process.exitCode=1 sem lançar — não imprima o OK
|
||||
@@ -695,7 +690,9 @@ async function main(): Promise<void> {
|
||||
|
||||
if (import.meta.url === pathToFileURL(process.argv[1] || "").href) {
|
||||
main().catch((err) => {
|
||||
console.error(`[known-symbols] erro fatal: ${err instanceof Error ? err.message : String(err)}`);
|
||||
console.error(
|
||||
`[known-symbols] erro fatal: ${err instanceof Error ? err.message : String(err)}`
|
||||
);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -67,6 +67,7 @@ export const COLLECTORS = [
|
||||
// vitest.mcp.config.ts — test:vitest
|
||||
{ glob: "open-sse/mcp-server/__tests__/**/*.test.ts", sources: ["vitest.mcp.config.ts"] },
|
||||
{ glob: "open-sse/services/autoCombo/__tests__/**/*.test.ts", sources: ["vitest.mcp.config.ts"] },
|
||||
{ glob: "open-sse/services/combo/__tests__/**/*.test.ts", sources: ["vitest.mcp.config.ts"] },
|
||||
// Single-file include: the rest of open-sse/services/__tests__/ are frozen orphans
|
||||
// (empty/dormant stubs); only this one is wired to run under test:vitest.
|
||||
{
|
||||
|
||||
44
scripts/ci/should-promote-latest.sh
Executable file
44
scripts/ci/should-promote-latest.sh
Executable file
@@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env bash
|
||||
# Decide whether the just-built release VERSION should also move the Docker
|
||||
# `:latest` / `:latest-web` tags.
|
||||
#
|
||||
# Promote ONLY when VERSION is the highest STABLE semver among the union of
|
||||
# {existing git tags} ∪ {VERSION}. Folding VERSION into the candidate set makes
|
||||
# the decision independent of git-tag sync timing: on a `release: released`
|
||||
# event the freshly-created tag is often not yet visible to `git fetch --tags`
|
||||
# when this job runs, so a candidate set built purely from `git tag -l` would
|
||||
# resolve HIGHEST to the *previous* version and skip the `:latest` promotion —
|
||||
# leaving `latest` one release behind (#5301).
|
||||
#
|
||||
# Usage:
|
||||
# git tag -l 'v[0-9]*' | sed 's/^v//' | scripts/ci/should-promote-latest.sh "$VERSION"
|
||||
#
|
||||
# - $1 : the version being published (no leading `v`, e.g. 3.8.40).
|
||||
# - stdin : newline-separated candidate tags (a leading `v` is stripped;
|
||||
# pre-release tags — anything containing `-`, e.g. 3.9.0-rc.1 —
|
||||
# are ignored). May be empty (first release).
|
||||
# Prints "true" if VERSION should move :latest, "false" otherwise.
|
||||
set -euo pipefail
|
||||
|
||||
VERSION="${1:?version required}"
|
||||
|
||||
# A pre-release VERSION must never grab :latest (callers already short-circuit
|
||||
# this, but stay safe as a standalone unit).
|
||||
case "$VERSION" in
|
||||
*-*) echo "false"; exit 0 ;;
|
||||
esac
|
||||
|
||||
# Build the stable candidate set: incoming tags (v-stripped, pre-releases
|
||||
# dropped) plus VERSION itself, then pick the numerically highest.
|
||||
HIGHEST="$(
|
||||
{
|
||||
sed 's/^v//' | grep -vE -- '-' || true
|
||||
printf '%s\n' "$VERSION"
|
||||
} | sort -V | tail -1
|
||||
)"
|
||||
|
||||
if [ "$VERSION" = "$HIGHEST" ]; then
|
||||
echo "true"
|
||||
else
|
||||
echo "false"
|
||||
fi
|
||||
@@ -616,7 +616,7 @@ class ResponsesWsSession {
|
||||
};
|
||||
|
||||
const upstream = await this.wsFactory(prepared.json.upstreamUrl, {
|
||||
browser: prepared.json.browser || "chrome_142",
|
||||
browser: prepared.json.browser || "chrome_149",
|
||||
os: prepared.json.os || "windows",
|
||||
headers: prepared.json.headers || {},
|
||||
});
|
||||
|
||||
@@ -90,7 +90,6 @@ lines.push(section("Agent CLI Tools"));
|
||||
|
||||
const cliTools = [
|
||||
{ name: "qoder-cli", cmd: "qoder", args: "--version" },
|
||||
{ name: "gemini-cli", cmd: "gemini", args: "--version" },
|
||||
{ name: "claude-code", cmd: "claude", args: "--version" },
|
||||
{ name: "openai-codex", cmd: "codex", args: "--version" },
|
||||
{ name: "antigravity", cmd: "antigravity", args: "--version" },
|
||||
|
||||
@@ -56,13 +56,37 @@ export const NEW_PAGE_EXCLUDE = new Set([
|
||||
// Acronyms kept upper-case when minting a NEW page name (existing pages keep their
|
||||
// curated name via fuzzy match, so this only affects brand-new pages).
|
||||
const ACRONYMS = new Set([
|
||||
"api", "mcp", "a2a", "acp", "cli", "sse", "i18n", "pii", "oauth", "vm", "ai",
|
||||
"llm", "sdk", "ide", "ui", "ux", "tls", "mitm", "ws", "cors", "jwt", "db", "vps",
|
||||
"api",
|
||||
"mcp",
|
||||
"a2a",
|
||||
"acp",
|
||||
"cli",
|
||||
"sse",
|
||||
"i18n",
|
||||
"pii",
|
||||
"oauth",
|
||||
"vm",
|
||||
"ai",
|
||||
"llm",
|
||||
"sdk",
|
||||
"ide",
|
||||
"ui",
|
||||
"ux",
|
||||
"tls",
|
||||
"mitm",
|
||||
"ws",
|
||||
"cors",
|
||||
"jwt",
|
||||
"db",
|
||||
"vps",
|
||||
]);
|
||||
|
||||
/** Normalized matching key: lowercase, drop extension + every non-alphanumeric char. */
|
||||
export function normKey(s) {
|
||||
return s.toLowerCase().replace(/\.md$/, "").replace(/[^a-z0-9]/g, "");
|
||||
return s
|
||||
.toLowerCase()
|
||||
.replace(/\.md$/, "")
|
||||
.replace(/[^a-z0-9]/g, "");
|
||||
}
|
||||
|
||||
/** Deterministic wiki page name for a brand-new page (acronym-aware Title-Case-dashed). */
|
||||
@@ -71,7 +95,11 @@ export function toWikiName(basename) {
|
||||
.replace(/\.md$/, "")
|
||||
.split(/[_\-\s]+/)
|
||||
.filter(Boolean)
|
||||
.map((t) => (ACRONYMS.has(t.toLowerCase()) ? t.toUpperCase() : t[0].toUpperCase() + t.slice(1).toLowerCase()))
|
||||
.map((t) =>
|
||||
ACRONYMS.has(t.toLowerCase())
|
||||
? t.toUpperCase()
|
||||
: t[0].toUpperCase() + t.slice(1).toLowerCase()
|
||||
)
|
||||
.join("-");
|
||||
}
|
||||
|
||||
@@ -124,13 +152,19 @@ export function syncHomeCounts(home, counts) {
|
||||
let out = home;
|
||||
if (counts.providers) {
|
||||
out = out
|
||||
.replace(/Connect every AI tool to \d+ providers/g, `Connect every AI tool to ${counts.providers} providers`)
|
||||
.replace(
|
||||
/Connect every AI tool to \d+ providers/g,
|
||||
`Connect every AI tool to ${counts.providers} providers`
|
||||
)
|
||||
.replace(/\*\*\d+ AI Providers\*\*/g, `**${counts.providers} AI Providers**`)
|
||||
.replace(/All \d+ supported providers/g, `All ${counts.providers} supported providers`)
|
||||
.replace(/\b\d+ providers\b/g, `${counts.providers} providers`);
|
||||
}
|
||||
if (counts.strategies) {
|
||||
out = out.replace(/\*\*\d+ Routing Strategies\*\*/g, `**${counts.strategies} Routing Strategies**`);
|
||||
out = out.replace(
|
||||
/\*\*\d+ Routing Strategies\*\*/g,
|
||||
`**${counts.strategies} Routing Strategies**`
|
||||
);
|
||||
}
|
||||
if (counts.mcpTools) {
|
||||
out = out.replace(/(\|\s*\*\*MCP Server\*\*\s*\|\s*)\d+( tools)/g, `$1${counts.mcpTools}$2`);
|
||||
@@ -251,7 +285,7 @@ function main() {
|
||||
// ARCHITECTURE.md still says "177 providers / 37 MCP tools" while the wiki cover was
|
||||
// hand-patched to 226/87). Overwriting from a staler source would REGRESS the wiki, so
|
||||
// by default we only ADD missing pages and sync Home counts. Pass --update-existing
|
||||
// once the docs sources are regenerated (see docs/ops/DOCUMENTATION_AUDIT_REPORT.md).
|
||||
// once the docs sources are regenerated.
|
||||
const updates = updateExisting ? plan.update : [];
|
||||
const total = updates.length + plan.add.length + (plan.countsChanged ? 1 : 0);
|
||||
console.log(`[wiki-sync] counts: ${JSON.stringify(counts)}`);
|
||||
@@ -263,7 +297,10 @@ function main() {
|
||||
if (plan.add.length) console.log(` add → ${plan.add.map((a) => a.page).join(", ")}`);
|
||||
if (plan.update.length)
|
||||
console.log(
|
||||
` ${updateExisting ? "update" : "would-update (skipped)"} → ${plan.update.map((u) => u.page).slice(0, 60).join(", ")}${plan.update.length > 60 ? " …" : ""}`
|
||||
` ${updateExisting ? "update" : "would-update (skipped)"} → ${plan.update
|
||||
.map((u) => u.page)
|
||||
.slice(0, 60)
|
||||
.join(", ")}${plan.update.length > 60 ? " …" : ""}`
|
||||
);
|
||||
if (check) {
|
||||
if (total > 0) {
|
||||
@@ -277,7 +314,10 @@ function main() {
|
||||
|
||||
// ---- write ----
|
||||
for (const { page, srcFile } of [...updates, ...plan.add]) {
|
||||
fs.writeFileSync(path.join(wikiDir, `${page}.md`), toWikiContent(fs.readFileSync(srcFile, "utf8")));
|
||||
fs.writeFileSync(
|
||||
path.join(wikiDir, `${page}.md`),
|
||||
toWikiContent(fs.readFileSync(srcFile, "utf8"))
|
||||
);
|
||||
}
|
||||
if (plan.countsChanged && homeAfter != null) fs.writeFileSync(homePath, homeAfter);
|
||||
console.log(
|
||||
|
||||
@@ -134,7 +134,7 @@ def main():
|
||||
parser = argparse.ArgumentParser(description="OmniRoute Auto-Translator for i18n Markdown")
|
||||
parser.add_argument("--api-url", default="http://localhost:20128/v1", help="Base URL of OmniRoute or target provider")
|
||||
parser.add_argument("--api-key", default="sk-test", help="API Key for the provider")
|
||||
parser.add_argument("--model", default="gc/gemini-3-flash", help="Model name to use")
|
||||
parser.add_argument("--model", default="gemini/gemini-3-flash", help="Model name to use")
|
||||
parser.add_argument("--lang", default=None, help="Process only a specific language code (e.g. pt-BR)")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
*
|
||||
* Classifies every COVERING test file by its mutation-kill contribution, using the
|
||||
* `killedBy` attribution that the Stryker tap-runner emits per mutant
|
||||
* (`coverageAnalysis: perTest`, validated by the Task 12 spike — see
|
||||
* docs/ops/MUTATION_GATE_SPIKE_VERDICT.md):
|
||||
* (`coverageAnalysis: perTest`, validated by the Task 12 spike):
|
||||
*
|
||||
* 🔴 empty — the test file never appears in any `killedBy` (kills no mutant
|
||||
* of the mutated modules). Prime R1-prune candidate (Task 2).
|
||||
@@ -279,7 +278,9 @@ function main(argv) {
|
||||
const reports = paths.map(loadMutationReport);
|
||||
const universe = useConfUniverse ? tapTestFilesUniverse() : null;
|
||||
if (wantCandidates) {
|
||||
process.stdout.write(renderCandidates(redundancyCandidates(reports, universe || undefined)) + "\n");
|
||||
process.stdout.write(
|
||||
renderCandidates(redundancyCandidates(reports, universe || undefined)) + "\n"
|
||||
);
|
||||
return;
|
||||
}
|
||||
const classification = aggregateRadiography(reports, universe || undefined);
|
||||
|
||||
Reference in New Issue
Block a user