Vendor OpenClaw source as Adolf fork baseline
Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled
Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled
Adolf is a fork/vendored clone of github.com/openclaw/openclaw (v2026.6.11), free to diverge. Tree copied sans upstream .git; upstream remote added for future syncs. Node pinned to 24 (.nvmrc); engines already require >=22.19. Preserves docs/ARCHITECTURE.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LeqyaxJF2nbRXJtae2kNB2
This commit is contained in:
333
extensions/vydra/shared.ts
Normal file
333
extensions/vydra/shared.ts
Normal file
@@ -0,0 +1,333 @@
|
||||
// Vydra plugin module implements shared behavior.
|
||||
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
|
||||
import { extensionForMime } from "openclaw/plugin-sdk/media-mime";
|
||||
import { resolveApiKeyForProvider } from "openclaw/plugin-sdk/provider-auth-runtime";
|
||||
import {
|
||||
assertOkOrThrowHttpError,
|
||||
createProviderOperationDeadline,
|
||||
fetchWithTimeout,
|
||||
readProviderJsonResponse,
|
||||
resolveProviderOperationTimeoutMs,
|
||||
resolveProviderHttpRequestConfig,
|
||||
waitProviderOperationPollInterval,
|
||||
type ProviderOperationDeadline,
|
||||
type ProviderOperationTimeoutMs,
|
||||
} from "openclaw/plugin-sdk/provider-http";
|
||||
import { readResponseWithLimit } from "openclaw/plugin-sdk/response-limit-runtime";
|
||||
import {
|
||||
normalizeOptionalLowercaseString,
|
||||
normalizeOptionalString,
|
||||
} from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||
|
||||
export const DEFAULT_VYDRA_BASE_URL = "https://www.vydra.ai/api/v1";
|
||||
export const DEFAULT_VYDRA_IMAGE_MODEL = "grok-imagine";
|
||||
export const DEFAULT_VYDRA_VIDEO_MODEL = "veo3";
|
||||
export const DEFAULT_VYDRA_SPEECH_MODEL = "elevenlabs/tts";
|
||||
export const DEFAULT_VYDRA_VOICE_ID = "21m00Tcm4TlvDq8ikWAM";
|
||||
const DEFAULT_HTTP_TIMEOUT_MS = 120_000;
|
||||
const DEFAULT_GENERATED_IMAGE_MAX_BYTES = 6 * 1024 * 1024;
|
||||
const DEFAULT_GENERATED_AUDIO_MAX_BYTES = 16 * 1024 * 1024;
|
||||
const DEFAULT_GENERATED_VIDEO_MAX_BYTES = 16 * 1024 * 1024;
|
||||
const POLL_INTERVAL_MS = 2_500;
|
||||
const MAX_POLL_ATTEMPTS = 120;
|
||||
type VydraAuthStore = Parameters<typeof resolveApiKeyForProvider>[0]["store"];
|
||||
|
||||
type VydraMediaKind = "audio" | "image" | "video";
|
||||
|
||||
type VydraJobPayload = {
|
||||
id?: string;
|
||||
jobId?: string;
|
||||
status?: string;
|
||||
message?: string;
|
||||
error?: string | { message?: string; detail?: string } | null;
|
||||
};
|
||||
|
||||
function asObject(value: unknown): Record<string, unknown> | undefined {
|
||||
return typeof value === "object" && value !== null && !Array.isArray(value)
|
||||
? (value as Record<string, unknown>)
|
||||
: undefined;
|
||||
}
|
||||
|
||||
function addUrlValue(value: unknown, urls: Set<string>): void {
|
||||
if (typeof value === "string") {
|
||||
const trimmed = value.trim();
|
||||
if (/^https?:\/\//iu.test(trimmed)) {
|
||||
urls.add(trimmed);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
for (const entry of value) {
|
||||
addUrlValue(entry, urls);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const trimToUndefined = normalizeOptionalString;
|
||||
|
||||
export function normalizeVydraBaseUrl(value: string | undefined): string {
|
||||
const fallback = DEFAULT_VYDRA_BASE_URL;
|
||||
const trimmed = trimToUndefined(value);
|
||||
if (!trimmed) {
|
||||
return fallback;
|
||||
}
|
||||
try {
|
||||
const url = new URL(trimmed);
|
||||
if (url.hostname === "vydra.ai") {
|
||||
url.hostname = "www.vydra.ai";
|
||||
}
|
||||
const pathname = url.pathname.replace(/\/+$/u, "");
|
||||
if (!pathname) {
|
||||
url.pathname = "/api/v1";
|
||||
} else {
|
||||
url.pathname = pathname;
|
||||
}
|
||||
return url.toString().replace(/\/$/u, "");
|
||||
} catch {
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
|
||||
function resolveVydraBaseUrlFromConfig(cfg: unknown): string {
|
||||
const models = asObject(asObject(cfg)?.models);
|
||||
const providers = asObject(models?.providers);
|
||||
const vydra = asObject(providers?.vydra);
|
||||
return normalizeVydraBaseUrl(trimToUndefined(vydra?.baseUrl));
|
||||
}
|
||||
|
||||
export async function resolveVydraRequestContext(params: {
|
||||
cfg: OpenClawConfig;
|
||||
agentDir?: string;
|
||||
authStore?: VydraAuthStore;
|
||||
capability: "image" | "video";
|
||||
}): Promise<{
|
||||
fetchFn: typeof fetch;
|
||||
baseUrl: string;
|
||||
allowPrivateNetwork: boolean;
|
||||
headers: Headers;
|
||||
dispatcherPolicy: ReturnType<typeof resolveProviderHttpRequestConfig>["dispatcherPolicy"];
|
||||
}> {
|
||||
const auth = await resolveApiKeyForProvider({
|
||||
provider: "vydra",
|
||||
cfg: params.cfg,
|
||||
agentDir: params.agentDir,
|
||||
store: params.authStore,
|
||||
});
|
||||
if (!auth.apiKey) {
|
||||
throw new Error("Vydra API key missing");
|
||||
}
|
||||
const fetchFn = fetch;
|
||||
const { baseUrl, allowPrivateNetwork, headers, dispatcherPolicy } =
|
||||
resolveProviderHttpRequestConfig({
|
||||
baseUrl: resolveVydraBaseUrlFromConfig(params.cfg),
|
||||
defaultBaseUrl: DEFAULT_VYDRA_BASE_URL,
|
||||
allowPrivateNetwork: false,
|
||||
defaultHeaders: {
|
||||
Authorization: `Bearer ${auth.apiKey}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
provider: "vydra",
|
||||
capability: params.capability,
|
||||
transport: "http",
|
||||
});
|
||||
return {
|
||||
fetchFn,
|
||||
baseUrl,
|
||||
allowPrivateNetwork,
|
||||
headers,
|
||||
dispatcherPolicy,
|
||||
};
|
||||
}
|
||||
|
||||
export function resolveVydraResponseJobId(payload: unknown): string | undefined {
|
||||
const object = asObject(payload) as VydraJobPayload | undefined;
|
||||
return trimToUndefined(object?.jobId) ?? trimToUndefined(object?.id);
|
||||
}
|
||||
|
||||
export function resolveVydraResponseStatus(payload: unknown): string | undefined {
|
||||
return normalizeOptionalLowercaseString(trimToUndefined(asObject(payload)?.status));
|
||||
}
|
||||
|
||||
function resolveVydraErrorMessage(payload: unknown): string | undefined {
|
||||
const object = asObject(payload) as VydraJobPayload | undefined;
|
||||
const error = object?.error;
|
||||
if (typeof error === "string" && error.trim()) {
|
||||
return error.trim();
|
||||
}
|
||||
const errorObject = asObject(error);
|
||||
return (
|
||||
trimToUndefined(errorObject?.message) ??
|
||||
trimToUndefined(errorObject?.detail) ??
|
||||
trimToUndefined(object?.message)
|
||||
);
|
||||
}
|
||||
|
||||
export function extractVydraResultUrls(payload: unknown, kind: VydraMediaKind): string[] {
|
||||
const urls = new Set<string>();
|
||||
const preferredKeys =
|
||||
kind === "audio"
|
||||
? ["audioUrl", "audioUrls"]
|
||||
: kind === "image"
|
||||
? ["imageUrl", "imageUrls"]
|
||||
: ["videoUrl", "videoUrls"];
|
||||
const sharedKeys = ["resultUrl", "resultUrls", "outputUrl", "outputUrls", "url", "urls"];
|
||||
const recurseKeys = ["output", "outputs", "result", "results", "data", "asset", "assets"];
|
||||
|
||||
const visit = (value: unknown, depth = 0) => {
|
||||
if (depth > 5) {
|
||||
return;
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
for (const entry of value) {
|
||||
visit(entry, depth + 1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
const object = asObject(value);
|
||||
if (!object) {
|
||||
return;
|
||||
}
|
||||
for (const key of [...preferredKeys, ...sharedKeys]) {
|
||||
addUrlValue(object[key], urls);
|
||||
}
|
||||
for (const key of recurseKeys) {
|
||||
if (key in object) {
|
||||
visit(object[key], depth + 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
visit(payload);
|
||||
return [...urls];
|
||||
}
|
||||
|
||||
function resolveVydraFileExtension(kind: VydraMediaKind, mimeType: string): string {
|
||||
return (
|
||||
extensionForMime(mimeType)?.slice(1) ??
|
||||
(kind === "image" ? "png" : kind === "audio" ? "mp3" : "mp4")
|
||||
);
|
||||
}
|
||||
|
||||
function resolveVydraHttpTimeoutMs(timeoutMs: ProviderOperationTimeoutMs | undefined): number {
|
||||
const resolved = typeof timeoutMs === "function" ? timeoutMs() : timeoutMs;
|
||||
if (typeof resolved !== "number" || !Number.isFinite(resolved) || resolved <= 0) {
|
||||
return DEFAULT_HTTP_TIMEOUT_MS;
|
||||
}
|
||||
return resolved;
|
||||
}
|
||||
|
||||
export function resolveVydraGeneratedMediaMaxBytes(params: {
|
||||
cfg: { agents?: { defaults?: { mediaMaxMb?: number } } };
|
||||
kind: VydraMediaKind;
|
||||
}): number {
|
||||
const configured = params.cfg.agents?.defaults?.mediaMaxMb;
|
||||
if (typeof configured === "number" && Number.isFinite(configured) && configured > 0) {
|
||||
return Math.floor(configured * 1024 * 1024);
|
||||
}
|
||||
if (params.kind === "image") {
|
||||
return DEFAULT_GENERATED_IMAGE_MAX_BYTES;
|
||||
}
|
||||
if (params.kind === "audio") {
|
||||
return DEFAULT_GENERATED_AUDIO_MAX_BYTES;
|
||||
}
|
||||
return DEFAULT_GENERATED_VIDEO_MAX_BYTES;
|
||||
}
|
||||
|
||||
export async function downloadVydraAsset(params: {
|
||||
url: string;
|
||||
kind: VydraMediaKind;
|
||||
timeoutMs?: ProviderOperationTimeoutMs;
|
||||
fetchFn: typeof fetch;
|
||||
maxBytes: number;
|
||||
}): Promise<{ buffer: Buffer; mimeType: string; fileName: string }> {
|
||||
const timeoutMs = resolveVydraHttpTimeoutMs(params.timeoutMs);
|
||||
const response = await fetchWithTimeout(params.url, { method: "GET" }, timeoutMs, params.fetchFn);
|
||||
await assertOkOrThrowHttpError(response, `Vydra ${params.kind} download failed`);
|
||||
const mimeType =
|
||||
response.headers.get("content-type")?.trim() ||
|
||||
(params.kind === "image" ? "image/png" : params.kind === "audio" ? "audio/mpeg" : "video/mp4");
|
||||
const buffer = await readResponseWithLimit(response, params.maxBytes, {
|
||||
chunkTimeoutMs: timeoutMs,
|
||||
onOverflow: ({ maxBytes }) =>
|
||||
new Error(`Vydra ${params.kind} download exceeds ${maxBytes} bytes`),
|
||||
onIdleTimeout: ({ chunkTimeoutMs }) =>
|
||||
new Error(`Vydra ${params.kind} download stalled after ${chunkTimeoutMs}ms`),
|
||||
});
|
||||
const extension = resolveVydraFileExtension(params.kind, mimeType);
|
||||
const fileStem = params.kind === "image" ? "image" : params.kind === "audio" ? "audio" : "video";
|
||||
return {
|
||||
buffer,
|
||||
mimeType,
|
||||
fileName: `${fileStem}-1.${extension}`,
|
||||
};
|
||||
}
|
||||
|
||||
async function waitForVydraJob(params: {
|
||||
baseUrl: string;
|
||||
jobId: string;
|
||||
headers: Headers;
|
||||
timeoutMs?: number;
|
||||
deadline?: ProviderOperationDeadline;
|
||||
fetchFn: typeof fetch;
|
||||
kind: VydraMediaKind;
|
||||
}): Promise<unknown> {
|
||||
const deadline =
|
||||
params.deadline ??
|
||||
createProviderOperationDeadline({
|
||||
timeoutMs: params.timeoutMs,
|
||||
label: `Vydra job ${params.jobId}`,
|
||||
});
|
||||
for (let attempt = 0; attempt < MAX_POLL_ATTEMPTS; attempt += 1) {
|
||||
const response = await fetchWithTimeout(
|
||||
`${params.baseUrl}/jobs/${params.jobId}`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: params.headers,
|
||||
},
|
||||
resolveProviderOperationTimeoutMs({ deadline, defaultTimeoutMs: DEFAULT_HTTP_TIMEOUT_MS }),
|
||||
params.fetchFn,
|
||||
);
|
||||
await assertOkOrThrowHttpError(response, "Vydra job status request failed");
|
||||
const payload = await readProviderJsonResponse<unknown>(response, "Vydra job status");
|
||||
const status = resolveVydraResponseStatus(payload);
|
||||
if (status === "completed" || extractVydraResultUrls(payload, params.kind).length > 0) {
|
||||
return payload;
|
||||
}
|
||||
if (status === "failed" || status === "error" || status === "cancelled") {
|
||||
throw new Error(resolveVydraErrorMessage(payload) ?? `Vydra job ${params.jobId} failed`);
|
||||
}
|
||||
await waitProviderOperationPollInterval({ deadline, pollIntervalMs: POLL_INTERVAL_MS });
|
||||
}
|
||||
throw new Error(`Vydra job ${params.jobId} did not finish in time`);
|
||||
}
|
||||
|
||||
export async function resolveCompletedVydraPayload(params: {
|
||||
submitted: unknown;
|
||||
baseUrl: string;
|
||||
headers: Headers;
|
||||
timeoutMs?: number;
|
||||
deadline?: ProviderOperationDeadline;
|
||||
fetchFn: typeof fetch;
|
||||
kind: VydraMediaKind;
|
||||
missingJobIdMessage: string;
|
||||
}): Promise<unknown> {
|
||||
if (
|
||||
resolveVydraResponseStatus(params.submitted) === "completed" ||
|
||||
extractVydraResultUrls(params.submitted, params.kind).length > 0
|
||||
) {
|
||||
return params.submitted;
|
||||
}
|
||||
const jobId = resolveVydraResponseJobId(params.submitted);
|
||||
if (!jobId) {
|
||||
throw new Error(resolveVydraErrorMessage(params.submitted) ?? params.missingJobIdMessage);
|
||||
}
|
||||
return waitForVydraJob({
|
||||
baseUrl: params.baseUrl,
|
||||
jobId,
|
||||
headers: params.headers,
|
||||
timeoutMs: params.timeoutMs,
|
||||
...(params.deadline ? { deadline: params.deadline } : {}),
|
||||
fetchFn: params.fetchFn,
|
||||
kind: params.kind,
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user