Files
wehub-resource-sync 0d3cb498a3
Deploy local.promptfoo.app / Deploy to Cloudflare Pages (push) Waiting to run
Test and Publish Multi-arch Docker Image / test (push) Waiting to run
Test and Publish Multi-arch Docker Image / build-docker-and-push-digests (map[digest-suffix:linux-amd64 platform:linux/amd64 runner:ubuntu-latest]) (push) Blocked by required conditions
Test and Publish Multi-arch Docker Image / build-docker-and-push-digests (map[digest-suffix:linux-arm64 platform:linux/arm64 runner:ubuntu-24.04-arm]) (push) Blocked by required conditions
Test and Publish Multi-arch Docker Image / merge-docker-digests (push) Blocked by required conditions
Test and Publish Multi-arch Docker Image / Attest Multi-arch Image (push) Blocked by required conditions
Validate Renovate Config / Validate Renovate Configuration (push) Waiting to run
CI / Shell Format Check (push) Has been cancelled
CI / Check Ruby (3.4) (push) Has been cancelled
CI / CI Config (push) Has been cancelled
CI / Test on Node ${{ matrix.node }} and ${{ matrix.os }}${{ matrix.shard && format(' (shard {0}/3)', matrix.shard) || '' }} (push) Has been cancelled
CI / Build on Node ${{ matrix.node }} (push) Has been cancelled
CI / Style Check (push) Has been cancelled
CI / Generate Assets (push) Has been cancelled
CI / Check Python (3.14) (push) Has been cancelled
CI / Check Python (3.9) (push) Has been cancelled
CI / Build Docs (push) Has been cancelled
CI / Code Scan Action (push) Has been cancelled
CI / Site tests (push) Has been cancelled
CI / webui tests (push) Has been cancelled
CI / Run Integration Tests (push) Has been cancelled
CI / Run Smoke Tests (push) Has been cancelled
CI / Go Tests (push) Has been cancelled
CI / Share Test (push) Has been cancelled
CI / Redteam (Production API) (push) Has been cancelled
CI / Redteam (Staging API) (push) Has been cancelled
CI / GitHub Actions Lint (push) Has been cancelled
CI / Check Ruby (3.0) (push) Has been cancelled
release-please / release-please (push) Has been cancelled
release-please / build (push) Has been cancelled
release-please / publish-npm (push) Has been cancelled
release-please / publish-npm-backfill (push) Has been cancelled
release-please / docker (push) Has been cancelled
release-please / publish-code-scan-action (push) Has been cancelled
release-please / attest-code-scan-action (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:24:08 +08:00

165 lines
5.9 KiB
JavaScript
Executable File

#!/usr/bin/env node
const fs = require('fs/promises');
const path = require('path');
const https = require('https');
// Load environment variables from .env file
require('dotenv').config({ path: path.join(__dirname, '..', '.env'), quiet: true });
// Get OpenAI API key from environment
const OPENAI_API_KEY = process.env.OPENAI_API_KEY;
if (!OPENAI_API_KEY) {
console.error('Please set the OPENAI_API_KEY environment variable');
process.exit(1);
}
// Parse command line arguments
const args = process.argv.slice(2);
let filename = 'blog-image.png';
let prompt = '';
let size = '1024x1024';
let outputDir = 'blog';
// Parse arguments
for (let i = 0; i < args.length; i++) {
if (args[i] === '--filename' || args[i] === '-f') {
filename = args[i + 1];
i++;
} else if (args[i] === '--prompt' || args[i] === '-p') {
prompt = args[i + 1];
i++;
} else if (args[i] === '--size' || args[i] === '-s') {
size = args[i + 1];
i++;
} else if (args[i] === '--output-dir' || args[i] === '-o') {
outputDir = args[i + 1];
i++;
} else if (args[i] === '--help' || args[i] === '-h') {
console.log(`
Usage: node generate-blog-image.js [options]
Options:
-f, --filename <name> Output filename (default: blog-image.png)
-p, --prompt <text> Image generation prompt
-s, --size <size> Image size: 1024x1024, 1536x1024, 1024x1536, auto (default: 1024x1024)
-o, --output-dir <dir> Output subdirectory under site/static/img/ (default: blog)
-h, --help Show this help message
Examples:
node generate-blog-image.js -f system-cards-hero.png -p "A red panda reading documentation"
node generate-blog-image.js --filename hero.jpg --prompt "Conference banner" --size 1536x1024 --output-dir events
`);
process.exit(0);
}
}
// Example prompt for system cards blog post:
/*
const systemCardsPrompt = `Create a professional hero image for a blog post about LLM System Cards and AI Safety Documentation.
The image should feature:
- A cute red panda character (the Promptfoo mascot) in a cybersecurity/tech setting
- The red panda should be examining or holding system documentation, security reports, or technical papers
- Include visual elements that suggest security, transparency, and documentation (like shields, locks, checklists, or documents)
- Modern tech aesthetic with a color palette that includes: deep purples, teals, and orange accents (matching Promptfoo's brand)
- The style should be professional but approachable, similar to tech blog illustrations
- Background could include subtle circuit patterns, document icons, or security symbols
- The red panda should look intelligent and focused, perhaps wearing glasses or holding a magnifying glass
- Overall mood: trustworthy, technical, but friendly
Style: Modern tech illustration, clean lines, professional but approachable, suitable for a security-focused blog post`;
*/
if (!prompt) {
console.error('Please provide a prompt using --prompt or -p');
console.log('Use --help for usage information');
process.exit(1);
}
async function generateImage() {
const data = JSON.stringify({
model: 'gpt-image-1',
prompt: prompt,
size: size,
quality: 'high',
n: 1,
});
const options = {
hostname: 'api.openai.com',
port: 443,
path: '/v1/images/generations',
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${OPENAI_API_KEY}`,
'Content-Length': data.length,
},
};
return new Promise((resolve, reject) => {
const req = https.request(options, (res) => {
let responseBody = '';
res.on('data', (chunk) => {
responseBody += chunk;
});
res.on('end', () => {
try {
const response = JSON.parse(responseBody);
if (response.error) {
reject(new Error(response.error.message));
} else {
resolve(response.data[0]);
}
} catch (error) {
reject(error);
}
});
});
req.on('error', (error) => {
reject(error);
});
req.write(data);
req.end();
});
}
async function downloadImage(url, filepath) {
const buffer = await new Promise((resolve, reject) => {
https
.get(url, (response) => {
if (response.statusCode && response.statusCode >= 400) {
response.resume();
reject(new Error(`Failed to download image: ${response.statusCode}`));
return;
}
const chunks = [];
response.on('data', (chunk) => {
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
});
response.on('end', () => {
resolve(Buffer.concat(chunks));
});
response.on('error', reject);
})
.on('error', reject);
});
try {
await fs.writeFile(filepath, buffer);
} catch (error) {
await fs.unlink(filepath).catch(() => {}); // Delete the file on error
throw error;
}
}
async function main() {
try {
console.log('Generating image with gpt-image-1...');
const imageData = await generateImage();
if (imageData.b64_json) {
// If we get base64 data, save it directly
const outputPath = path.join(__dirname, '..', 'site', 'static', 'img', outputDir, filename);
const buffer = Buffer.from(imageData.b64_json, 'base64');
await fs.writeFile(outputPath, buffer);
console.log(`Image saved to: ${outputPath}`);
} else if (imageData.url) {
// If we get a URL, download the image
const outputPath = path.join(__dirname, '..', 'site', 'static', 'img', outputDir, filename);
await downloadImage(imageData.url, outputPath);
console.log(`Image downloaded and saved to: ${outputPath}`);
} else {
console.error('Unexpected response format:', imageData);
}
} catch (error) {
console.error('Error generating image:', error);
process.exit(1);
}
}
// biome-ignore lint/nursery/noFloatingPromises: convert this to ESM
main();