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.
- package/README.md +242 -62
- package/dist/index.js +68 -159
- package/dist/server/commands.d.ts +6 -0
- package/dist/server/commands.js +400 -71
- package/dist/server/config.d.ts +32 -23
- package/dist/server/config.js +183 -99
- package/dist/server/connect-response.d.ts +2 -0
- package/dist/server/connect-response.js +141 -0
- package/dist/server/generate-config.d.ts +3 -30
- package/dist/server/generate-config.js +164 -106
- package/dist/server/index.d.ts +2 -1
- package/dist/server/index.js +124 -149
- package/dist/server/logger.d.ts +8 -0
- package/dist/server/logger.js +36 -0
- package/dist/server/models/cache.d.ts +35 -0
- package/dist/server/models/cache.js +160 -0
- package/dist/server/models/fetcher.d.ts +18 -0
- package/dist/server/models/fetcher.js +150 -0
- package/dist/server/models/index.d.ts +6 -0
- package/dist/server/models/index.js +5 -0
- package/dist/server/models/manual.d.ts +15 -0
- package/dist/server/models/manual.js +92 -0
- package/dist/server/models/types.d.ts +55 -0
- package/dist/server/models/types.js +7 -0
- package/dist/server/models/worker.d.ts +21 -0
- package/dist/server/models/worker.js +97 -0
- package/dist/server/pollinations-api.d.ts +11 -0
- package/dist/server/pollinations-api.js +21 -8
- package/dist/server/proxy.js +223 -160
- package/dist/server/quota.d.ts +2 -0
- package/dist/server/quota.js +89 -86
- package/dist/server/scripts/pollinations_pricing.d.ts +8 -0
- package/dist/server/scripts/pollinations_pricing.js +246 -0
- package/dist/server/scripts/test_cost_endpoints.d.ts +1 -0
- package/dist/server/scripts/test_cost_endpoints.js +61 -0
- package/dist/server/scripts/test_dynamic_pricing.d.ts +1 -0
- package/dist/server/scripts/test_dynamic_pricing.js +39 -0
- package/dist/server/scripts/test_freetier_audit.d.ts +11 -0
- package/dist/server/scripts/test_freetier_audit.js +215 -0
- package/dist/server/scripts/test_parallel_cost.d.ts +1 -0
- package/dist/server/scripts/test_parallel_cost.js +104 -0
- package/dist/server/toast.d.ts +7 -1
- package/dist/server/toast.js +43 -10
- package/dist/tools/design/gen_diagram.d.ts +2 -0
- package/dist/tools/design/gen_diagram.js +94 -0
- package/dist/tools/design/gen_palette.d.ts +2 -0
- package/dist/tools/design/gen_palette.js +182 -0
- package/dist/tools/design/gen_qrcode.d.ts +2 -0
- package/dist/tools/design/gen_qrcode.js +50 -0
- package/dist/tools/ffmpeg.d.ts +24 -0
- package/dist/tools/ffmpeg.js +54 -0
- package/dist/tools/index.d.ts +24 -0
- package/dist/tools/index.js +83 -0
- package/dist/tools/pollinations/beta_discovery.d.ts +9 -0
- package/dist/tools/pollinations/beta_discovery.js +197 -0
- package/dist/tools/pollinations/cost-guard.d.ts +38 -0
- package/dist/tools/pollinations/cost-guard.js +141 -0
- package/dist/tools/pollinations/deepsearch.d.ts +7 -0
- package/dist/tools/pollinations/deepsearch.js +80 -0
- package/dist/tools/pollinations/gen_audio.d.ts +18 -0
- package/dist/tools/pollinations/gen_audio.js +246 -0
- package/dist/tools/pollinations/gen_image.d.ts +11 -0
- package/dist/tools/pollinations/gen_image.js +225 -0
- package/dist/tools/pollinations/gen_music.d.ts +14 -0
- package/dist/tools/pollinations/gen_music.js +180 -0
- package/dist/tools/pollinations/gen_video.d.ts +16 -0
- package/dist/tools/pollinations/gen_video.js +256 -0
- package/dist/tools/pollinations/polli_gen_confirm.d.ts +2 -0
- package/dist/tools/pollinations/polli_gen_confirm.js +48 -0
- package/dist/tools/pollinations/polli_status.d.ts +2 -0
- package/dist/tools/pollinations/polli_status.js +31 -0
- package/dist/tools/pollinations/polli_web_search.d.ts +15 -0
- package/dist/tools/pollinations/polli_web_search.js +164 -0
- package/dist/tools/pollinations/search_crawl_scrape.d.ts +7 -0
- package/dist/tools/pollinations/search_crawl_scrape.js +85 -0
- package/dist/tools/pollinations/shared.d.ts +165 -0
- package/dist/tools/pollinations/shared.js +665 -0
- package/dist/tools/pollinations/test_estimators.d.ts +1 -0
- package/dist/tools/pollinations/test_estimators.js +22 -0
- package/dist/tools/pollinations/transcribe_audio.d.ts +13 -0
- package/dist/tools/pollinations/transcribe_audio.js +194 -0
- package/dist/tools/power/extract_audio.d.ts +2 -0
- package/dist/tools/power/extract_audio.js +179 -0
- package/dist/tools/power/extract_frames.d.ts +2 -0
- package/dist/tools/power/extract_frames.js +237 -0
- package/dist/tools/power/file_to_url.d.ts +2 -0
- package/dist/tools/power/file_to_url.js +217 -0
- package/dist/tools/power/remove_background.d.ts +2 -0
- package/dist/tools/power/remove_background.js +366 -0
- package/dist/tools/power/rmbg_keys.d.ts +2 -0
- package/dist/tools/power/rmbg_keys.js +79 -0
- package/dist/tools/shared.d.ts +30 -0
- package/dist/tools/shared.js +80 -0
- package/package.json +10 -4
- package/dist/server/models-seed.d.ts +0 -18
- package/dist/server/models-seed.js +0 -55
package/dist/server/commands.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import * as https from 'https';
|
|
2
|
-
import { loadConfig, saveConfig } from './config.js';
|
|
3
|
-
import { getQuotaStatus } from './quota.js';
|
|
2
|
+
import { loadConfig, saveConfig, saveKeyToAuthJson } from './config.js';
|
|
3
|
+
import { getQuotaStatus, fetchUsageForPeriod } from './quota.js';
|
|
4
4
|
import { emitStatusToast } from './toast.js';
|
|
5
|
-
import { getDetailedUsage } from './pollinations-api.js';
|
|
6
5
|
import { generatePollinationsConfig } from './generate-config.js';
|
|
6
|
+
import { ModelRegistry } from './models/index.js';
|
|
7
7
|
function checkEndpoint(ep, key) {
|
|
8
8
|
return new Promise((resolve) => {
|
|
9
9
|
const req = https.request({
|
|
@@ -57,6 +57,7 @@ export async function checkKeyPermissions(key) {
|
|
|
57
57
|
}
|
|
58
58
|
// === CONSTANTS & PRICING ===
|
|
59
59
|
const TIER_LIMITS = {
|
|
60
|
+
microbe: { pollen: 0.1, emoji: '🦠' },
|
|
60
61
|
spore: { pollen: 1, emoji: '🦠' },
|
|
61
62
|
seed: { pollen: 3, emoji: '🌱' },
|
|
62
63
|
flower: { pollen: 10, emoji: '🌸' },
|
|
@@ -130,6 +131,10 @@ function calculateCurrentPeriodStats(usage, lastReset, tierLimit) {
|
|
|
130
131
|
};
|
|
131
132
|
}
|
|
132
133
|
// === COMMAND HANDLER ===
|
|
134
|
+
let globalClient = null;
|
|
135
|
+
export function setClientForCommands(client) {
|
|
136
|
+
globalClient = client;
|
|
137
|
+
}
|
|
133
138
|
export async function handleCommand(command) {
|
|
134
139
|
const parts = command.trim().split(/\s+/);
|
|
135
140
|
if (!parts[0].startsWith('/poll')) {
|
|
@@ -150,6 +155,19 @@ export async function handleCommand(command) {
|
|
|
150
155
|
return handleConfigCommand(args);
|
|
151
156
|
case 'help':
|
|
152
157
|
return handleHelpCommand();
|
|
158
|
+
case 'models':
|
|
159
|
+
return await handleModelsCommand(args);
|
|
160
|
+
case 'pricing':
|
|
161
|
+
return await handlePricingCommand();
|
|
162
|
+
case 'infos':
|
|
163
|
+
return await handleInfosCommand();
|
|
164
|
+
case 'addKey': // External trigger
|
|
165
|
+
// UI Pollution Fix: User hates appendPrompt.
|
|
166
|
+
// Just return a message telling them to use the tool.
|
|
167
|
+
return {
|
|
168
|
+
handled: true,
|
|
169
|
+
response: "💡 Pour ajouter une clé : Utilisez l'outil `rmbg_keys`\nExemple : `rmbg_keys action=add key=bkgc_...`"
|
|
170
|
+
};
|
|
153
171
|
default:
|
|
154
172
|
return {
|
|
155
173
|
handled: true,
|
|
@@ -202,7 +220,7 @@ async function handleModeCommand(args) {
|
|
|
202
220
|
return { handled: true, error: `❌ Erreur de vérification: ${e.message}` };
|
|
203
221
|
}
|
|
204
222
|
}
|
|
205
|
-
// Allow switch (if
|
|
223
|
+
// Allow switch (if alwaysfree or manual, or verified pro)
|
|
206
224
|
saveConfig({ mode: mode });
|
|
207
225
|
const config = loadConfig();
|
|
208
226
|
if (config.gui.status !== 'none') {
|
|
@@ -213,7 +231,7 @@ async function handleModeCommand(args) {
|
|
|
213
231
|
response: `✅ Mode changé: ${mode}`
|
|
214
232
|
};
|
|
215
233
|
}
|
|
216
|
-
async function handleUsageCommand(args) {
|
|
234
|
+
export async function handleUsageCommand(args) {
|
|
217
235
|
const isFull = args[0] === 'full';
|
|
218
236
|
try {
|
|
219
237
|
const quota = await getQuotaStatus(true);
|
|
@@ -233,10 +251,10 @@ async function handleUsageCommand(args) {
|
|
|
233
251
|
response += `\n> ⚠️ *Votre clé API ne permet pas l'accès aux détails d'usage (Restriction).*`;
|
|
234
252
|
}
|
|
235
253
|
else {
|
|
236
|
-
const
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
const stats = calculateCurrentPeriodStats(usageData
|
|
254
|
+
const lastReset = calculateResetDate(quota.nextResetAt);
|
|
255
|
+
const usageData = await fetchUsageForPeriod(config.apiKey, lastReset);
|
|
256
|
+
if (usageData && usageData.length > 0) {
|
|
257
|
+
const stats = calculateCurrentPeriodStats(usageData, lastReset, quota.tierLimit);
|
|
240
258
|
response += `\n### 📊 Détail Période (depuis ${lastReset.toLocaleTimeString()})\n`;
|
|
241
259
|
response += `**Total Requêtes**: ${stats.totalRequests} | **Tokens**: In ${formatTokens(stats.inputTokens)} / Out ${formatTokens(stats.outputTokens)}\n\n`;
|
|
242
260
|
response += `| Modèle | Reqs | Coût | Tokens |\n`;
|
|
@@ -264,36 +282,32 @@ async function handleUsageCommand(args) {
|
|
|
264
282
|
}
|
|
265
283
|
}
|
|
266
284
|
function handleFallbackCommand(args) {
|
|
267
|
-
const [
|
|
268
|
-
if (!
|
|
285
|
+
const [main, agent] = args;
|
|
286
|
+
if (!main) {
|
|
269
287
|
const config = loadConfig();
|
|
288
|
+
const freeConfig = `Free: main=${config.fallbacks.free.main}, agent=${config.fallbacks.free.agent}`;
|
|
289
|
+
const enterConfig = `Enter: agent=${config.fallbacks.enter.agent}`;
|
|
270
290
|
return {
|
|
271
291
|
handled: true,
|
|
272
|
-
response: `Fallbacks actuels:\n
|
|
292
|
+
response: `Fallbacks actuels:\n${freeConfig}\n${enterConfig}`
|
|
273
293
|
};
|
|
274
294
|
}
|
|
295
|
+
// Default behavior for "/poll fallback <model> <agent>" is setting FREE fallbacks
|
|
296
|
+
// User needs to use commands (maybe add /poll fallback enter ...) later
|
|
297
|
+
// For now, map to Free Fallback as it's the primary Safety Net
|
|
275
298
|
const config = loadConfig();
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
if (mode === 'pro' && model) {
|
|
286
|
-
saveConfig({
|
|
287
|
-
fallbacks: { ...config.fallbacks, pro: model }
|
|
288
|
-
});
|
|
289
|
-
return {
|
|
290
|
-
handled: true,
|
|
291
|
-
response: `✅ Fallback Pro configuré: ${model}`
|
|
292
|
-
};
|
|
293
|
-
}
|
|
299
|
+
saveConfig({
|
|
300
|
+
fallbacks: {
|
|
301
|
+
...config.fallbacks,
|
|
302
|
+
free: {
|
|
303
|
+
main: main,
|
|
304
|
+
agent: agent || config.fallbacks.free.agent
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
});
|
|
294
308
|
return {
|
|
295
309
|
handled: true,
|
|
296
|
-
|
|
310
|
+
response: `✅ Fallback (Free) configuré: main=${main}, agent=${agent || config.fallbacks.free.agent}`
|
|
297
311
|
};
|
|
298
312
|
}
|
|
299
313
|
async function handleConnectCommand(args) {
|
|
@@ -307,15 +321,16 @@ async function handleConnectCommand(args) {
|
|
|
307
321
|
// 1. Universal Validation (No Syntax Check) - Functional Check
|
|
308
322
|
emitStatusToast('info', 'Vérification de la clé...', 'Pollinations Config');
|
|
309
323
|
try {
|
|
310
|
-
const models = await generatePollinationsConfig(key);
|
|
311
|
-
// 2. Check if we got
|
|
312
|
-
const
|
|
313
|
-
if (
|
|
324
|
+
const models = await generatePollinationsConfig(key, true);
|
|
325
|
+
// 2. Check if we got Enterprise models
|
|
326
|
+
const enterpriseModels = models.filter(m => m.id.startsWith('enter/'));
|
|
327
|
+
if (enterpriseModels.length > 0) {
|
|
314
328
|
// SUCCESS
|
|
315
329
|
saveConfig({ apiKey: key }); // Don't force mode 'pro'. Let user decide.
|
|
330
|
+
saveKeyToAuthJson(key); // NATIVE SYNC: Hot-reload on OpenCode bypasses restart requirement !
|
|
316
331
|
const masked = key.substring(0, 6) + '...';
|
|
317
332
|
// Count Paid Only models found
|
|
318
|
-
const diamondCount =
|
|
333
|
+
const diamondCount = enterpriseModels.filter(m => m.name.includes('💎')).length;
|
|
319
334
|
// CHECK RESTRICTIONS: Strict Check (Usage + Profile + Balance)
|
|
320
335
|
let forcedModeMsg = "";
|
|
321
336
|
let isLimited = false;
|
|
@@ -340,10 +355,10 @@ async function handleConnectCommand(args) {
|
|
|
340
355
|
else {
|
|
341
356
|
saveConfig({ apiKey: key, keyHasAccessToProfile: true }); // Let user keep current mode or default
|
|
342
357
|
}
|
|
343
|
-
emitStatusToast('success', `Clé Valide! (${
|
|
358
|
+
emitStatusToast('success', `Clé Valide! (${enterpriseModels.length} modèles Pro débloqués)`, 'Pollinations Config');
|
|
344
359
|
return {
|
|
345
360
|
handled: true,
|
|
346
|
-
response: `✅ **Connexion Réussie
|
|
361
|
+
response: `✅ **Connexion Réussie! (Injection à chaud)**\n- Clé: \`${masked}\`\n- Modèles Pro Débloqués: ${enterpriseModels.length} (dont ${diamondCount} 💎 Paid)${forcedModeMsg}`
|
|
347
362
|
};
|
|
348
363
|
}
|
|
349
364
|
else {
|
|
@@ -355,8 +370,18 @@ async function handleConnectCommand(args) {
|
|
|
355
370
|
// Wait, generate-config falls back to providing a list containing "[Enter] GPT-4o (Fallback)" if fetch failed.
|
|
356
371
|
// So we need to detect if it's a "REAL" fetch or a "FALLBACK" fetch.
|
|
357
372
|
// The fallback models have `variants: {}` usually, but real ones might too.
|
|
358
|
-
//
|
|
359
|
-
|
|
373
|
+
// A better check: The fallback list is hardcoded in generate-config.ts catch block.
|
|
374
|
+
// Let's modify generate-config to return EMPTY list on error?
|
|
375
|
+
// Or just check if the returned models work?
|
|
376
|
+
// Simplest: If `generatePollinationsConfig` returns any model starting with `enter/` that includes "(Fallback)" in name, we assume failure?
|
|
377
|
+
// "GPT-4o (Fallback)" is the name.
|
|
378
|
+
const isFallback = models.some(m => m.name.includes('(Fallback)') && m.id.startsWith('enter/'));
|
|
379
|
+
if (isFallback) {
|
|
380
|
+
throw new Error("Clé rejetée par l'API (Accès refusé ou invalide).");
|
|
381
|
+
}
|
|
382
|
+
// If we are here, we got no enter models, or empty list?
|
|
383
|
+
// If key is valid but has no access?
|
|
384
|
+
throw new Error("Aucun modèle Enterprise détecté pour cette clé.");
|
|
360
385
|
}
|
|
361
386
|
}
|
|
362
387
|
catch (e) {
|
|
@@ -373,9 +398,29 @@ function handleConfigCommand(args) {
|
|
|
373
398
|
const [key, value] = args;
|
|
374
399
|
if (!key) {
|
|
375
400
|
const config = loadConfig();
|
|
401
|
+
const k = config.apiKey ? (config.apiKey.length > 8 ? `${config.apiKey.substring(0, 5)}****${config.apiKey.substring(config.apiKey.length - 4)}` : '****') : 'Non configurée';
|
|
402
|
+
const markdownResponse = `## ⚙️ Configuration Pollinations
|
|
403
|
+
Voici l'état actuel de votre configuration locale.
|
|
404
|
+
|
|
405
|
+
| Paramètre | Valeur Actuelle | Rôle | Commande |
|
|
406
|
+
|-----------|-----------------|------|----------|
|
|
407
|
+
| **apiKey** | \`${k}\` | Votre clé API secrète (BYOK) | \`/pollinations connect <key>\` |
|
|
408
|
+
| **mode** | \`${config.mode}\` | Mode d'accès | \`/pollinations mode <manual/pro/alwaysfree>\` |
|
|
409
|
+
| **enablePaidTools**| \`${config.enablePaidTools ?? true}\` | Sécurité: Désactiver outils payants | \`/poll config enablePaidTools <true/false>\` |
|
|
410
|
+
| **costConfirmationRequired**| \`${config.costConfirmationRequired ?? true}\` | Demande confirmation si le seuil d'alerte est dépassé | \`/poll config costConfirmation <true/false>\` |
|
|
411
|
+
| **costThreshold**| \`${config.costThreshold ?? 0.15} 🌻\` | Seuil d'alerte coût Outils | \`/poll config costThreshold <X>\` |
|
|
412
|
+
| **cost_estimator**| \`${config.costEstimator ?? true}\` | Afficher l'estimation de coût dans les Toasts | \`/poll config cost_estimator <true/false>\` |
|
|
413
|
+
| **fallbacks.free.main** | \`${config.fallbacks?.free?.main || 'free/mistral'}\` | Modèle de repli Chat vers l'univers free legacy | \`/pollinations fallback <main> <agent>\` |
|
|
414
|
+
| **fallbacks.free.agent** | \`${config.fallbacks?.free?.agent || 'free/openai-fast'}\`| Modèle de repli Agent vers l'univers free legacy | \`/pollinations fallback <main> <agent>\` |
|
|
415
|
+
| **fallbacks.enter.agent** | \`${config.fallbacks?.enter?.agent || 'free/openai-fast'}\`| Modèle Agent principal (free/* ou enter/*) | *Géré automatiquement* |
|
|
416
|
+
| **status_gui** | \`${config.gui?.status || 'all'}\` | Toasts de statut (all, alert, none) | \`/poll config status_gui <all/alert/none>\` |
|
|
417
|
+
| **logs_gui** | \`${config.gui?.logs || 'error'}\` | Niveau de log (verbose, error) | \`/poll config logs_gui <verbose/error/none>\` |
|
|
418
|
+
| **threshold_tier** | \`${config.thresholds?.tier || 80}%\` | Alerte limite Quotidienne Gratuite | \`/poll config threshold_tier <1-100>\` |
|
|
419
|
+
| **threshold_wallet** | \`${config.thresholds?.wallet || 80}%\` | Alerte baisse de Wallet Premium | \`/poll config threshold_wallet <1-100>\` |
|
|
420
|
+
| **status_bar** | \`${config.statusBar ?? true}\` | Affiche l'icône dans la barre de statut | \`/poll config status_bar <true/false>\` |`;
|
|
376
421
|
return {
|
|
377
422
|
handled: true,
|
|
378
|
-
response:
|
|
423
|
+
response: markdownResponse
|
|
379
424
|
};
|
|
380
425
|
}
|
|
381
426
|
if (key === 'toast_verbosity' && value) {
|
|
@@ -415,65 +460,349 @@ function handleConfigCommand(args) {
|
|
|
415
460
|
saveConfig({ thresholds: { ...config.thresholds, tier: threshold } });
|
|
416
461
|
return { handled: true, response: `✅ threshold_tier = ${threshold}%` };
|
|
417
462
|
}
|
|
418
|
-
if (key === '
|
|
463
|
+
if (key === 'threshold_wallet' && value) {
|
|
419
464
|
const threshold = parseInt(value);
|
|
420
465
|
if (isNaN(threshold) || threshold < 0 || threshold > 100) {
|
|
421
466
|
return { handled: true, error: 'Valeur entre 0 et 100 requise' };
|
|
422
467
|
}
|
|
423
468
|
const config = loadConfig();
|
|
424
|
-
saveConfig({ thresholds: { ...config.thresholds,
|
|
425
|
-
return { handled: true, response: `✅
|
|
426
|
-
}
|
|
427
|
-
if (key === 'threshold_wallet_stop' && value) {
|
|
428
|
-
const stopValue = parseFloat(value);
|
|
429
|
-
if (isNaN(stopValue) || stopValue < 0) {
|
|
430
|
-
return { handled: true, error: 'Valeur $ positive requise' };
|
|
431
|
-
}
|
|
432
|
-
const config = loadConfig();
|
|
433
|
-
saveConfig({ thresholds: { ...config.thresholds, wallet_stop: stopValue } });
|
|
434
|
-
return { handled: true, response: `✅ threshold_wallet_stop = $${stopValue}` };
|
|
469
|
+
saveConfig({ thresholds: { ...config.thresholds, wallet: threshold } });
|
|
470
|
+
return { handled: true, response: `✅ threshold_wallet = ${threshold}%` };
|
|
435
471
|
}
|
|
436
472
|
if (key === 'status_bar' && value) {
|
|
437
473
|
const enabled = value === 'true';
|
|
438
474
|
saveConfig({ statusBar: enabled });
|
|
439
475
|
return { handled: true, response: `✅ status_bar = ${enabled}` };
|
|
440
476
|
}
|
|
477
|
+
if (key === 'cost_estimator' && value) {
|
|
478
|
+
const enabled = value === 'true';
|
|
479
|
+
const config = loadConfig();
|
|
480
|
+
saveConfig({ ...config, costEstimator: enabled });
|
|
481
|
+
return { handled: true, response: `✅ cost_estimator = ${enabled}` };
|
|
482
|
+
}
|
|
483
|
+
if (key === 'enablePaidTools' && value) {
|
|
484
|
+
const enabled = value === 'true';
|
|
485
|
+
saveConfig({ enablePaidTools: enabled });
|
|
486
|
+
return { handled: true, response: `✅ enablePaidTools = ${enabled}${!enabled ? ' (wallet protection active)' : ''}` };
|
|
487
|
+
}
|
|
488
|
+
if (key === 'costThreshold' && value) {
|
|
489
|
+
const threshold = parseFloat(value);
|
|
490
|
+
if (isNaN(threshold) || threshold < 0) {
|
|
491
|
+
return { handled: true, error: 'Valeur numérique positive requise (en pollen). Ex: 0.15' };
|
|
492
|
+
}
|
|
493
|
+
saveConfig({ costThreshold: threshold });
|
|
494
|
+
return { handled: true, response: `✅ costThreshold = ${threshold} 🌻` };
|
|
495
|
+
}
|
|
496
|
+
if (key === 'costConfirmation' && value) {
|
|
497
|
+
const enabled = value === 'true';
|
|
498
|
+
saveConfig({ costConfirmationRequired: enabled });
|
|
499
|
+
return { handled: true, response: `✅ costConfirmationRequired = ${enabled}` };
|
|
500
|
+
}
|
|
441
501
|
return {
|
|
442
502
|
handled: true,
|
|
443
|
-
error: `Clé inconnue: ${key}. Clés: status_gui, logs_gui, threshold_tier,
|
|
503
|
+
error: `Clé inconnue: ${key}. Clés: status_gui, logs_gui, threshold_tier, threshold_wallet, status_bar, cost_estimator, enablePaidTools, costThreshold, costConfirmation`
|
|
444
504
|
};
|
|
445
505
|
}
|
|
446
506
|
function handleHelpCommand() {
|
|
447
507
|
const help = `
|
|
448
|
-
### 🌸 Pollinations Plugin - Commandes V6
|
|
508
|
+
### 🌸 Pollinations Plugin - Commandes V6
|
|
449
509
|
|
|
450
|
-
|
|
510
|
+
**Mode & Usage**
|
|
511
|
+
- **\`/pollinations mode [mode]\`**: Change le mode (manual, alwaysfree, pro).
|
|
451
512
|
- **\`/pollinations usage [full]\`**: Affiche le dashboard (full = détail).
|
|
452
|
-
- **\`/pollinations fallback
|
|
513
|
+
- **\`/pollinations fallback <main> [agent]\`**: Configure le Safety Net.
|
|
514
|
+
|
|
515
|
+
**Configuration**
|
|
453
516
|
- **\`/pollinations config [key] [value]\`**:
|
|
454
|
-
|
|
455
|
-
- \`
|
|
456
|
-
- \`
|
|
457
|
-
- \`
|
|
458
|
-
- \`
|
|
459
|
-
- \`
|
|
517
|
+
- \`status_gui\`: none, alert, all
|
|
518
|
+
- \`logs_gui\`: none, error, verbose
|
|
519
|
+
- \`threshold_tier\` / \`threshold_wallet\`: 0-100
|
|
520
|
+
- \`status_bar\`: true/false
|
|
521
|
+
- \`cost_estimator\`: true/false (show cost in outputs)
|
|
522
|
+
- \`enablePaidTools\`: true/false (wallet protection)
|
|
523
|
+
- \`costThreshold\`: seuil en pollen (défaut: 0.15)
|
|
524
|
+
- \`costConfirmation\`: true/false (confirmation coût)
|
|
525
|
+
|
|
526
|
+
**Modèles & Pricing**
|
|
527
|
+
- **\`/pollinations models [type]\`**: Liste des modèles (type: image, video, audio, text)
|
|
528
|
+
- **\`/pollinations pricing\`**: Tableau de pricing détaillé
|
|
529
|
+
- **\`/pollinations infos\`**: Explications sur les Tiers et le Pollen
|
|
530
|
+
|
|
531
|
+
> 💡 **RMBG keys**: Use the \`rmbg_keys\` tool (works with any model).
|
|
460
532
|
`.trim();
|
|
461
533
|
return { handled: true, response: help };
|
|
462
534
|
}
|
|
535
|
+
// === MODELS & PRICING COMMANDS ===
|
|
536
|
+
function parseNameDesc(m) {
|
|
537
|
+
const fullDesc = m.description || m.name;
|
|
538
|
+
const parts = fullDesc.split(" - ");
|
|
539
|
+
if (parts.length > 1) {
|
|
540
|
+
return { nom: parts[0].trim(), desc: parts.slice(1).join(" - ").trim() };
|
|
541
|
+
}
|
|
542
|
+
return { nom: fullDesc, desc: "" };
|
|
543
|
+
}
|
|
544
|
+
export async function handleModelsCommand(args) {
|
|
545
|
+
const filter = args[0]; // optional: image, video, audio, text
|
|
546
|
+
if (!ModelRegistry.isReady()) {
|
|
547
|
+
return {
|
|
548
|
+
handled: true,
|
|
549
|
+
response: '⏳ Le registre des modèles est en cours de chargement. Réessayez dans quelques secondes.'
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
const sections = [];
|
|
553
|
+
// --- FETCH FREE UNIVERSE GITHUB/LEGACY MODELS ---
|
|
554
|
+
if (!filter || filter === 'text') {
|
|
555
|
+
try {
|
|
556
|
+
const freeRes = await fetch('https://text.pollinations.ai/models', { signal: AbortSignal.timeout(4000) });
|
|
557
|
+
if (freeRes.ok) {
|
|
558
|
+
const freeData = await freeRes.json();
|
|
559
|
+
sections.push('## 🎁 Modèles Free Universe (Communautaire / Legacy API)\n');
|
|
560
|
+
sections.push(`> *Bonus gratuit disponible sans clé via l'API \`text.pollinations.ai\`. Dédié au Chat textuel.*\n`);
|
|
561
|
+
sections.push('| Nom | Alias / ID | Description | Vision | Tools |');
|
|
562
|
+
sections.push('|-----|------------|-------------|--------|-------|');
|
|
563
|
+
for (const m of freeData) {
|
|
564
|
+
const desc = m.description || m.name;
|
|
565
|
+
const aliases = m.aliases ? m.aliases.join(', ') : m.name;
|
|
566
|
+
sections.push(`| \`${m.name}\` | ${aliases} | ${desc.substring(0, 40)} | ${m.vision ? '👁️' : '❌'} | ${m.tools ? '🛠️' : '❌'} |`);
|
|
567
|
+
}
|
|
568
|
+
sections.push('');
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
catch (e) {
|
|
572
|
+
sections.push('## 🎁 Modèles Free Universe\n*(⚠️ L\'API communautaire text.pollinations.ai semble temporairement indisponible)*\n');
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
sections.push('## 📋 Modèles Pollinations Enter (Principaux)\n');
|
|
576
|
+
const categories = [
|
|
577
|
+
{ cat: 'image', emoji: '🎨', label: 'Image' },
|
|
578
|
+
{ cat: 'video', emoji: '🎬', label: 'Video' },
|
|
579
|
+
{ cat: 'audio', emoji: '🔊', label: 'Audio' },
|
|
580
|
+
{ cat: 'text', emoji: '📝', label: 'Text' },
|
|
581
|
+
];
|
|
582
|
+
for (const { cat, emoji, label } of categories) {
|
|
583
|
+
if (filter && filter !== cat)
|
|
584
|
+
continue;
|
|
585
|
+
const models = ModelRegistry.list(cat);
|
|
586
|
+
if (models.length === 0)
|
|
587
|
+
continue;
|
|
588
|
+
const sorted = [...models].sort((a, b) => a.name.localeCompare(b.name));
|
|
589
|
+
sections.push(`### ${emoji} ${label} (${models.length} modèles)\n`);
|
|
590
|
+
sections.push('| Nom | ID | Description | Capabilities | Input | Output |');
|
|
591
|
+
sections.push('|-----|----|-------------|--------------|-------|--------|');
|
|
592
|
+
for (const m of sorted) {
|
|
593
|
+
const { nom, desc } = parseNameDesc(m);
|
|
594
|
+
const badges = buildBadges(m);
|
|
595
|
+
const input = buildInputIcons(m);
|
|
596
|
+
const output = buildOutputCost(m);
|
|
597
|
+
sections.push(`| ${nom} | \`${m.name}\` | ${desc.substring(0, 40)} | ${badges} | ${input} | ${output} |`);
|
|
598
|
+
}
|
|
599
|
+
sections.push('');
|
|
600
|
+
}
|
|
601
|
+
sections.push('> **Capabilities** : 👁️ vision · 🧠 reasoning · 🎙️ audio in · 🔍 search · 🔊 audio out · 💻 code exec');
|
|
602
|
+
sections.push('> **Other** : 💎 PAID ONLY (Wallet direct) · 📏 Contexte API max');
|
|
603
|
+
return { handled: true, response: sections.join('\n') };
|
|
604
|
+
}
|
|
605
|
+
export async function handlePricingCommand() {
|
|
606
|
+
try {
|
|
607
|
+
const cp = require('child_process');
|
|
608
|
+
const path = require('path');
|
|
609
|
+
// Pointeur __dirname -> dist/server. Donc on cible scripts/pollinations_pricing.js
|
|
610
|
+
const scriptPath = path.join(__dirname, 'scripts', 'pollinations_pricing.js');
|
|
611
|
+
// Exécution locale via Node (sécurisé pour le bundle prod, pas de npx tsx)
|
|
612
|
+
const output = cp.execSync(`node "${scriptPath}"`, { encoding: 'utf-8', stdio: 'pipe' });
|
|
613
|
+
return { handled: true, response: output };
|
|
614
|
+
}
|
|
615
|
+
catch (e) {
|
|
616
|
+
return { handled: true, error: `Erreur lors de la récupération des prix: ${e.message}` };
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
// ─── Formatting Helpers for Models/Pricing ────────────────────────────────
|
|
620
|
+
function buildBadges(m) {
|
|
621
|
+
const f = [];
|
|
622
|
+
if (m.paid_only)
|
|
623
|
+
f.push('💎');
|
|
624
|
+
const allFlags = [...(m.input_modalities || []), ...(m.output_modalities || []), m.name];
|
|
625
|
+
if (m.supportsI2X)
|
|
626
|
+
allFlags.push("👁️");
|
|
627
|
+
const str = allFlags.join(" ").toLowerCase();
|
|
628
|
+
if (str.includes("image") || str.includes("👁️"))
|
|
629
|
+
f.push("👁️");
|
|
630
|
+
if (m.reasoning || str.includes("reasoning"))
|
|
631
|
+
f.push("🧠");
|
|
632
|
+
if (str.includes("audio") || str.includes("whisper") || str.includes("scribe") || str.includes("🎙️"))
|
|
633
|
+
f.push("🎙️");
|
|
634
|
+
if (str.includes("search") || str.includes("sonar") || str.includes("gemini"))
|
|
635
|
+
f.push("🔍");
|
|
636
|
+
if (m.output_modalities.includes("audio") || (m.voices && m.voices.length > 0) || str.includes("tts") || str.includes("music"))
|
|
637
|
+
f.push("🔊");
|
|
638
|
+
if (str.includes("coder") || str.includes("code") || str.includes("gemini"))
|
|
639
|
+
f.push("💻");
|
|
640
|
+
return f.filter((v, i, a) => a.indexOf(v) === i).join(" ");
|
|
641
|
+
}
|
|
642
|
+
function buildInputIcons(m) {
|
|
643
|
+
const icons = [];
|
|
644
|
+
if (m.input_modalities.includes('text'))
|
|
645
|
+
icons.push('📝');
|
|
646
|
+
if (m.input_modalities.includes('image'))
|
|
647
|
+
icons.push('🖼️');
|
|
648
|
+
if (m.input_modalities.includes('audio'))
|
|
649
|
+
icons.push('🎤');
|
|
650
|
+
return icons.join('') || '📝';
|
|
651
|
+
}
|
|
652
|
+
function buildOutputCost(m) {
|
|
653
|
+
const p = m.pricing;
|
|
654
|
+
if (p.completionImageTokens) {
|
|
655
|
+
return p.completionImageTokens < 0.0001
|
|
656
|
+
? `~tokens`
|
|
657
|
+
: `${p.completionImageTokens} 🌻/img`;
|
|
658
|
+
}
|
|
659
|
+
if (p.completionVideoSeconds)
|
|
660
|
+
return `${p.completionVideoSeconds} 🌻/s`;
|
|
661
|
+
if (p.completionVideoTokens)
|
|
662
|
+
return `~tokens/s`;
|
|
663
|
+
if (p.completionAudioTokens)
|
|
664
|
+
return `${p.completionAudioTokens} 🌻/tok`;
|
|
665
|
+
if (p.completionAudioSeconds)
|
|
666
|
+
return `${p.completionAudioSeconds} 🌻/s`;
|
|
667
|
+
if (p.promptAudioSeconds)
|
|
668
|
+
return `${p.promptAudioSeconds} 🌻/s`;
|
|
669
|
+
if (p.completionTextTokens)
|
|
670
|
+
return `${p.completionTextTokens} 🌻/tok`;
|
|
671
|
+
return '~tokens';
|
|
672
|
+
}
|
|
673
|
+
export async function handleInfosCommand() {
|
|
674
|
+
const config = loadConfig();
|
|
675
|
+
let name = "Developer";
|
|
676
|
+
let tier = "anonymous";
|
|
677
|
+
if (config.apiKey) {
|
|
678
|
+
try {
|
|
679
|
+
const res = await fetch('https://gen.pollinations.ai/account/profile', {
|
|
680
|
+
headers: { 'Authorization': `Bearer ${config.apiKey}` }
|
|
681
|
+
});
|
|
682
|
+
if (res.ok) {
|
|
683
|
+
const data = await res.json();
|
|
684
|
+
if (data.name)
|
|
685
|
+
name = data.name;
|
|
686
|
+
tier = data.tier || "anonymous";
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
catch (e) {
|
|
690
|
+
// Ignorer l'erreur réseau et garder les valeurs par défaut
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
const emojis = {
|
|
694
|
+
microbe: '🦠', spore: '🍄', seed: '🌱', flower: '🌸', nectar: '🍯', anonymous: '👤'
|
|
695
|
+
};
|
|
696
|
+
const tierEmoji = emojis[tier] || '❓';
|
|
697
|
+
const response = `## 🍯💚 POLLINATIONS OPENCODE PLUGIN 💚🍯
|
|
698
|
+
|
|
699
|
+
Bienvenue **${name}** sur le plugin Pollinations pour OpenCode !
|
|
700
|
+
|
|
701
|
+
Ce plugin vous permet de générer du code, des images, d'analyser des vidéos et interagir avec les meilleurs modèles d'Intelligence Artificielle de manière totalement transparente et intégrée à votre environnement de travail. Accédez aux capacités des LLMs de pointe, que ce soit via des requêtes de chat, la refonte de votre base de code, ou directement dans le terminal.
|
|
702
|
+
|
|
703
|
+
**Ce Que ce plugin vous apporte en plus ! :**
|
|
704
|
+
|
|
705
|
+
**🛠️ Outils Gratuits Intégrés (Toujours disponibles) :**
|
|
706
|
+
- \`gen_qrcode\` / \`gen_diagram\` / \`gen_palette\` : Outils visuels et dev.
|
|
707
|
+
- \`remove_background\` : Détourage d'image natif.
|
|
708
|
+
- \`extract_frames\` / \`extract_audio\` : Extraction rapide de contenu média.
|
|
709
|
+
- \`file_to_url\` : Hébergement instantané de vos fichiers locaux en ligne.
|
|
710
|
+
|
|
711
|
+
**💎 Outils Pollinations (Premium - Automatisés avec votre Clé) :**
|
|
712
|
+
- \`polli_gen_image\` : Génération d'images (Flux, Seedream, Gemini) + support Image-to-Image.
|
|
713
|
+
- \`polli_gen_video\` : Génération vidéo text-to-video / image-to-video (Veo, Wan, LTX...).
|
|
714
|
+
- \`polli_gen_audio\`/\`polli_stt\` : Transcription Whisper, Text-to-Speech ElevenLabs.
|
|
715
|
+
- \`polli_gen_music\` : Moteur de musique générative.
|
|
716
|
+
- \`polli_web_search\` : Recherche connectée pour étendre la base de contexte de l'agent.
|
|
717
|
+
|
|
718
|
+
- **Une configuration granulaire**, des modes de gestions de vos tokens et de vos outils, de la sécurisation des coûts des outils consommant du pollen...
|
|
719
|
+
|
|
720
|
+
---
|
|
721
|
+
|
|
722
|
+
> **Your tiers:** ${tierEmoji} ${tier.toUpperCase()}
|
|
723
|
+
|
|
724
|
+
---
|
|
725
|
+
|
|
726
|
+
## 🌍 Qu'est-ce que pollinations.ai ?
|
|
727
|
+
pollinations.ai est une plateforme d'IA open-source construite par et pour la communauté. Nous offrons une API unifiée pour les images, le texte, l'audio et la vidéo. Tout fonctionne de manière ouverte : notre code, notre feuille de route, nos conversations. Des centaines de développeurs construisent déjà des outils, des jeux, des bots et des expériences farfelues avec nous. Vous êtes les bienvenus !
|
|
728
|
+
|
|
729
|
+
Pas de boîtes noires. Pas de dépendance exclusive (vendor lock-in). Juste une API conviviale et un Discord rempli de personnes qui s'entraident réellement.
|
|
730
|
+
|
|
731
|
+
---
|
|
732
|
+
|
|
733
|
+
## 📈 Évoluez votre Palier (Tier)
|
|
734
|
+
Pour les développeurs qui créent avec pollinations.ai. Montez de niveau pour gagner plus de Pollen quotidien.
|
|
735
|
+
|
|
736
|
+
- 🦠 **Microbe** (0.1 pollen/jour) : Pour débloquer : S'inscrire
|
|
737
|
+
- 🍄 **Spore** (1 pollen/jour) : Pour débloquer : Vérification automatique (Vérifié à l'inscription)
|
|
738
|
+
- 🌱 **Seed** (3 pollen/jour) : Pour débloquer : 8+ points dev (Mise à niveau automatique hebdomadaire)
|
|
739
|
+
- 🌸 **Flower** (10 pollen/jour) : Pour débloquer : Publier une application (🌱 Doit être Seed en premier)
|
|
740
|
+
- 🍯 **Nectar** (20 pollen/jour) : Bientôt disponible 🔮
|
|
741
|
+
|
|
742
|
+
✨ *Nous sommes en bêta ! Nous apprenons ce qui fonctionne le mieux pour notre communauté et pourrons ajuster les valeurs de pollen et les règles des paliers. Merci de faire partie de cette aventure !*
|
|
743
|
+
|
|
744
|
+
---
|
|
745
|
+
|
|
746
|
+
## 💎 Qu'est-ce que le Pollen ?
|
|
747
|
+
Faire tourner des modèles d'IA coûte de l'argent. Le Pollen est notre moyen de faire fonctionner les serveurs sans publicité ni revente de vos données. Un crédit simple et unique pour tous les modèles — prévisible, transparent, sans surprises.
|
|
748
|
+
|
|
749
|
+
**$1 ≈ 1 Pollen** (les prix peuvent évoluer). Vous le dépensez pour faire des appels API.
|
|
750
|
+
|
|
751
|
+
## 🛒 Comment obtenir du Pollen ?
|
|
752
|
+
Il y a trois moyens d'ajouter du Pollen à votre solde :
|
|
753
|
+
|
|
754
|
+
1. **L'acheter** : Achetez des packs de Pollen directement par carte bancaire. Ce Pollen va dans votre portefeuille (wallet) et n'expire jamais. Des packs simples, pas d'abonnements, pas de paliers bloquants.
|
|
755
|
+
2. **Pollen Quotidien** : Pendant et après la bêta, les développeurs inscrits reçoivent des subventions quotidiennes de Pollen pour soutenir leurs expérimentations, en fonction de leur palier (microbe, spore, seed, flower, nectar).
|
|
756
|
+
3. **Le Gagner** : Complétez des récompenses communautaires ponctuelles (comme aider à résoudre un problème technique ou contribuer au projet). Chaque contribution vous fait gagner du Pollen. Nous le remarquons et nous partageons.
|
|
757
|
+
|
|
758
|
+
---
|
|
759
|
+
|
|
760
|
+
### 💡 Comment le Pollen est-il dépensé ?
|
|
761
|
+
1. **Les subventions quotidiennes (Tier grants)** sont utilisées en premier.
|
|
762
|
+
2. **Le Pollen acheté (Wallet)** est utilisé une fois le Pollen quotidien épuisé.
|
|
763
|
+
⚠️ **Exception** : 💎 Les modèles *Paid Only* (ex: claude-large, veo, seedream-pro) requièrent uniquement du Pollen acheté.`;
|
|
764
|
+
return { handled: true, response };
|
|
765
|
+
}
|
|
463
766
|
// === INTEGRATION OPENCODE ===
|
|
464
767
|
export function createCommandHooks() {
|
|
465
768
|
return {
|
|
466
769
|
'tui.command.execute': async (input, output) => {
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
770
|
+
if (!input.command.startsWith('/pollinations')) {
|
|
771
|
+
return;
|
|
772
|
+
}
|
|
773
|
+
try {
|
|
774
|
+
// Parse command
|
|
775
|
+
const rawArgs = input.command.replace('/pollinations', '').trim();
|
|
776
|
+
const result = await handleCommand(rawArgs);
|
|
777
|
+
if (result.handled) {
|
|
778
|
+
if (result.error) {
|
|
779
|
+
output.error = `❌ **Erreur:** ${result.error}`;
|
|
780
|
+
}
|
|
781
|
+
else if (result.response) {
|
|
782
|
+
output.response = result.response;
|
|
783
|
+
}
|
|
784
|
+
// If no response and no error, assume handled silently (like appendPrompt)
|
|
475
785
|
}
|
|
476
786
|
}
|
|
787
|
+
catch (err) {
|
|
788
|
+
output.error = `❌ **Erreur Critique:** ${err.message}`;
|
|
789
|
+
}
|
|
790
|
+
},
|
|
791
|
+
// Hook for UI Commands (Palette / Buttons)
|
|
792
|
+
'command.execute.before': async (input, output) => {
|
|
793
|
+
const cmd = input.command;
|
|
794
|
+
if (cmd === 'pollinations.addKey') {
|
|
795
|
+
handleCommand('addKey'); // Return help message
|
|
796
|
+
}
|
|
797
|
+
else if (cmd === 'pollinations.usage') {
|
|
798
|
+
const res = await handleCommand('usage');
|
|
799
|
+
if (res.response)
|
|
800
|
+
globalClient?.tui.showToast({ title: "Pollinations Usage", metadata: { type: 'info', message: "Voir logs pour usage détaillé" } });
|
|
801
|
+
}
|
|
802
|
+
else if (cmd === 'pollinations.mode') {
|
|
803
|
+
// UI Pollution Fix: SILENCE.
|
|
804
|
+
// User explicitly requested NO messages.
|
|
805
|
+
}
|
|
477
806
|
}
|
|
478
807
|
};
|
|
479
808
|
}
|