mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-16 20:02:45 +03:00
* fix(adobe-firefly): open browser sign-in and resolve provider slug in /login
POST /api/providers/[id]/login passed the connection DB id to
inAppLoginService.startLogin, but that service looks up the provider by
slug in TOKEN_EXTRACTION_CONFIGS. The lookup always missed and returned
"No extraction config" without launching a browser — so the VibeProxy
"Sign in" button for Adobe Firefly (and every other web-cookie provider)
never opened a browser.
Adobe Firefly additionally had no extraction config because its IMS JWT
is never in cookies/localStorage — it only rides on the Authorization:
Bearer header of firefly-3p.ff.adobe.io XHRs.
- Resolve the provider slug from the connection row and pass the slug
(not the DB id) to inAppLoginService.startLogin.
- Add open-sse/services/adobeFireflyBrowserLogin.ts: a Playwright
service that launches a visible browser at firefly.adobe.com and
intercepts firefly-3p requests to capture the IMS JWT + sherlockToken
cookie. Wire it into the /login route for the adobe-firefly slug.
- Fix latent bug: updateProviderConnection reads camelCase keys
(apiKey, providerSpecificData), so the previous snake_case call never
persisted extracted credentials.
* fix(adobe-firefly): open browser sign-in and resolve provider slug in /login
POST /api/providers/[id]/login passed the connection DB id to
inAppLoginService.startLogin, but TOKEN_EXTRACTION_CONFIGS is keyed by
provider slug — so browser login never launched for web-cookie providers.
Adobe Firefly also cannot use cookie extraction: the IMS JWT only appears
on Authorization headers to firefly-3p.ff.adobe.io. Add a dedicated
Playwright interceptor and persist credentials with camelCase keys that
updateProviderConnection actually reads.
* fix(adobe-firefly): use system Chrome/Edge CDP for browser sign-in
Playwright is not available inside the pkg-packaged VibeProxyServices.exe,
so import('playwright') always failed with 'Playwright not installed' and
never opened a window. Launch Chrome/Edge with --remote-debugging-port and
capture the firefly-3p Authorization Bearer via pure CDP WebSocket instead.
* fix(adobe-firefly): live x-arp-session-id / Arkose wire (stop 408 under load)
Browser generate-async requires x-arp-session-id as base64({sid,ark,ftr}) with a
real Arkose blob (sherlockToken). JWT alone frequently returns colligo HTTP 408
system under load while credits still work.
- Match live ftr magic __UDF43-m4_31ck + Arkose pk in synthetic ARP fallback
- Ranked extract of sherlockToken / x-arp from Cookie, HAR, fetch() paste, and
space-joined JWT+ARP (PasswordBox newline collapse)
- Reuse one ARP for storage upload + generate-async
- Clearer 408 errors when browser ARP is missing vs stale
- Unit suite 42/42
* fix(adobe-firefly): durable session ARP rebuild and aux_sid false-positive
Rebuild x-arp-session-id from forterToken/arkose/ff_session_guid instead of
ranking long Cookie pairs (e.g. aux_sid=…) as opaque ARP, which caused colligo
HTTP 408. Cache IMS JWT + cookie sessions, rotate ARP on 408 retries, and keep
Playwright warm-up opt-in only (headless Forter is rejected).
Also expand synthetic ARP shape with bfp/fpjs to match live successful captures.
* fix(adobe-firefly): durable session, off-screen Chrome recovery, browser sign-in
Rebuild x-arp-session-id from Cookie pieces (sid/ark/forter) so aux_sid is never
sent as ARP. Sticky ARP + submit spacing reduce mid-batch colligo 408 thrash.
Add optional managed Chrome warm (off-screen headed by default; Forter rejects
headless) and POST /api/providers/{id}/login browser sign-in that returns JWT+Cookie
after a fresh SSO. Visible sign-in resets off-screen window placement and clears
prior Adobe session when adding another account.
* fix(adobe-firefly): renew sessions through durable CDP
* fix(adobe-firefly): isolate browser sessions per account
* fix(adobe-firefly): make account login fresh and deterministic
* chore(adobe-firefly): remove obsolete browser fallback
* docs(adobe-firefly): document renewal controls
* fix(adobe-firefly): harden CDP warm, risk session, and browser sign-in
Stop colligo 408 thrash from stale Forter and frozen Google login during
Sign in with browser:
- CDP warm: clear Firefly origin storage + risk cookies (keep SSO); require
forter age under 10 minutes on loop and timeout paths; dual CDP queues;
await Runtime.runIfWaitingForDebugger; profile-lock launch retries
- Session: connectionId fingerprint; write-back JWT+Cookie; warm-fail
cooldown; fail closed risk_session_stale when forter is known-stale
- Client: submit gate around generate-async; max 2 attempts when forter
known-stale; poll 401 one refresh; pass sessionBrowserKey through handlers
- Login route: pure system Chrome/Edge CDP only; camelCase credential persist
- Unit: browser-login + firefly suites green (60)
---------
Co-authored-by: artickc <artur1992123@mail.ru>
167 lines
5.2 KiB
TypeScript
167 lines
5.2 KiB
TypeScript
// Adobe Firefly (unofficial) video-generation handler.
|
|
// Family: adobe-firefly-video | Provider: adobe-firefly
|
|
//
|
|
// Credentials: IMS access_token (JWT) or full Cookie header from
|
|
// firefly.adobe.com / new.express.adobe.com.
|
|
|
|
import { saveCallLog } from "@/lib/usageDb";
|
|
import { sanitizeErrorMessage } from "../../utils/error.ts";
|
|
import {
|
|
AdobeFireflyError,
|
|
adobeFireflyGenerateVideo,
|
|
resolveAdobeSourceImageIds,
|
|
resolveAdobeVideoModel,
|
|
} from "../../services/adobeFireflyClient.ts";
|
|
import { ensureAdobeFireflySession } from "../../services/adobeFireflySession.ts";
|
|
|
|
function normalizePositiveNumber(value: unknown, fallback: number): number {
|
|
const n = Number(value);
|
|
return Number.isFinite(n) && n > 0 ? n : fallback;
|
|
}
|
|
|
|
export async function handleAdobeFireflyVideoGeneration({
|
|
model,
|
|
provider,
|
|
body,
|
|
credentials,
|
|
log,
|
|
fetchImpl = fetch,
|
|
}: {
|
|
model: string;
|
|
provider: string;
|
|
providerConfig?: { baseUrl?: string };
|
|
body: Record<string, unknown>;
|
|
credentials?: {
|
|
apiKey?: string;
|
|
accessToken?: string;
|
|
connectionId?: string;
|
|
providerSpecificData?: {
|
|
cookie?: unknown;
|
|
access_token?: unknown;
|
|
accessToken?: unknown;
|
|
browserSessionKey?: unknown;
|
|
} | null;
|
|
} | null;
|
|
log?: { info?: (...args: unknown[]) => void; error?: (...args: unknown[]) => void };
|
|
fetchImpl?: typeof fetch;
|
|
}) {
|
|
const startTime = Date.now();
|
|
const prompt = typeof body.prompt === "string" ? body.prompt.trim() : "";
|
|
if (!prompt) {
|
|
return {
|
|
success: false,
|
|
status: 400,
|
|
error: "Prompt is required for Adobe Firefly video generation",
|
|
};
|
|
}
|
|
|
|
try {
|
|
const session = await ensureAdobeFireflySession({
|
|
credentials,
|
|
fetchImpl,
|
|
log,
|
|
});
|
|
const accessToken = session.accessToken;
|
|
const sessionCookie = session.cookie || undefined;
|
|
const arpSessionId = session.arpSessionId;
|
|
const timeoutMs = normalizePositiveNumber(body.timeout_ms, 300_000);
|
|
const seed =
|
|
typeof body.seed === "number"
|
|
? body.seed
|
|
: typeof body.seed === "string" && String(body.seed).trim()
|
|
? Number(body.seed)
|
|
: undefined;
|
|
|
|
// Kling i2v / Veo ref / Sora frame: upload reference images first.
|
|
const { id: videoModelId } = resolveAdobeVideoModel(String(model));
|
|
const maxFrames = videoModelId.includes("kling") || videoModelId.includes("sora") ? 2 : 3;
|
|
const sourceImageIds = await resolveAdobeSourceImageIds({
|
|
accessToken,
|
|
body,
|
|
max: maxFrames,
|
|
sessionCookie,
|
|
arpSessionId,
|
|
prompt,
|
|
fetchImpl,
|
|
log,
|
|
});
|
|
|
|
log?.info?.(
|
|
"VIDEO",
|
|
`${provider}/${model} (adobe-firefly) | prompt: "${prompt.slice(0, 60)}${prompt.length > 60 ? "..." : ""}"` +
|
|
(sourceImageIds.length ? ` | frames: ${sourceImageIds.length}` : "") +
|
|
` | session=${session.source}`
|
|
);
|
|
|
|
const result = await adobeFireflyGenerateVideo({
|
|
accessToken,
|
|
prompt,
|
|
model,
|
|
size: body.size,
|
|
aspectRatio: body.aspect_ratio ?? body.aspectRatio ?? body.ratio ?? body.size,
|
|
duration: body.duration ?? body.durationSeconds,
|
|
quality: body.quality,
|
|
resolution: body.resolution ?? body.quality,
|
|
seed: Number.isFinite(seed as number) ? (seed as number) : undefined,
|
|
negativePrompt:
|
|
typeof body.negative_prompt === "string"
|
|
? body.negative_prompt
|
|
: typeof body.negativePrompt === "string"
|
|
? body.negativePrompt
|
|
: undefined,
|
|
generateAudio: body.generate_audio !== false && body.generateAudio !== false,
|
|
sourceImageIds: sourceImageIds.length ? sourceImageIds : undefined,
|
|
sessionCookie,
|
|
arpSessionId,
|
|
sessionFingerprint: session.fingerprint,
|
|
sessionBrowserKey: session.browserSessionKey,
|
|
timeoutMs,
|
|
fetchImpl,
|
|
log,
|
|
});
|
|
|
|
saveCallLog({
|
|
method: "POST",
|
|
path: "/v1/videos/generations",
|
|
status: 200,
|
|
model: `${provider}/${model}`,
|
|
provider,
|
|
duration: Date.now() - startTime,
|
|
}).catch(() => {});
|
|
|
|
return {
|
|
success: true,
|
|
data: {
|
|
created: Math.floor(Date.now() / 1000),
|
|
data: [{ url: result.url, format: result.format || "mp4" }],
|
|
},
|
|
};
|
|
} catch (err) {
|
|
if (err instanceof AdobeFireflyError) {
|
|
log?.error?.("VIDEO", `${provider} adobe-firefly error ${err.status}: ${err.message}`);
|
|
saveCallLog({
|
|
method: "POST",
|
|
path: "/v1/videos/generations",
|
|
status: err.status,
|
|
model: `${provider}/${model}`,
|
|
provider,
|
|
duration: Date.now() - startTime,
|
|
error: err.message.slice(0, 500),
|
|
}).catch(() => {});
|
|
return { success: false, status: err.status, error: err.message };
|
|
}
|
|
const errorText = sanitizeErrorMessage(err instanceof Error ? err.message : String(err));
|
|
log?.error?.("VIDEO", `${provider} adobe-firefly exception: ${errorText}`);
|
|
saveCallLog({
|
|
method: "POST",
|
|
path: "/v1/videos/generations",
|
|
status: 500,
|
|
model: `${provider}/${model}`,
|
|
provider,
|
|
duration: Date.now() - startTime,
|
|
error: errorText.slice(0, 500),
|
|
}).catch(() => {});
|
|
return { success: false, status: 500, error: errorText };
|
|
}
|
|
}
|