import { config as loadEnv } from "dotenv"; import { eq } from "drizzle-orm"; import { mkdir, writeFile } from "node:fs/promises"; import Module from "node:module"; import path from "node:path"; loadEnv({ path: ".env.local" }); const moduleLoader = Module as typeof Module & { _load: (request: string, parent: NodeModule | null, isMain: boolean) => unknown; }; const originalLoad = moduleLoader._load; moduleLoader._load = function patchedServerOnlyLoad( this: typeof moduleLoader, request, parent, isMain ) { if (request === "server-only") { return {}; } return originalLoad.call(this, request, parent, isMain); }; const PREVIEW_WIDTH = 230; const PREVIEW_QUALITY = 76; const dryRun = process.argv.includes("--dry-run"); function normalizeUrl(value: unknown): string { return String(value || "").trim(); } function isAbsoluteHttpUrl(value: string) { return /^https?:\/\//i.test(value); } function isUnsupportedPreviewSource(value: string) { return !value || value.startsWith("data:") || value.startsWith("blob:"); } function withoutKnownImageProcess(url: URL) { const next = new URL(url.toString()); next.searchParams.delete("x-oss-process"); const rawSearch = next.search.replace(/^\?/, ""); if (/^imageView2\//i.test(rawSearch)) { next.search = ""; } return next; } function buildOptimizedPreviewUrl(source: string): string | null { const normalized = normalizeUrl(source); if (!isAbsoluteHttpUrl(normalized) || isUnsupportedPreviewSource(normalized)) { return null; } let url: URL; try { url = new URL(normalized); } catch { return null; } const host = url.hostname.toLowerCase(); const sourceUrl = withoutKnownImageProcess(url); if (host.includes("aliyuncs.com") || host.includes("oss-")) { sourceUrl.searchParams.set( "x-oss-process", `image/resize,w_${PREVIEW_WIDTH}/quality,q_${PREVIEW_QUALITY}/format,webp` ); return sourceUrl.toString(); } if (host.includes("qiniu") || host.includes("clouddn") || host.includes("qiniucdn")) { const separator = sourceUrl.search ? "&" : "?"; return `${sourceUrl.toString()}${separator}imageView2/2/w/${PREVIEW_WIDTH}/q/${PREVIEW_QUALITY}/format/webp`; } return null; } function visitOptions(options: any[], visitor: (option: any) => void) { for (const option of options || []) { visitor(option); visitOptions(option.children || [], visitor); } } function visitSteps(steps: any[], visitor: (option: any) => void) { for (const step of steps || []) { visitOptions(step.options || [], visitor); visitSteps(step.children || [], visitor); } } function createBackupPath() { const timestamp = new Date().toISOString().replace(/[:.]/g, "-"); return path.join( process.cwd(), "backups", `agent-workflow-ui-config-before-url-rewrite-${timestamp}.json` ); } async function main() { const dbModule = await import("../lib/db"); const schemaModule = await import("../lib/db/schema"); const workflowConfig = await import("../lib/agents/workflow-ui-config"); const { db } = dbModule; const { systemSettings } = schemaModule; const [setting] = await db .select({ value: systemSettings.value }) .from(systemSettings) .where(eq(systemSettings.key, workflowConfig.AGENT_WORKFLOW_UI_CONFIG_KEY)) .limit(1); if (!setting?.value) { console.log("No agent workflow UI config found."); return; } const normalized = workflowConfig.normalizeAgentWorkflowUiConfig(setting.value); const nextConfig = structuredClone(normalized); const summary = { total: 0, rewritten: 0, skipped: 0, unsupported: 0, }; visitSteps(nextConfig.steps || [], (option) => { summary.total += 1; const currentPreview = normalizeUrl(option.previewImageUrl); const preferredSource = normalizeUrl(option.previewImageSourceUrl); const source = preferredSource || currentPreview; const optimized = buildOptimizedPreviewUrl(source) || (preferredSource && currentPreview !== preferredSource ? buildOptimizedPreviewUrl(currentPreview) : null); if (!currentPreview || !source) { summary.skipped += 1; return; } if (!optimized) { summary.unsupported += 1; return; } if (currentPreview === optimized && option.previewImageSourceUrl === source) { summary.skipped += 1; return; } option.previewImageUrl = optimized; option.previewImageSourceUrl = source; summary.rewritten += 1; }); console.log(JSON.stringify({ dryRun, summary }, null, 2)); if (dryRun || summary.rewritten === 0) { return; } const backupPath = createBackupPath(); await mkdir(path.dirname(backupPath), { recursive: true }); await writeFile( backupPath, JSON.stringify( { key: workflowConfig.AGENT_WORKFLOW_UI_CONFIG_KEY, backedUpAt: new Date().toISOString(), value: setting.value, }, null, 2 ) ); console.log(`Backup written: ${backupPath}`); await db .update(systemSettings) .set({ value: nextConfig, updatedAt: new Date(), }) .where(eq(systemSettings.key, workflowConfig.AGENT_WORKFLOW_UI_CONFIG_KEY)); console.log(`Rewritten ${summary.rewritten} agent workflow preview image URLs.`); } main().catch((error) => { console.error(error); process.exit(1); });