opencode-pollinations-plugin 6.1.0-beta.2 → 6.1.0-beta.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/README.md +242 -62
  2. package/dist/index.js +68 -159
  3. package/dist/server/commands.d.ts +6 -0
  4. package/dist/server/commands.js +400 -71
  5. package/dist/server/config.d.ts +32 -23
  6. package/dist/server/config.js +183 -99
  7. package/dist/server/connect-response.d.ts +2 -0
  8. package/dist/server/connect-response.js +141 -0
  9. package/dist/server/generate-config.d.ts +3 -30
  10. package/dist/server/generate-config.js +164 -106
  11. package/dist/server/index.d.ts +2 -1
  12. package/dist/server/index.js +124 -149
  13. package/dist/server/logger.d.ts +8 -0
  14. package/dist/server/logger.js +36 -0
  15. package/dist/server/models/cache.d.ts +35 -0
  16. package/dist/server/models/cache.js +160 -0
  17. package/dist/server/models/fetcher.d.ts +18 -0
  18. package/dist/server/models/fetcher.js +150 -0
  19. package/dist/server/models/index.d.ts +6 -0
  20. package/dist/server/models/index.js +5 -0
  21. package/dist/server/models/manual.d.ts +15 -0
  22. package/dist/server/models/manual.js +92 -0
  23. package/dist/server/models/types.d.ts +55 -0
  24. package/dist/server/models/types.js +7 -0
  25. package/dist/server/models/worker.d.ts +21 -0
  26. package/dist/server/models/worker.js +97 -0
  27. package/dist/server/pollinations-api.d.ts +11 -0
  28. package/dist/server/pollinations-api.js +21 -8
  29. package/dist/server/proxy.js +223 -160
  30. package/dist/server/quota.d.ts +2 -0
  31. package/dist/server/quota.js +89 -86
  32. package/dist/server/scripts/pollinations_pricing.d.ts +8 -0
  33. package/dist/server/scripts/pollinations_pricing.js +246 -0
  34. package/dist/server/scripts/test_cost_endpoints.d.ts +1 -0
  35. package/dist/server/scripts/test_cost_endpoints.js +61 -0
  36. package/dist/server/scripts/test_dynamic_pricing.d.ts +1 -0
  37. package/dist/server/scripts/test_dynamic_pricing.js +39 -0
  38. package/dist/server/scripts/test_freetier_audit.d.ts +11 -0
  39. package/dist/server/scripts/test_freetier_audit.js +215 -0
  40. package/dist/server/scripts/test_parallel_cost.d.ts +1 -0
  41. package/dist/server/scripts/test_parallel_cost.js +104 -0
  42. package/dist/server/toast.d.ts +7 -1
  43. package/dist/server/toast.js +43 -10
  44. package/dist/tools/design/gen_diagram.d.ts +2 -0
  45. package/dist/tools/design/gen_diagram.js +94 -0
  46. package/dist/tools/design/gen_palette.d.ts +2 -0
  47. package/dist/tools/design/gen_palette.js +182 -0
  48. package/dist/tools/design/gen_qrcode.d.ts +2 -0
  49. package/dist/tools/design/gen_qrcode.js +50 -0
  50. package/dist/tools/ffmpeg.d.ts +24 -0
  51. package/dist/tools/ffmpeg.js +54 -0
  52. package/dist/tools/index.d.ts +24 -0
  53. package/dist/tools/index.js +83 -0
  54. package/dist/tools/pollinations/beta_discovery.d.ts +9 -0
  55. package/dist/tools/pollinations/beta_discovery.js +197 -0
  56. package/dist/tools/pollinations/cost-guard.d.ts +38 -0
  57. package/dist/tools/pollinations/cost-guard.js +141 -0
  58. package/dist/tools/pollinations/deepsearch.d.ts +7 -0
  59. package/dist/tools/pollinations/deepsearch.js +80 -0
  60. package/dist/tools/pollinations/gen_audio.d.ts +18 -0
  61. package/dist/tools/pollinations/gen_audio.js +246 -0
  62. package/dist/tools/pollinations/gen_image.d.ts +11 -0
  63. package/dist/tools/pollinations/gen_image.js +225 -0
  64. package/dist/tools/pollinations/gen_music.d.ts +14 -0
  65. package/dist/tools/pollinations/gen_music.js +180 -0
  66. package/dist/tools/pollinations/gen_video.d.ts +16 -0
  67. package/dist/tools/pollinations/gen_video.js +256 -0
  68. package/dist/tools/pollinations/polli_gen_confirm.d.ts +2 -0
  69. package/dist/tools/pollinations/polli_gen_confirm.js +48 -0
  70. package/dist/tools/pollinations/polli_status.d.ts +2 -0
  71. package/dist/tools/pollinations/polli_status.js +31 -0
  72. package/dist/tools/pollinations/polli_web_search.d.ts +15 -0
  73. package/dist/tools/pollinations/polli_web_search.js +164 -0
  74. package/dist/tools/pollinations/search_crawl_scrape.d.ts +7 -0
  75. package/dist/tools/pollinations/search_crawl_scrape.js +85 -0
  76. package/dist/tools/pollinations/shared.d.ts +165 -0
  77. package/dist/tools/pollinations/shared.js +665 -0
  78. package/dist/tools/pollinations/test_estimators.d.ts +1 -0
  79. package/dist/tools/pollinations/test_estimators.js +22 -0
  80. package/dist/tools/pollinations/transcribe_audio.d.ts +13 -0
  81. package/dist/tools/pollinations/transcribe_audio.js +194 -0
  82. package/dist/tools/power/extract_audio.d.ts +2 -0
  83. package/dist/tools/power/extract_audio.js +179 -0
  84. package/dist/tools/power/extract_frames.d.ts +2 -0
  85. package/dist/tools/power/extract_frames.js +237 -0
  86. package/dist/tools/power/file_to_url.d.ts +2 -0
  87. package/dist/tools/power/file_to_url.js +217 -0
  88. package/dist/tools/power/remove_background.d.ts +2 -0
  89. package/dist/tools/power/remove_background.js +366 -0
  90. package/dist/tools/power/rmbg_keys.d.ts +2 -0
  91. package/dist/tools/power/rmbg_keys.js +79 -0
  92. package/dist/tools/shared.d.ts +30 -0
  93. package/dist/tools/shared.js +80 -0
  94. package/package.json +10 -4
  95. package/dist/server/models-seed.d.ts +0 -18
  96. package/dist/server/models-seed.js +0 -55
@@ -0,0 +1,225 @@
1
+ /**
2
+ * gen_image Tool - Pollinations Image Generation
3
+ *
4
+ * Updated: 2026-02-19 - Dynamic ModelRegistry + Cost Guard + Toasts
5
+ *
6
+ * All models are dynamic from the Pollinations API.
7
+ * Unknown models are accepted as (beta) and passed through to the API.
8
+ * Cost Guard reads enablePaidTools, costConfirmationRequired, costThreshold.
9
+ */
10
+ import { tool } from '@opencode-ai/plugin/tool';
11
+ import * as fs from 'fs';
12
+ import * as path from 'path';
13
+ import { getApiKey, hasApiKey, httpsGet, ensureDir, generateFilename, getDefaultOutputDir, formatCost, formatFileSize, estimateImageCost, extractCostFromHeaders, isCostEstimatorEnabled, supportsI2I, getPaidImageModels, fetchEnterBalance, } from './shared.js';
14
+ import { loadConfig } from '../../server/config.js';
15
+ import { checkCostControl, isTokenBased } from './cost-guard.js';
16
+ import { emitStatusToast } from '../../server/toast.js';
17
+ // ─── Constants ─────────────────────────────────────────────────────────────
18
+ const DEFAULT_MODEL = 'flux';
19
+ // ─── Tool Definition ──────────────────────────────────────────────────────
20
+ export const polliGenImageTool = tool({
21
+ description: `Generate an image from a text prompt using Pollinations AI.
22
+
23
+ 💡 **Modèles Image Dynamiques** :
24
+ L'API Pollinations (Enter) possède une quantité importante de modèles (Flux, Midjourney, Seedream, etc.) et ils changent fréquemment. Le catalogue à jour est listé ci-dessous.
25
+
26
+ **Exemples d'utilisation Optionnelle** :
27
+ - **I2I (Image-to-Image)** : Utilisez le paramètre \`reference_image\` avec une URL ou chemin local si le modèle le supporte.
28
+ - L'outil embarque un "costGuard" automatique gérant la confirmation des coûts.`,
29
+ args: {
30
+ prompt: tool.schema.string().describe('Description of the image to generate'),
31
+ model: tool.schema.string().optional().describe('Model to use (default: flux). Unknown models accepted as (beta).'),
32
+ width: tool.schema.number().min(256).max(4096).optional().describe('Image width (default: 1024)'),
33
+ height: tool.schema.number().min(256).max(4096).optional().describe('Image height (default: 1024)'),
34
+ reference_image: tool.schema.string().optional().describe('URL(s) for image-to-image editing (comma-separated for multi-image models)'),
35
+ seed: tool.schema.number().optional().describe('Seed for reproducibility (-1 for random)'),
36
+ quality: tool.schema.enum(['low', 'med', 'high']).optional().describe('Quality for gptimage models only'),
37
+ transparent: tool.schema.boolean().optional().describe('Transparent background for gptimage models only'),
38
+ save_to: tool.schema.string().optional().describe('Custom output directory'),
39
+ filename: tool.schema.string().optional().describe('Custom filename (without extension)'),
40
+ },
41
+ async execute(args, context) {
42
+ const apiKey = getApiKey();
43
+ const hasKey = hasApiKey();
44
+ // Determine model based on key presence
45
+ let model = args.model || DEFAULT_MODEL;
46
+ const width = args.width || 1024;
47
+ const height = args.height || 1024;
48
+ // Fetch known models from registry
49
+ const imageModels = getPaidImageModels();
50
+ const knownModel = !!imageModels[model];
51
+ const isBetaModel = !knownModel;
52
+ // Force Auth Check for ALL Image Generations
53
+ if (!hasKey) {
54
+ return `❌ **Clé API Requise** pour la génération d'images.
55
+ 💡 Utilisez \`/pollinations connect <clé>\` pour activer le service.
56
+ 💎 Modèles disponibles: ${Object.keys(imageModels).slice(0, 5).join(', ')}...`;
57
+ }
58
+ // Unknown model → beta passthrough (don't reject)
59
+ if (isBetaModel) {
60
+ emitStatusToast('warning', `Modèle "${model}" non référencé — mode (beta)`, '🎨 gen_image');
61
+ }
62
+ // Validate I2I support (for known models only; beta models get default behavior)
63
+ if (args.reference_image && knownModel && !supportsI2I(model)) {
64
+ return `⚠️ Le modèle "${model}" ne supporte pas l'Image-to-Image.
65
+ 💡 Modèles I2I supportés: ${Object.entries(imageModels)
66
+ .filter(([, info]) => info.i2i)
67
+ .map(([name]) => name)
68
+ .join(', ')}`;
69
+ }
70
+ // Estimate cost
71
+ const estimatedCost = estimateImageCost(model);
72
+ // Cost Guard check V2
73
+ const costCheck = checkCostControl('polli_gen_image', args, model, estimatedCost, 'image');
74
+ if (!costCheck.allowed) {
75
+ return costCheck.message || '❌ Opération bloquée par le Cost Guard.';
76
+ }
77
+ // Emit start toast
78
+ const config = loadConfig();
79
+ const argsStr = config.gui?.logs === 'verbose' ? `\nParameters: ${JSON.stringify(args)}` : '';
80
+ emitStatusToast('info', `Génération image: ${model} (${width}×${height})${argsStr}`, '🎨 polli_gen_image');
81
+ // Set metadata
82
+ context.metadata({ title: `🎨 Image: ${model}${isBetaModel ? ' (beta)' : ''}` });
83
+ try {
84
+ let imageData;
85
+ let responseHeaders = {};
86
+ let usedModel = model;
87
+ // === ENTER endpoint ONLY (gen.pollinations.ai) ===
88
+ const params = new URLSearchParams({
89
+ nologo: 'true',
90
+ private: 'true',
91
+ width: String(width),
92
+ height: String(height),
93
+ });
94
+ // Model parameter
95
+ params.set('model', model);
96
+ // Seed
97
+ if (args.seed !== undefined) {
98
+ params.set('seed', String(args.seed));
99
+ }
100
+ // I2I: reference image(s)
101
+ if (args.reference_image) {
102
+ // Check if it's a local file path
103
+ let imageUrl = args.reference_image;
104
+ if (!args.reference_image.startsWith('http')) {
105
+ // For local files, we'd need to upload first
106
+ // For now, require URL
107
+ return `❌ Les fichiers locaux nécessitent d'être uploadés d'abord.
108
+ 💡 Utilisez l'outil \`file_to_url\` pour obtenir une URL publique.`;
109
+ }
110
+ params.set('image', imageUrl);
111
+ }
112
+ // Quality (gptimage only)
113
+ if (args.quality && model.startsWith('gptimage')) {
114
+ params.set('quality', args.quality);
115
+ }
116
+ // Transparent (gptimage only)
117
+ if (args.transparent !== undefined && model.startsWith('gptimage')) {
118
+ params.set('transparent', String(args.transparent));
119
+ }
120
+ const promptEncoded = encodeURIComponent(args.prompt);
121
+ const url = `https://gen.pollinations.ai/image/${promptEncoded}?${params}`;
122
+ const headers = {};
123
+ if (apiKey)
124
+ headers['Authorization'] = `Bearer ${apiKey}`;
125
+ // 1. Fetch balance avant génération
126
+ const balBefore = await fetchEnterBalance();
127
+ const result = await httpsGet(url, headers);
128
+ imageData = result.data;
129
+ responseHeaders = result.headers;
130
+ // Update used model from response if available
131
+ if (responseHeaders['x-model-used']) {
132
+ usedModel = responseHeaders['x-model-used'];
133
+ }
134
+ // Save the image
135
+ let outputDir = getDefaultOutputDir('images');
136
+ let filename = args.filename;
137
+ if (args.save_to) {
138
+ if (args.save_to.match(/\.(png|jpe?g|webp|gif)$/i)) {
139
+ outputDir = path.dirname(args.save_to);
140
+ filename = path.basename(args.save_to);
141
+ }
142
+ else {
143
+ outputDir = args.save_to;
144
+ }
145
+ }
146
+ ensureDir(outputDir);
147
+ filename = filename || generateFilename('image', usedModel, 'png');
148
+ const filePath = path.join(outputDir, filename.includes('.') ? filename : `${filename}.png`);
149
+ fs.writeFileSync(filePath, imageData);
150
+ // 2. Fetch balance après génération (delay for API sync)
151
+ let balAfter = null;
152
+ let realCost;
153
+ if (balBefore !== null) {
154
+ await new Promise(r => setTimeout(r, 1000)); // Laisse le temps au ledger
155
+ balAfter = await fetchEnterBalance();
156
+ if (balAfter !== null) {
157
+ realCost = Math.round((balBefore - balAfter) * 10000) / 10000;
158
+ }
159
+ }
160
+ // Extract cost from headers as fallback/info
161
+ const costTracking = extractCostFromHeaders(responseHeaders);
162
+ // Build result
163
+ const fileSize = fs.statSync(filePath).size;
164
+ const lines = [];
165
+ // Inject costWarning at top if present
166
+ if (costCheck.message && !costCheck.allowed) { // Assuming costWarning should come from costCheck if not allowed
167
+ lines.push(costCheck.message);
168
+ lines.push('');
169
+ }
170
+ lines.push(`🎨 Image Générée`);
171
+ lines.push(`━━━━━━━━━━━━━━━━━━`);
172
+ lines.push(`Prompt: ${args.prompt.substring(0, 100)}${args.prompt.length > 100 ? '...' : ''}`);
173
+ lines.push(`Modèle: ${usedModel}${isBetaModel ? ' (beta)' : ''}`);
174
+ lines.push(`Résolution: ${width}×${height}`);
175
+ // Add I2I info if used
176
+ if (args.reference_image) {
177
+ lines.push(`I2I Source: ${args.reference_image.substring(0, 50)}...`);
178
+ }
179
+ lines.push(`Fichier: ${filePath}`);
180
+ lines.push(`Taille: ${formatFileSize(fileSize)}`);
181
+ // Pricing details (Estimé vs Réel)
182
+ if (isCostEstimatorEnabled()) {
183
+ const maxCost = estimatedCost * 3;
184
+ lines.push(`\n💰 **Rapport Financier :**`);
185
+ if (isTokenBased('image', usedModel)) {
186
+ lines.push(`- Coût Estimé : ${formatCost(estimatedCost)} (Max théorique: ${formatCost(maxCost)})`);
187
+ }
188
+ else {
189
+ lines.push(`- Coût Estimé : ${formatCost(estimatedCost)}`);
190
+ }
191
+ if (realCost !== undefined) {
192
+ lines.push(`- Coût Réel : **${formatCost(realCost)}** (via Solde Wallet)`);
193
+ }
194
+ else if (costTracking.costUsd !== undefined) {
195
+ lines.push(`- Coût Réel : **${formatCost(costTracking.costUsd)}** (via Headers API)`);
196
+ }
197
+ else {
198
+ lines.push(`- Coût Réel : Inconnu (API injoignable)`);
199
+ }
200
+ }
201
+ if (responseHeaders['x-request-id']) {
202
+ lines.push(`Request ID: ${responseHeaders['x-request-id']}`);
203
+ }
204
+ // Emit success toast
205
+ emitStatusToast('success', `Image générée ✓ (${usedModel})`, '🎨 gen_image', { filePath: filePath });
206
+ return lines.join('\n');
207
+ }
208
+ catch (err) {
209
+ emitStatusToast('error', `Erreur: ${err.message?.substring(0, 60)}`, '🎨 gen_image');
210
+ if (err.message?.includes('402') || err.message?.includes('Payment')) {
211
+ return `❌ Crédits pollen insuffisants pour le modèle "${model}".
212
+ 💡 Vérifiez votre solde avec /pollinations usage`;
213
+ }
214
+ if (err.message?.includes('401') || err.message?.includes('403')) {
215
+ return `❌ Clé API invalide ou non autorisée.
216
+ 🔧 Vérifiez votre clé avec /pollinations connect`;
217
+ }
218
+ if (err.message?.includes('400')) {
219
+ return `❌ Paramètres invalides: ${err.message}
220
+ 💡 Vérifiez que le modèle supporte les paramètres fournis.`;
221
+ }
222
+ return `❌ Erreur génération image: ${err.message}`;
223
+ }
224
+ },
225
+ });
@@ -0,0 +1,14 @@
1
+ /**
2
+ * gen_music Tool - Pollinations Music Generation
3
+ *
4
+ * Updated: 2026-02-12 - Verified API Reference
5
+ *
6
+ * Model: elevenmusic (ElevenLabs Music)
7
+ * Endpoint: gen.pollinations.ai/audio/{text}
8
+ *
9
+ * Parameters:
10
+ * - duration: 3-300 seconds
11
+ * - instrumental: boolean (vocals or instrumental only)
12
+ */
13
+ import { type ToolDefinition } from '@opencode-ai/plugin/tool';
14
+ export declare const polliGenMusicTool: ToolDefinition;
@@ -0,0 +1,180 @@
1
+ /**
2
+ * gen_music Tool - Pollinations Music Generation
3
+ *
4
+ * Updated: 2026-02-12 - Verified API Reference
5
+ *
6
+ * Model: elevenmusic (ElevenLabs Music)
7
+ * Endpoint: gen.pollinations.ai/audio/{text}
8
+ *
9
+ * Parameters:
10
+ * - duration: 3-300 seconds
11
+ * - instrumental: boolean (vocals or instrumental only)
12
+ */
13
+ import { tool } from '@opencode-ai/plugin/tool';
14
+ import * as fs from 'fs';
15
+ import * as path from 'path';
16
+ import { getApiKey, httpsGet, ensureDir, generateFilename, getDefaultOutputDir, formatCost, formatFileSize, estimateMusicCost, extractCostFromHeaders, isCostEstimatorEnabled, } from './shared.js';
17
+ import { loadConfig } from '../../server/config.js';
18
+ import { checkCostControl, isTokenBased } from './cost-guard.js';
19
+ import { emitStatusToast } from '../../server/toast.js';
20
+ // ─── Constants ─────────────────────────────────────────────────────────────
21
+ const MIN_DURATION = 3;
22
+ const MAX_DURATION = 300; // 5 minutes
23
+ const DEFAULT_DURATION = 10;
24
+ const MODEL_NAME = 'elevenmusic';
25
+ // ─── Tool Definition ──────────────────────────────────────────────────────
26
+ export const polliGenMusicTool = tool({
27
+ description: `Generate music from a text description using Pollinations AI.
28
+
29
+ **🎵 Model:** elevenmusic (ElevenLabs Music)
30
+
31
+ **📝 Parameters:**
32
+ - \`duration\`: 3-300 seconds (default: 10s)
33
+ - \`instrumental\`: true = no vocals, false = vocals allowed
34
+
35
+ **💡 Example Prompts:**
36
+ - "upbeat jazz with saxophone solo"
37
+ - "ambient electronic for meditation"
38
+ - "epic orchestral film score with dramatic strings"
39
+ - "lo-fi hip hop beats with piano"
40
+ - "acoustic guitar ballad with soft vocals"
41
+ - "electronic dance music with heavy bass drop"
42
+
43
+ **💰 Cost:** ~0.005 🌻 per second
44
+ - 10 seconds ≈ 0.05 🌻
45
+ - 30 seconds ≈ 0.15 🌻
46
+ - 60 seconds ≈ 0.30 🌻
47
+
48
+ **⚠️ Notes:**
49
+ - Generation time scales with duration (~1s per second of audio)
50
+ - Longer tracks (60s+) may take 1-2 minutes
51
+ - Instrumental mode produces cleaner results for background music`,
52
+ args: {
53
+ prompt: tool.schema.string().describe('Description of the music to generate'),
54
+ duration: tool.schema.number().min(MIN_DURATION).max(MAX_DURATION).optional()
55
+ .describe(`Duration in seconds (default: ${DEFAULT_DURATION}, max: ${MAX_DURATION})`),
56
+ instrumental: tool.schema.boolean().optional().describe('Instrumental only - no vocals (default: false)'),
57
+ seed: tool.schema.number().optional().describe('Seed for reproducibility (-1 for random)'),
58
+ save_to: tool.schema.string().optional().describe('Custom output directory'),
59
+ filename: tool.schema.string().optional().describe('Custom filename (without extension)'),
60
+ },
61
+ async execute(args, context) {
62
+ const apiKey = getApiKey();
63
+ if (!apiKey) {
64
+ return `❌ La génération musicale nécessite une clé API Pollinations.
65
+ 🔧 Connectez votre clé avec /pollinations connect`;
66
+ }
67
+ const duration = Math.min(args.duration || DEFAULT_DURATION, MAX_DURATION);
68
+ const instrumental = args.instrumental || false;
69
+ // Estimate cost
70
+ const estimatedCost = estimateMusicCost(duration);
71
+ // Cost Guard check V2
72
+ const costCheck = checkCostControl('polli_gen_music', args, MODEL_NAME, estimatedCost, 'audio');
73
+ if (!costCheck.allowed) {
74
+ return costCheck.message || '❌ Opération bloquée par le Cost Guard.';
75
+ }
76
+ // Estimate generation time
77
+ const genTimeSeconds = Math.ceil(duration * 1.2);
78
+ // Emit start toast
79
+ const config = loadConfig();
80
+ const argsStr = config.gui?.logs === 'verbose' ? `\nParameters: ${JSON.stringify(args)}` : '';
81
+ emitStatusToast('info', `Génération musique: ${duration}s (~${genTimeSeconds}s gen)${argsStr}`, '🎵 polli_gen_music');
82
+ // Metadata
83
+ context.metadata({ title: `🎵 Music: ${duration}s (~${genTimeSeconds}s gen time)` });
84
+ try {
85
+ // Build URL
86
+ const params = new URLSearchParams({
87
+ model: MODEL_NAME,
88
+ nologo: 'true',
89
+ private: 'true',
90
+ duration: String(duration),
91
+ });
92
+ if (instrumental) {
93
+ params.set('instrumental', 'true');
94
+ }
95
+ // Seed for reproducibility
96
+ if (args.seed !== undefined) {
97
+ params.set('seed', String(args.seed));
98
+ }
99
+ const promptEncoded = encodeURIComponent(args.prompt);
100
+ const url = `https://gen.pollinations.ai/audio/${promptEncoded}?${params}`;
101
+ const headers = {
102
+ 'Authorization': `Bearer ${apiKey}`,
103
+ };
104
+ // Music generation takes time
105
+ const result = await httpsGet(url, headers);
106
+ const audioData = result.data;
107
+ const responseHeaders = result.headers;
108
+ // Save audio
109
+ let outputDir = getDefaultOutputDir('music');
110
+ let filename = args.filename;
111
+ if (args.save_to) {
112
+ if (args.save_to.match(/\.(mp3|wav|ogg|m4a)$/i)) {
113
+ outputDir = path.dirname(args.save_to);
114
+ filename = path.basename(args.save_to);
115
+ }
116
+ else {
117
+ outputDir = args.save_to;
118
+ }
119
+ }
120
+ ensureDir(outputDir);
121
+ filename = filename || generateFilename('music', MODEL_NAME, 'mp3');
122
+ const filePath = path.join(outputDir, filename.endsWith('.mp3') ? filename : `${filename}.mp3`);
123
+ fs.writeFileSync(filePath, audioData);
124
+ const fileSize = fs.statSync(filePath).size;
125
+ let actualCost = estimatedCost;
126
+ if (responseHeaders) {
127
+ const costTracking = extractCostFromHeaders(responseHeaders);
128
+ if (costTracking.costUsd !== undefined)
129
+ actualCost = costTracking.costUsd;
130
+ }
131
+ // Build result
132
+ const lines = [];
133
+ // Inject costWarning at top if present
134
+ if (costCheck.message && !costCheck.allowed) {
135
+ lines.push(costCheck.message);
136
+ lines.push('');
137
+ }
138
+ lines.push(`🎵 Musique Générée`);
139
+ lines.push(`━━━━━━━━━━━━━━━━━━`);
140
+ lines.push(`Prompt: ${args.prompt}`);
141
+ lines.push(`Durée: ~${duration}s`);
142
+ lines.push(`Mode: ${instrumental ? 'Instrumental (sans voix)' : 'Avec voix possible'}`);
143
+ lines.push(`Fichier: ${filePath}`);
144
+ lines.push(`Taille: ${formatFileSize(fileSize)}`);
145
+ // Cost info
146
+ if (isCostEstimatorEnabled()) {
147
+ if (isTokenBased('audio', MODEL_NAME)) {
148
+ const maxCost = estimatedCost * 3;
149
+ lines.push(`Coût: ${formatCost(actualCost)} (Max théorique: ${formatCost(maxCost)})`);
150
+ }
151
+ else {
152
+ lines.push(`Coût: ${formatCost(actualCost)}`);
153
+ }
154
+ }
155
+ if (responseHeaders['x-model-used']) {
156
+ lines.push(`Modèle utilisé: ${responseHeaders['x-model-used']}`);
157
+ }
158
+ if (responseHeaders['x-request-id']) {
159
+ lines.push(`Request ID: ${responseHeaders['x-request-id']}`);
160
+ }
161
+ // Emit success toast
162
+ emitStatusToast('success', `Musique générée ✓ (${duration}s)`, '🎵 gen_music');
163
+ return lines.join('\n');
164
+ }
165
+ catch (err) {
166
+ emitStatusToast('error', `Erreur: ${err.message?.substring(0, 60)}`, '🎵 gen_music');
167
+ if (err.message?.includes('402') || err.message?.includes('Payment')) {
168
+ return `❌ Crédits pollen insuffisants.`;
169
+ }
170
+ if (err.message?.includes('401') || err.message?.includes('403')) {
171
+ return `❌ Clé API invalide ou non autorisée.`;
172
+ }
173
+ if (err.message?.includes('Timeout')) {
174
+ return `❌ Timeout - La génération musicale a pris trop de temps.
175
+ 💡 Essayez une durée plus courte.`;
176
+ }
177
+ return `❌ Erreur génération musicale: ${err.message}`;
178
+ }
179
+ },
180
+ });
@@ -0,0 +1,16 @@
1
+ /**
2
+ * gen_video Tool - Pollinations Video Generation
3
+ *
4
+ * Updated: 2026-02-12 - Verified API Reference
5
+ *
6
+ * Video models with different capabilities:
7
+ * - T2V (Text-to-Video): grok-video, ltx-2, veo, seedance, seedance-pro
8
+ * - I2V (Image-to-Video): wan (I2V ONLY!), veo, seedance, seedance-pro
9
+ * - Veo Interpolation: Uses image=url1,url2 for transitions
10
+ *
11
+ * Response headers for cost tracking:
12
+ * - x-usage-completion-video-seconds (grok, ltx-2, veo, wan)
13
+ * - x-usage-completion-video-tokens (seedance, seedance-pro)
14
+ */
15
+ import { type ToolDefinition } from '@opencode-ai/plugin/tool';
16
+ export declare const polliGenVideoTool: ToolDefinition;