Files
hack/tools/scripts/llm-key-hunter/results/_cache/content/afjf123_gh-repo-clone-lioensky-vcptoolbox.json

1 line
20 KiB
JSON
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{"repo": "afjf123/gh-repo-clone-lioensky-vcptoolbox", "files": {"Plugin/DMXDoubaoGen/DoubaoGen.js": {"10182c50d3d0f903ab9932a96022dd01f605a043": {"t": 1785768923.0086474, "n": 18733, "h": "989dc03f5088e447", "c": "#!/usr/bin/env node\r\nimport axios from \"axios\";\r\nimport fs from 'fs/promises';\r\nimport path from 'path';\r\nimport { v4 as uuidv4 } from 'uuid'; // For generating unique filenames\r\nimport { fileURLToPath } from 'url'; // Needed for file:// URLs\r\nimport mime from 'mime-types'; // To determine MIME type from file path\r\n// VolcEngine SDK (if needed for signing or other functionalities - to be determined)\r\n// import * as VolcEngineSDK from '@volcengine/openapi-sdk'; // Example, actual import may vary\r\n\r\n// --- Configuration (from environment variables set by Plugin.js) ---\r\nconst VOLCENGINE_API_KEY = process.env.VOLCENGINE_API_KEY; // API Key for the image generation service (e.g., DMXAPI). Ensure this is set in config.env.\r\nconst PROJECT_BASE_PATH = process.env.PROJECT_BASE_PATH;\r\nconst SERVER_PORT = process.env.SERVER_PORT;\r\nconst IMAGESERVER_IMAGE_KEY = process.env.IMAGESERVER_IMAGE_KEY; // Key for our own image server\r\nconst VAR_HTTP_URL = process.env.VarHttpUrl; // Read VarHttpUrl from env\r\nconst VAR_HTTPS_URL = process.env.VarHttpsUrl; // Read VarHttps Url from env\r\n\r\n// API specific configurations for DMXAPI\r\nconst DMX_API_CONFIG = {\r\n BASE_URL: 'https://www.dmxapi.cn', // New API Host\r\n IMAGE_GENERATION_ENDPOINT: '/v1/images/generations', // New API Endpoint\r\n MODEL_ID: \"doubao-seedream-4-0-250828\", // New unified model\r\n DEFAULT_PARAMS: {\r\n n: 1, // Number of images to generate, typically fixed at 1 for this API\r\n }\r\n};\r\n\r\n// Helper to validate input arguments\r\nfunction isValidDoubaoGenArgs(args) {\r\n if (!args || typeof args !== 'object' || !args.command) return false;\r\n\r\n // Common validation\r\n if (typeof args.prompt !== 'string' || !args.prompt.trim()) return false;\r\n if (args.seed !== undefined && (typeof args.seed !== 'number' || !Number.isInteger(args.seed) || args.seed < 0)) return false;\r\n\r\n // Command-specific validation\r\n if (args.command === 'DoubaoGenerateImage') {\r\n if (typeof args.resolution !== 'string') return false;\r\n const parts = args.resolution.split('x');\r\n if (parts.length !== 2) return false;\r\n const width = parseInt(parts[0], 10);\r\n const height = parseInt(parts[1], 10);\r\n if (isNaN(width) || isNaN(height)) return false;\r\n if (width < 512 || width > 2048 || height < 512 || height > 2048) return false;\r\n\r\n } else if (args.command === 'DoubaoEditImage') {\r\n if (typeof args.image !== 'string' || !args.image.trim()) return false;\r\n if (typeof args.resolution !== 'string') return false; // Can be \"adaptive\" or \"WxH\"\r\n if (args.guidance_scale !== undefined) {\r\n const scale = parseFloat(args.guidance_scale);\r\n if (isNaN(scale) || scale < 0 || scale > 10) return false;\r\n }\r\n \r\n } else if (args.command === 'DoubaoComposeImage') {\r\n if (typeof args.prompt !== 'string' || !args.prompt.trim()) return false;\r\n \r\n // Find at least one image parameter\r\n const imageKeys = Object.keys(args).filter(k => k.startsWith('image_'));\r\n if (imageKeys.length === 0) return false;\r\n\r\n if (typeof args.resolution !== 'string') return false; // Can be \"adaptive\" or \"WxH\"\r\n if (args.guidance_scale !== undefined) {\r\n const scale = parseFloat(args.guidance_scale);\r\n if (isNaN(scale) || scale < 0 || scale > 10) return false;\r\n }\r\n\r\n } else {\r\n return false; // Unknown command\r\n }\r\n \r\n return true;\r\n}\r\n\r\n// --- Assuming Bearer Token Authentication ---\r\nasync function signRequest() {\r\n if (!VOLCENGINE_API_KEY) {\r\n // This warning is important if the key is missing\r\n console.warn(\"[DoubaoGen Plugin] WARN: API_KEY (e.g., VOLCENGINE_API_KEY in config.env) is not set. Authentication will likely fail.\");\r\n // Decide if to throw an error or let the API call fail\r\n // throw new Error(\"DoubaoGen Plugin Error: API_KEY (e.g., VOLCENGINE_API_KEY in config.env) is required for authentication.\");\r\n }\r\n return {\r\n 'Authorization': `Bearer ${VOLCENGINE_API_KEY}`,\r\n 'Content-Type': 'application/json',\r\n 'Accept': 'application/json', // Added based on DMXAPI example\r\n 'User-Agent': 'DMXAPI/1.0.0 (https://www.dmxapi.cn)' // Added based on DMXAPI example\r\n };\r\n}\r\n\r\n// --- Helper function to process the 'image' parameter ---\r\nasync function getImageData(imageUrl, imageBase64) {\r\n // Priority to imageBase64 if provided (on retry from file fetch)\r\n if (imageBase64) {\r\n // The provided data is a full Data URI, e.g., \"data:image/png;base64,...\"\r\n // The API expects this full string.\r\n return imageBase64;\r\n }\r\n\r\n if (!imageUrl) {\r\n return null;\r\n }\r\n\r\n // Handle Data URI\r\n if (imageUrl.startsWith('data:image/')) {\r\n return imageUrl;\r\n }\r\n\r\n // Handle public https URL\r\n if (imageUrl.startsWith('http://') || imageUrl.startsWith('https://')) {\r\n return imageUrl;\r\n }\r\n\r\n // Handle local file URL\r\n if (imageUrl.startsWith('file://')) {\r\n const filePath = fileURLToPath(imageUrl);\r\n try {\r\n const buffer = await fs.readFile(filePath);\r\n const mimeType = mime.lookup(filePath) || 'application/octet-stream';\r\n \r\n // Check file size (10MB limit)\r\n if (buffer.length > 10 * 1024 * 1024) {\r\n throw new Error(\"Image size exceeds the 10MB limit.\");\r\n }\r\n\r\n const base64Image = buffer.toString('base64');\r\n // Construct the Data URI format required by the API\r\n return `data:${mimeType};base64,${base64Image}`;\r\n } catch (e) {\r\n if (e.code === 'ENOENT') {\r\n // Hyper-Stack-Trace: File not found locally, request remote fetch.\r\n const structuredError = new Error(`File not found locally, requesting remote fetch for: ${imageUrl}`);\r\n structuredError.code = 'FILE_NOT_FOUND_LOCALLY';\r\n structuredError.fileUrl = imageUrl;\r\n throw structuredError;\r\n } else {\r\n throw new Error(`Error reading local file: ${e.message}`);\r\n }\r\n }\r\n }\r\n\r\n // If the format is unrecognized, treat it as an error.\r\n throw new Error(`Unsupported image format or protocol. Please use an https:// or file:// URL.`);\r\n}\r\n\r\n\r\nasync function generateImageAndSave(args) {\r\n // Check for essential environment variables\r\n if (!VOLCENGINE_API_KEY) {\r\n throw new Error(\"DoubaoGen Plugin Error: API_KEY (e.g., VOLCENGINE_API_KEY in config.env) environment variable is required.\");\r\n }\r\n if (!PROJECT_BASE_PATH) {\r\n throw new Error(\"DoubaoGen Plugin Error: PROJECT_BASE_PATH environment variable is required for saving images.\");\r\n }\r\n if (!SERVER_PORT) {\r\n throw new Error(\"DoubaoGen Plugin Error: SERVER_PORT environment variable is required for constructing image URL.\");\r\n }\r\n if (!IMAGESERVER_IMAGE_KEY) {\r\n throw new Error(\"DoubaoGen Plugin Error: IMAGESERVER_IMAGE_KEY environment variable is required for constructing image URL.\");\r\n }\r\n if (!VAR_HTTP_URL) {\r\n throw new Error(\"DoubaoGen Plugin Error: VarHttpUrl environment variable is required for constructing image URL.\");\r\n }\r\n\r\n const command = args.command;\r\n if (!command) {\r\n throw new Error(`DoubaoGen Plugin Error: 'command' not specified in arguments.`);\r\n }\r\n\r\n if (!isValidDoubaoGenArgs(args)) {\r\n throw new Error(`DoubaoGen Plugin Error: Invalid arguments for command '${command}': ${JSON.stringify(args)}.`);\r\n }\r\n\r\n // --- Image Data Processing (only for EditImage command) ---\r\n let imageData = null;\r\n let imagesData = [];\r\n\r\n if (command === 'DoubaoEditImage') {\r\n imageData = await getImageData(args.image, args.image_base64);\r\n if (!imageData) {\r\n // This case should ideally not be hit if validation is correct, but as a safeguard.\r\n throw new Error(\"DoubaoGen Plugin Error: 'image' parameter is required and must be processed for the DoubaoEditImage command.\");\r\n }\r\n } else if (command === 'DoubaoComposeImage') {\r\n const imageKeys = Object.keys(args).filter(k => k.startsWith('image_'));\r\n \r\n const indices = imageKeys.map(k => {\r\n const num = k.split('_').pop();\r\n return isNaN(num) ? 0 : parseInt(num, 10);\r\n }).filter(n => n > 0);\r\n\r\n if (indices.length === 0) {\r\n throw new Error(\"DoubaoGen Plugin Error: For DoubaoComposeImage, at least one 'image_N' or 'image_base64_N' (N>0) parameter is required.\");\r\n }\r\n const maxIndex = Math.max(...indices);\r\n\r\n for (let i = 1; i <= maxIndex; i++) {\r\n const imageUrlKey = `image_${i}`;\r\n const imageBase64Key = `image_base64_${i}`;\r\n \r\n const imageUrl = args[imageUrlKey];\r\n const imageBase64 = args[imageBase64Key];\r\n\r\n if (!imageUrl && !imageBase64) {\r\n throw new Error(`DoubaoGen Plugin Error: Image parameters are not continuous. Missing 'image_${i}' or 'image_base64_${i}'.`);\r\n }\r\n\r\n try {\r\n const processedImage = await getImageData(imageUrl, imageBase64);\r\n imagesData.push(processedImage);\r\n } catch (e) {\r\n if (e.code === 'FILE_NOT_FOUND_LOCALLY') {\r\n const enhancedError = new Error(`In multi-image composition, image ${i} (parameter: ${imageUrlKey}) was not found locally and requires remote fetching.`);\r\n enhancedError.code = 'FILE_NOT_FOUND_LOCALLY';\r\n enhancedError.fileUrl = e.fileUrl;\r\n enhancedError.failedParameter = imageUrlKey;\r\n throw enhancedError;\r\n }\r\n throw new Error(`Error processing image ${i} ('${imageUrlKey}'): ${e.message}`);\r\n }\r\n }\r\n }\r\n\r\n // --- Payload Construction ---\r\n const payload = {\r\n model: DMX_API_CONFIG.MODEL_ID,\r\n prompt: args.prompt,\r\n n: DMX_API_CONFIG.DEFAULT_PARAMS.n,\r\n size: args.resolution,\r\n watermark: false\r\n };\r\n\r\n if (command === 'DoubaoEditImage') {\r\n payload.image = imageData; // For single image edit\r\n if (args.guidance_scale !== undefined) {\r\n payload.guidance_scale = args.guidance_scale;\r\n }\r\n if (args.resolution.toLowerCase() === 'adaptive') {\r\n payload.size = 'adaptive';\r\n }\r\n } else if (command === 'DoubaoComposeImage') {\r\n payload.images = imagesData; // For multiple image composition\r\n if (args.guidance_scale !== undefined) {\r\n payload.guidance_scale = args.guidance_scale;\r\n }\r\n if (args.resolution.toLowerCase() === 'adaptive') {\r\n payload.size = 'adaptive';\r\n }\r\n }\r\n \r\n if (args.seed !== undefined) {\r\n payload.seed = args.seed; // Add seed if provided\r\n }\r\n\r\n // console.error(`[DoubaoGen Plugin] Sending payload to DMXAPI: ${JSON.stringify(payload)}`);\r\n\r\n // --- Bearer Token Authentication ---\r\n const headers = await signRequest();\r\n\r\n const dmxApiAxiosInstance = axios.create({\r\n baseURL: DMX_API_CONFIG.BASE_URL,\r\n headers: headers,\r\n timeout: 120000 // 120 second timeout, image generation can be slow\r\n });\r\n \r\n const fullApiUrl = DMX_API_CONFIG.IMAGE_GENERATION_ENDPOINT;\r\n\r\n const response = await dmxApiAxiosInstance.post(\r\n fullApiUrl,\r\n payload\r\n );\r\n\r\n // console.error(`[DoubaoGen Plugin] Received response from DMXAPI: ${JSON.stringify(response.data)}`);\r\n\r\n // Parse the response (assuming OpenAI API V1 compatible structure for images)\r\n // response.data usually contains { created: ..., data: [ { url: ... } or { b64_json: ... } ] }\r\n // If DMXAPI has a different response structure, this part needs adjustment.\r\n let generatedImageUrlOrBase64;\r\n const responseData = response.data?.data?.[0];\r\n\r\n if (responseData?.url) { // Check for a URL\r\n generatedImageUrlOrBase64 = responseData.url;\r\n } else if (responseData?.b64_json) { // Check for base64 data\r\n generatedImageUrlOrBase64 = responseData.b64_json;\r\n // TODO: VolcEngine might specify the image format if returning b64_json (e.g., 'png', 'jpeg')\r\n // For now, we default to 'png' later if extension not found, but API might provide this.\r\n }\r\n\r\n if (!generatedImageUrlOrBase64) {\r\n throw new Error(\"DoubaoGen Plugin Error: Failed to extract image data/URL from DMXAPI response. Response: \" + JSON.stringify(response.data));\r\n }\r\n\r\n let imageBuffer;\r\n let imageExtension = 'png'; // Default extension, adjust if VolcEngine specifies or returns different types\r\n\r\n if (generatedImageUrlOrBase64.startsWith('http')) {\r\n // If it's a URL, download it\r\n const imageResponse = await axios({\r\n method: 'get',\r\n url: generatedImageUrlOrBase64,\r\n responseType: 'arraybuffer',\r\n timeout: 60000 // 60 second timeout for image download\r\n });\r\n imageBuffer = imageResponse.data;\r\n const contentType = imageResponse.headers['content-type'];\r\n if (contentType && contentType.startsWith('image/')) {\r\n imageExtension = contentType.split('/')[1];\r\n } else {\r\n const urlExtMatch = generatedImageUrlOrBase64.match(/\\.([^.?]+)(?:[?#]|$)/);\r\n if (urlExtMatch && urlExtMatch[1]) {\r\n imageExtension = urlExtMatch[1];\r\n }\r\n }\r\n } else {\r\n // If it's base64 data\r\n imageBuffer = Buffer.from(generatedImageUrlOrBase64, 'base64');\r\n // TODO: Determine image type from DMXAPI response if possible, otherwise default (e.g., 'png')\r\n // For example, if API returns content-type for base64 or an explicit format field.\r\n }\r\n \r\n const generatedFileName = `${uuidv4()}.${imageExtension}`;\r\n const doubaoGenImageDir = path.join(PROJECT_BASE_PATH, 'image', 'doubaogen'); // Dir name can be kept or changed\r\n const localImageServerPath = path.join(doubaoGenImageDir, generatedFileName);\r\n\r\n await fs.mkdir(doubaoGenImageDir, { recursive: true });\r\n await fs.writeFile(localImageServerPath, imageBuffer);\r\n // console.error(`[DoubaoGen Plugin] Image saved to: ${localImageServerPath}`);\r\n\r\n const relativeServerPathForUrl = path.join('doubaogen', generatedFileName).replace(/\\\\\\\\/g, '/');\r\n const accessibleImageUrl = `${VAR_HTTP_URL}:${SERVER_PORT}/pw=${IMAGESERVER_IMAGE_KEY}/images/${relativeServerPathForUrl}`;\r\n // const accessibleImageUrl = `${VAR_HTTPS_URL}/pw=${IMAGESERVER_IMAGE_KEY}/images/${relativeServerPathForUrl}`;\r\n\r\n const altText = args.prompt ? args.prompt.substring(0, 80) + (args.prompt.length > 80 ? \"...\" : \"\") : (generatedFileName || \"生成的图片\");\r\n const imageHtml = `<img src=\"${accessibleImageUrl}\" alt=\"${altText}\" width=\"300\">`;\r\n const successMessage = `图片已成功生成!`;\r\n const aiInstructions = `图片已成功生成!\\n\\n` +\r\n `详细信息:\\n` +\r\n `- 图片URL: ${accessibleImageUrl}\\n` +\r\n `- 服务器路径: image/doubaogen/${generatedFileName}\\n` +\r\n `- 文件名: ${generatedFileName}\\n\\n` +\r\n `请务必使用以下HTML <img> 标签将图片直接展示给用户 (您可以调整width属性,建议200-500像素):\\n` +\r\n `${imageHtml}\\n`;\r\n\r\n const base64Image = imageBuffer.toString('base64');\r\n const imageMimeType = `image/${imageExtension}`;\r\n\r\n // Attempt to get the seed from the API response, fallback to payload or N/A\r\n const responseSeed = response.data?.data?.[0]?.seed;\r\n const payloadSeed = payload.seed;\r\n const finalSeed = responseSeed !== undefined ? responseSeed : (payloadSeed !== undefined ? payloadSeed : 'N/A');\r\n\r\n\r\n const result = {\r\n content: [\r\n {\r\n type: 'text',\r\n text: `图片已成功生成!\\n- 提示词: ${args.prompt}\\n- 分辨率: ${args.resolution}\\n- Seed: ${finalSeed}\\n- 可访问URL: ${accessibleImageUrl}\\n请将生成好的图片转发给用户哦。`\r\n },\r\n {\r\n type: 'image_url',\r\n image_url: {\r\n url: `data:${imageMimeType};base64,${base64Image}`\r\n }\r\n }\r\n ],\r\n details: { // Keep details for logging or other purposes if needed\r\n serverPath: `image/doubaogen/${generatedFileName}`,\r\n fileName: generatedFileName,\r\n prompt: args.prompt,\r\n resolution: args.resolution,\r\n seed: finalSeed,\r\n imageUrl: accessibleImageUrl\r\n }\r\n };\r\n\r\n return result;\r\n}\r\n\r\nasync function main() {\r\n let inputChunks = [];\r\n process.stdin.setEncoding('utf8');\r\n\r\n for await (const chunk of process.stdin) {\r\n inputChunks.push(chunk);\r\n }\r\n const inputData = inputChunks.join('');\r\n let parsedArgs;\r\n\r\n try {\r\n if (!inputData.trim()) {\r\n console.log(JSON.stringify({ status: \"error\", error: \"DoubaoGen Plugin Error: No input data received from stdin.\" }));\r\n process.exit(1);\r\n return;\r\n }\r\n parsedArgs = JSON.parse(inputData);\r\n const resultObject = await generateImageAndSave(parsedArgs);\r\n // The result from the plugin should be a string for Plugin.js to parse, so we stringify our result object.\r\n console.log(JSON.stringify({ status: \"success\", result: resultObject }));\r\n } catch (e) {\r\n // Handle Hyper-Stack-Trace for remote file fetching\r\n if (e.code === 'FILE_NOT_FOUND_LOCALLY') {\r\n const errorPayload = {\r\n status: \"error\",\r\n code: e.code,\r\n error: e.message,\r\n fileUrl: e.fileUrl\r\n };\r\n if (e.failedParameter) {\r\n errorPayload.failedParameter = e.failedParameter;\r\n }\r\n console.log(JSON.stringify(errorPayload));\r\n } else {\r\n let detailedError = e.message || \"Unknown error in DoubaoGen plugin\";\r\n if (e.response && e.response.data) {\r\n // If the API returned a specific error message, include it.\r\n detailedError += ` - API Response: ${JSON.stringify(e.response.data)}`;\r\n } else if (e.request) {\r\n detailedError += ` - No response received from API.`;\r\n }\r\n const finalErrorMessage = detailedError.startsWith(\"DoubaoGen Plugin Error:\") ? detailedError : `DoubaoGen Plugin Error: ${detailedError}`;\r\n console.log(JSON.stringify({ status: \"error\", error: finalErrorMessage }));\r\n }\r\n process.exit(1);\r\n }\r\n}\r\n\r\nmain();\r\n"}}}}