mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-13 18:32:12 +03:00
fix(ci): repair release-root regressions — pack dedup, web-session syntax, uc-image ids (#12423)
Three regressions inherited by every PR rebased onto release/v3.8.51, caught and documented with the exact failing output. The one that mattered most: src/shared/providers/webSessionCredentials.ts did not parse. The UC merge (#11513) inserted the uc: entry inside maxai.storageKeys and lost the array's closing ], plus the entry's }, leaving `ERROR: Expected "]" but found ":"` at line 351. That module is imported by the provider API routes, bulk-web-session, autoCombo's virtualFactory, keepaliveThreshold and dashboard components, so the break was live on the tip and flooded unrelated catalog tests with transform failures. That was my conflict resolution, not the contributor's code — thank you for catching it and for tracing it to the root commit rather than patching around the symptom. Also fixed: the duplicate bin/cli/utils/volatileEnvPath.mjs entry in PACK_ARTIFACT_REQUIRED_PATHS (findMissingArtifactPaths reported it twice), and UC image models made prefix-addressable without letting them claim historical bare model ids belonging to other providers. Reconciled on merge: #12394 landed the busy_timeout/probe work first, so src/lib/db/core.ts takes the tip's side. probeUtils.ts is the union of both rather than either side — this PR's message regex is wider (SQLite also reports "database table is locked", "database schema is locked" and "database is busy"), while #12394 added the driver code/errcode path that keeps a transient lock from being classified as corruption and renaming the database away. Taking either alone would have dropped the other half; this PR's own ENOENT test is what surfaced it. Verified: 76/76 across uc-image, probe-9541-repro, web-session-contract, pack-artifact-policy, bulk-web-session-import and exclusive-connection-leases, and every changed .ts file parses. Thanks @backryun.
This commit is contained in:
@@ -276,45 +276,6 @@ export const IMAGE_PROVIDERS: Record<string, ImageProviderConfig> = {
|
||||
supportedSizes: ["1024x1024", "1024x1536", "1536x1024", "1024x1792", "1792x1024"],
|
||||
},
|
||||
|
||||
// UC (uncensored.com) image generation. Two surfaces served by one handler
|
||||
// (handleUcImageGeneration picks by credential): PERSONA web (un-metered,
|
||||
// Clerk JWT -> internal.chatuncensored.ai/v2/image-gen + result-URL polling)
|
||||
// and uc-direct REST (metered, X-api-key -> api.uncensored.com, OpenAI-shaped).
|
||||
uc: {
|
||||
id: "uc",
|
||||
baseUrl: "https://internal.chatuncensored.ai/v2/image-gen",
|
||||
authType: "apikey",
|
||||
authHeader: "bearer",
|
||||
format: "uc-image",
|
||||
models: [
|
||||
{ id: "model-dev", name: "Flux Dev (UC)" },
|
||||
{ id: "model-pro", name: "Flux Pro (UC)" },
|
||||
{ id: "model-1.1", name: "Flux Pro 1.1 (UC)" },
|
||||
{ id: "model-1.2", name: "Wan 2.2 (UC)" },
|
||||
{ id: "seedream-v4.5", name: "Seedream v4.5 (UC)" },
|
||||
{ id: "seedream-v5", name: "Seedream v5 (UC)" },
|
||||
{ id: "flux-2", name: "FLUX.2 (UC)" },
|
||||
{ id: "flux-2-pro", name: "FLUX.2 Pro (UC)" },
|
||||
{ id: "lustify-v7", name: "Lustify v7 (UC)" },
|
||||
{ id: "nano-banana", name: "Nano Banana (UC)" },
|
||||
{ id: "nano-banana-2", name: "Nano Banana 2 (UC)" },
|
||||
{ id: "nano-banana-pro", name: "Nano Banana Pro (UC)" },
|
||||
{ id: "nano-banana-ultra", name: "Nano Banana Ultra (UC)" },
|
||||
{ id: "gpt-image", name: "GPT Image (UC)" },
|
||||
{ id: "gpt-image-2", name: "GPT Image 2 (UC)" },
|
||||
{ id: "realism", name: "Realism (UC)" },
|
||||
{ id: "realism-2", name: "Realism 2 (UC)" },
|
||||
{ id: "z-image-turbo", name: "Z-Image Turbo (UC)" },
|
||||
{ id: "prefect-pony-xl", name: "Prefect Pony XL (UC)" },
|
||||
{ id: "wan-2.6", name: "Wan 2.6 (UC)" },
|
||||
{ id: "wan-2.7-text-to-image", name: "Wan 2.7 Text-to-Image (UC)" },
|
||||
{ id: "wan-2.7-text-to-image-pro", name: "Wan 2.7 Text-to-Image Pro (UC)" },
|
||||
],
|
||||
// Persona web derives imageWidth/imageHeight from an aspect ratio; uc-direct
|
||||
// passes any OpenAI-style size through. These are the aspect buckets.
|
||||
supportedSizes: ["1024x1024", "1024x576", "576x1024", "1024x768", "768x1024"],
|
||||
},
|
||||
|
||||
xai: {
|
||||
id: "xai",
|
||||
baseUrl: "https://api.x.ai/v1/images/generations",
|
||||
@@ -894,6 +855,44 @@ export const IMAGE_PROVIDERS: Record<string, ImageProviderConfig> = {
|
||||
supportedSizes: ["1024x1024", "2048x2048"],
|
||||
},
|
||||
aihorde: AI_HORDE_IMAGE_PROVIDER,
|
||||
|
||||
// Keep UC after every existing image provider because parseImageModel() resolves
|
||||
// bare duplicate ids by first match. Explicit `uc/` routes remain available while
|
||||
// historical owners retain bare ids such as nano-banana and z-image-turbo.
|
||||
uc: {
|
||||
id: "uc",
|
||||
baseUrl: "https://internal.chatuncensored.ai/v2/image-gen",
|
||||
authType: "apikey",
|
||||
authHeader: "bearer",
|
||||
format: "uc-image",
|
||||
models: [
|
||||
{ id: "model-dev", name: "Flux Dev (UC)" },
|
||||
{ id: "model-pro", name: "Flux Pro (UC)" },
|
||||
{ id: "model-1.1", name: "Flux Pro 1.1 (UC)" },
|
||||
{ id: "model-1.2", name: "Wan 2.2 (UC)" },
|
||||
{ id: "seedream-v4.5", name: "Seedream v4.5 (UC)" },
|
||||
{ id: "seedream-v5", name: "Seedream v5 (UC)" },
|
||||
{ id: "flux-2", name: "FLUX.2 (UC)" },
|
||||
{ id: "flux-2-pro", name: "FLUX.2 Pro (UC)" },
|
||||
{ id: "lustify-v7", name: "Lustify v7 (UC)" },
|
||||
{ id: "nano-banana", name: "Nano Banana (UC)" },
|
||||
{ id: "nano-banana-2", name: "Nano Banana 2 (UC)" },
|
||||
{ id: "nano-banana-pro", name: "Nano Banana Pro (UC)" },
|
||||
{ id: "nano-banana-ultra", name: "Nano Banana Ultra (UC)" },
|
||||
{ id: "gpt-image", name: "GPT Image (UC)" },
|
||||
{ id: "gpt-image-2", name: "GPT Image 2 (UC)" },
|
||||
{ id: "realism", name: "Realism (UC)" },
|
||||
{ id: "realism-2", name: "Realism 2 (UC)" },
|
||||
{ id: "z-image-turbo", name: "Z-Image Turbo (UC)" },
|
||||
{ id: "prefect-pony-xl", name: "Prefect Pony XL (UC)" },
|
||||
{ id: "wan-2.6", name: "Wan 2.6 (UC)" },
|
||||
{ id: "wan-2.7-text-to-image", name: "Wan 2.7 Text-to-Image (UC)" },
|
||||
{ id: "wan-2.7-text-to-image-pro", name: "Wan 2.7 Text-to-Image Pro (UC)" },
|
||||
],
|
||||
// Persona web derives imageWidth/imageHeight from an aspect ratio; uc-direct
|
||||
// passes any OpenAI-style size through. These are the aspect buckets.
|
||||
supportedSizes: ["1024x1024", "1024x576", "576x1024", "1024x768", "768x1024"],
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -216,12 +216,6 @@ export const PACK_ARTIFACT_REQUIRED_PATHS: string[] = [
|
||||
"bin/mcpStdioConsoleGuard.mjs",
|
||||
"bin/nodeRuntimeSupport.mjs",
|
||||
"bin/omniroute.mjs",
|
||||
// #11437: bin/omniroute.mjs imports ./cli/utils/volatileEnvPath.mjs at startup
|
||||
// (describeVolatileEnvWarning — flags a .env living inside the installed package).
|
||||
// bin/cli/ is only an allowlist PREFIX, so its absence would never fail the
|
||||
// unexpected-paths check; list it REQUIRED so a regression is loud (#7065 class,
|
||||
// enforced by tests/unit/pack-artifact-entrypoint-closures.test.ts).
|
||||
"bin/cli/utils/volatileEnvPath.mjs",
|
||||
// #7808: aliasResolver + its hook file. bin/omniroute.mjs imports
|
||||
// bin/aliasResolver.mjs at startup, which in turn registers
|
||||
// bin/aliasResolverHook.mjs as the ESM loader. Both must ship in the tarball
|
||||
|
||||
@@ -22,7 +22,14 @@ import path from "node:path";
|
||||
*/
|
||||
export function isTransientProbeError(error: unknown): boolean {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
if (/SQLITE_BUSY|SQLITE_PROTOCOL|SQLITE_IOERR|ENOENT|database is locked/i.test(message)) {
|
||||
// #12423 widened the message side: SQLite also reports "database table is
|
||||
// locked", "database schema is locked" and "database is busy" for the same
|
||||
// transient contention that "database is locked" covers.
|
||||
if (
|
||||
/SQLITE_BUSY|SQLITE_PROTOCOL|SQLITE_IOERR|ENOENT|database(?: table| schema)? is (?:locked|busy)/i.test(
|
||||
message
|
||||
)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
// The real drivers do not put the result-code name in the message: both
|
||||
|
||||
@@ -348,6 +348,8 @@ export const WEB_SESSION_CREDENTIAL_REQUIREMENTS = {
|
||||
"maxaiDeviceId",
|
||||
"userId",
|
||||
"maxaiUserId",
|
||||
],
|
||||
},
|
||||
uc: {
|
||||
// UC (uncensored.com) persona: auth is the durable Clerk `__client` cookie
|
||||
// (a JWT with no exp) plus the session id + user id, all stored in
|
||||
|
||||
@@ -50,6 +50,11 @@ test("FIX-GREEN: isTransientProbeError does NOT classify fatal errors", () => {
|
||||
test("FIX-GREEN: isTransientProbeError classifies BUSY/PROTOCOL/IOERR/ENOENT", () => {
|
||||
const transientPatterns = [
|
||||
"SQLITE_BUSY: database is locked",
|
||||
// better-sqlite3 can omit the symbolic SQLite error code entirely.
|
||||
"database is locked",
|
||||
"database table is locked",
|
||||
"database schema is locked: main",
|
||||
"database is busy",
|
||||
"SQLITE_PROTOCOL: locking protocol",
|
||||
"SQLITE_IOERR: disk I/O error",
|
||||
"ENOENT: no such file or directory, open '/tmp/db.sqlite'",
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
UC_PERSONA_IMAGE_URL,
|
||||
UC_DIRECT_IMAGE_URL,
|
||||
} from "../../open-sse/handlers/imageGeneration/providers/ucImage.ts";
|
||||
import { IMAGE_PROVIDERS } from "../../open-sse/config/imageRegistry.ts";
|
||||
import { IMAGE_PROVIDERS, parseImageModel } from "../../open-sse/config/imageRegistry.ts";
|
||||
|
||||
// A valid PERSONA credential (durable Clerk cookie + sid + uid in psd). No API
|
||||
// key, so the handler takes the persona web path (mint -> POST -> poll).
|
||||
@@ -51,6 +51,25 @@ test("uc is registered in IMAGE_PROVIDERS with the uc-image format + 22 models",
|
||||
assert.equal((entry.models ?? []).length, 22);
|
||||
});
|
||||
|
||||
test("uc image models require an explicit prefix when an existing provider owns the bare id", () => {
|
||||
assert.deepEqual(parseImageModel("uc/nano-banana"), {
|
||||
provider: "uc",
|
||||
model: "nano-banana",
|
||||
});
|
||||
assert.deepEqual(parseImageModel("uc/z-image-turbo"), {
|
||||
provider: "uc",
|
||||
model: "z-image-turbo",
|
||||
});
|
||||
assert.deepEqual(parseImageModel("nano-banana"), {
|
||||
provider: "adobe-firefly",
|
||||
model: "nano-banana",
|
||||
});
|
||||
assert.deepEqual(parseImageModel("z-image-turbo"), {
|
||||
provider: "nanogpt",
|
||||
model: "z-image-turbo",
|
||||
});
|
||||
});
|
||||
|
||||
// --- Pure helpers --------------------------------------------------------
|
||||
|
||||
test("resolveUcImageModel strips uc/ and uc-direct/ prefixes", () => {
|
||||
@@ -226,7 +245,9 @@ test("handleUcImageGeneration (persona) 401s (retryable) when the credential is
|
||||
test("handleUcImageGeneration (persona) times out with 504 when the result never readies", async () => {
|
||||
const resultUrl = "https://gen.moveinwater.com/img_never.png";
|
||||
const fetchImpl = personaFetch({
|
||||
pendingPolls: 1000, // never becomes ready within the window
|
||||
// The injected no-op sleep can execute more than 1,000 polls inside 5 ms on
|
||||
// fast runners, so use an unbounded pending count to make the timeout deterministic.
|
||||
pendingPolls: Number.POSITIVE_INFINITY,
|
||||
resultUrl,
|
||||
jwt: fakeJwt("uid", FUTURE_EXP),
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user