#!/usr/bin/env node /** * 一键更新 Dify workflow 的装饰层 prompt * * 用法: * 1) 在 Dify 控制台 → Settings → Account → 生成 Personal Access Token(管理员权限) * 2) 运行: DIFY_BASE_URL=https://api.dify.ai/v1 DIFY_ADMIN_TOKEN=pat-xxx node patch_dify_overlay_prompt.mjs * * app_id 在 URL 里: https://your-dify/app//workflow * * 说明: * - 脚本会: * 1) GET /apps/{app_id}/export → 下载当前 DSL * 2) 在 workflow.graph.nodes 中找到 title === "MiniMax 批量装饰图" 的节点 * 3) 把 body.data.prompt 改成新模板 * 4) POST /apps/import → 创建新 App(避免直接覆盖线上) * 5) 输出新 App 的 ID,您可选择: * a) 直接把新 App 当作 laser_card_variants_v2 部署 * b) 或者导出 DSL 后手工在控制台替换原 App */ import fs from 'node:fs/promises' import path from 'node:path' const DIFY_BASE_URL = process.env.DIFY_BASE_URL || 'http://localhost/v1' const DIFY_ADMIN_TOKEN = process.env.DIFY_ADMIN_TOKEN const NEW_OVERLAY_PROMPT = '镭射卡装饰叠加层,透明背景,软光晕与微光粒子飘散,光影流动效果,营造氛围而非遮挡主体。生成5种不同风格的装饰变体: {{#code-param.overlay_prompts_all#}}' if (!DIFY_ADMIN_TOKEN) { console.error('❌ Missing DIFY_ADMIN_TOKEN env var (PAT with admin scope)') process.exit(1) } const appId = process.argv[2] if (!appId) { console.error('❌ Usage: node patch_dify_overlay_prompt.mjs ') process.exit(1) } const headers = { Authorization: `Bearer ${DIFY_ADMIN_TOKEN}`, 'Content-Type': 'application/json', } async function api(method, urlPath, body) { const url = `${DIFY_BASE_URL.replace(/\/$/, '')}${urlPath}` const init = { method, headers } if (body) init.body = JSON.stringify(body) const res = await fetch(url, init) const text = await res.text() if (!res.ok) { throw new Error(`HTTP ${res.status} ${method} ${urlPath}\n${text.slice(0, 500)}`) } return text ? JSON.parse(text) : null } async function main() { console.log(`▶ Exporting app ${appId}...`) const dsl = await api('GET', `/apps/${appId}/export`) const nodes = dsl?.workflow?.graph?.nodes || [] const overlayNode = nodes.find((n) => n?.data?.title === 'MiniMax 批量装饰图') if (!overlayNode) { console.error('❌ 未找到 "MiniMax 批量装饰图" 节点,请检查 app_id 或确认 workflow 未被改名') process.exit(2) } console.log(`▶ 找到节点 ${overlayNode.id},更新 prompt...`) const oldData = overlayNode.data let oldPrompt = '' try { const parsed = JSON.parse(oldData.body?.data || '{}') oldPrompt = parsed.prompt || '' } catch { console.warn('⚠️ 现有 body.data 不是合法 JSON,跳过 oldPrompt 对比') } console.log(` 旧 prompt: ${oldPrompt.slice(0, 80)}...`) console.log(` 新 prompt: ${NEW_OVERLAY_PROMPT.slice(0, 80)}...`) // 构造新 body.data(必须保持 JSON 字符串格式,这是 Dify 节点存储方式) const newBodyData = JSON.stringify({ model: 'image-01', prompt: NEW_OVERLAY_PROMPT, aspect_ratio: '3:4', n: 5, prompt_optimizer: true, response_format: 'url', }) oldData.body = { ...(oldData.body || {}), data: newBodyData } console.log(`▶ Importing as new app...`) const imported = await api('POST', '/apps/import', { data: dsl, import_mode: 'yaml-content', }) const newAppId = imported?.app_id || imported?.id console.log('') console.log('✅ 完成!') console.log(` 新 App ID: ${newAppId}`) console.log(` 新 App 名称: ${imported?.name}`) console.log('') console.log('下一步:') console.log(' 1) 进 Dify 控制台打开新 App,确认装饰层 prompt 已更新') console.log(' 2) 发布新 App,获取新 API Key') console.log(' 3) 替换 backend/.env 中的 DIFY_API_KEY') console.log(' 4) 重启 Gateway') console.log('') console.log('或者:把当前 App 在控制台手工改一个节点的 prompt 也可以,见 docs/laser-card-style-pool.md 方案 A') } main().catch((err) => { console.error('❌ 失败:', err.message) process.exit(99) })