opencode-pollinations-plugin 6.1.0-beta.12 → 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 +11 -6
- package/dist/index.js +40 -10
- package/dist/server/commands.d.ts +4 -0
- package/dist/server/commands.js +296 -12
- package/dist/server/config.d.ts +5 -0
- package/dist/server/config.js +163 -35
- package/dist/server/connect-response.d.ts +2 -0
- package/dist/server/connect-response.js +141 -0
- package/dist/server/generate-config.js +10 -24
- 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.js +1 -8
- package/dist/server/proxy.js +52 -27
- package/dist/server/quota.d.ts +2 -8
- package/dist/server/quota.js +47 -89
- 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 +4 -1
- package/dist/server/toast.js +27 -10
- package/dist/tools/ffmpeg.d.ts +24 -0
- package/dist/tools/ffmpeg.js +54 -0
- package/dist/tools/index.d.ts +10 -8
- package/dist/tools/index.js +27 -25
- 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/gen_audio.d.ts +1 -1
- package/dist/tools/pollinations/gen_audio.js +65 -23
- package/dist/tools/pollinations/gen_image.d.ts +5 -7
- package/dist/tools/pollinations/gen_image.js +146 -160
- package/dist/tools/pollinations/gen_music.d.ts +1 -1
- package/dist/tools/pollinations/gen_music.js +57 -16
- package/dist/tools/pollinations/gen_video.d.ts +1 -1
- package/dist/tools/pollinations/gen_video.js +99 -65
- 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/shared.d.ts +34 -39
- package/dist/tools/pollinations/shared.js +300 -89
- 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 +5 -9
- package/dist/tools/pollinations/transcribe_audio.js +31 -72
- package/dist/tools/power/extract_audio.js +26 -27
- package/dist/tools/power/extract_frames.js +24 -27
- package/dist/tools/power/remove_background.js +2 -1
- package/dist/tools/power/rmbg_keys.js +2 -1
- package/dist/tools/shared.js +9 -3
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
Free AI models, multimodal generation, and smart quota management — directly in your editor.
|
|
8
8
|
<br><br>
|
|
9
9
|
|
|
10
|
-

|
|
10
|
+

|
|
11
11
|

|
|
12
12
|

|
|
13
13
|

|
|
@@ -23,12 +23,13 @@
|
|
|
23
23
|
|
|
24
24
|
---
|
|
25
25
|
|
|
26
|
-
## ✨ What's New in v6.1-beta
|
|
26
|
+
## ✨ What's New in v6.1-beta.22
|
|
27
27
|
|
|
28
28
|
The jump from v5.9 to v6.1 is not incremental. The plugin has grown from a smart proxy into a **full multimodal agent toolkit**:
|
|
29
29
|
|
|
30
30
|
- **15+ native tools** for generation, design, and media processing — usable directly in OpenCode's agent mode
|
|
31
|
-
- **
|
|
31
|
+
- **Smart Fetch Quota**: 100% exact math fetching directly from Pollinations API for quota verification (zero local cache)
|
|
32
|
+
- **Dynamic Pricing**: Tool costs are estimated live based on Tinybird API model stats. Token models have a built-in max theoretical threshold limit (Cost Guard).
|
|
32
33
|
- **Stealth notifications**: status toasts now only fire in relevant contexts, no more noise
|
|
33
34
|
- **Background removal** with multi-key rotation and automatic fallback to free provider
|
|
34
35
|
- **Video, music, audio generation** via the Pollinations API
|
|
@@ -49,7 +50,9 @@ Then in OpenCode:
|
|
|
49
50
|
```
|
|
50
51
|
Select **pollinations** → Enter your API key from [enter.pollinations.ai](https://enter.pollinations.ai), or leave blank to use the free tier.
|
|
51
52
|
|
|
52
|
-
Select any `pollinations/*` model and start chatting. **No key required
|
|
53
|
+
Select any `pollinations/*` text model and start chatting. **No key required for text.**
|
|
54
|
+
|
|
55
|
+
> ⚠️ **Note:** Image generation (`gen_image`) requires a Pollinations API Key. Text models remain free.
|
|
53
56
|
|
|
54
57
|
> ⚠️ After connecting a new key, restart OpenCode once for the model list to update.
|
|
55
58
|
|
|
@@ -98,13 +101,14 @@ These tools call the Pollinations APIs directly, enabling the AI to generate med
|
|
|
98
101
|
|
|
99
102
|
| Tool | Description |
|
|
100
103
|
|------|-------------|
|
|
101
|
-
| `gen_image` | Generate images from a text prompt (Flux, SDXL, etc.) |
|
|
104
|
+
| `gen_image` | Generate images from a text prompt (Flux, SDXL, etc.) **[Requires Key]** |
|
|
102
105
|
| `gen_audio` | Generate speech or sound effects |
|
|
103
106
|
| `gen_music` | Generate music from a description |
|
|
104
107
|
| `gen_video` | Generate short video clips |
|
|
105
108
|
| `transcribe_audio` | Transcribe an audio file to text |
|
|
106
109
|
| `deepsearch` | Multi-step AI-powered research |
|
|
107
110
|
| `search_crawl_scrape` | Web search with full page scraping |
|
|
111
|
+
| `beta_discovery` | Probe undocumented model parameters (400 validation extraction) |
|
|
108
112
|
|
|
109
113
|
### 🖌️ Design Tools
|
|
110
114
|
|
|
@@ -236,7 +240,8 @@ Config is read from (highest priority first):
|
|
|
236
240
|
### ✅ Shipped (v5.x → v6.1-beta)
|
|
237
241
|
- Free + Enterprise proxy with transparent fallback (Safety Net)
|
|
238
242
|
- Dynamic port allocation — cross-platform (no more `fuser`, no port conflicts)
|
|
239
|
-
- Pollen/tier quota tracking with local
|
|
243
|
+
- Pollen/tier quota tracking with Smart Fetch API (no local logging)
|
|
244
|
+
- Dynamic Pricing (Tinybird stats) and Cost Guard max thresholds
|
|
240
245
|
- Agent tools: image, audio, music, video generation
|
|
241
246
|
- Agent tools: web search, scraping, deep research
|
|
242
247
|
- Design tools: diagrams, palettes, QR codes
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import * as http from 'http';
|
|
2
|
-
import * as fs from 'fs';
|
|
3
2
|
import { generatePollinationsConfig } from './server/generate-config.js';
|
|
4
|
-
import { loadConfig } from './server/config.js';
|
|
3
|
+
import { loadConfig, migrateLegacyConfig } from './server/config.js';
|
|
5
4
|
import { handleChatCompletion } from './server/proxy.js';
|
|
6
5
|
import { createToastHooks, createToolHooks, setGlobalClient } from './server/toast.js';
|
|
7
6
|
import { createStatusHooks } from './server/status.js';
|
|
@@ -9,14 +8,9 @@ import { createCommandHooks, setClientForCommands } from './server/commands.js';
|
|
|
9
8
|
import { createToolRegistry } from './tools/index.js';
|
|
10
9
|
import { createRequire } from 'module';
|
|
11
10
|
const require = createRequire(import.meta.url);
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
fs.appendFileSync(LOG_FILE, `[${new Date().toISOString()}] ${msg}\n`);
|
|
16
|
-
}
|
|
17
|
-
catch (e) { }
|
|
18
|
-
}
|
|
19
|
-
// Port killing removed: Using dynamic ports.
|
|
11
|
+
import { log } from './server/logger.js';
|
|
12
|
+
import { ModelRegistry } from './server/models/index.js';
|
|
13
|
+
const sessionModels = new Map();
|
|
20
14
|
const startProxy = () => {
|
|
21
15
|
return new Promise((resolve) => {
|
|
22
16
|
const server = http.createServer(async (req, res) => {
|
|
@@ -80,10 +74,22 @@ const startProxy = () => {
|
|
|
80
74
|
export const PollinationsPlugin = async (ctx) => {
|
|
81
75
|
const v = require('../package.json').version;
|
|
82
76
|
log(`Plugin Initializing v${v}...`);
|
|
77
|
+
log(`[ENV] Keys: ${Object.keys(process.env).filter(k => k.includes('OPENCODE') || k.includes('APP') || k.includes('DATA')).join(', ')}`);
|
|
83
78
|
console.log(`🚀 POLLINATIONS PLUGIN v${v} LOADED 🚀`);
|
|
79
|
+
// MIGRATE CONFIG
|
|
80
|
+
migrateLegacyConfig();
|
|
84
81
|
// START PROXY
|
|
85
82
|
const port = await startProxy();
|
|
86
83
|
const localBaseUrl = `http://127.0.0.1:${port}/v1`;
|
|
84
|
+
// INIT MODEL REGISTRY (non-blocking, fire-and-forget)
|
|
85
|
+
ModelRegistry.refresh().then(() => {
|
|
86
|
+
const stats = ModelRegistry.stats();
|
|
87
|
+
log(`[ModelRegistry] Ready: ${stats.image} image, ${stats.video} video, ${stats.audio} audio, ${stats.text} text`);
|
|
88
|
+
// Démarrage du patcher asynchrone des descriptions des Outils (Phase 1.5)
|
|
89
|
+
import('./server/models/worker.js').then(module => {
|
|
90
|
+
module.ToolRegistryWorker.start();
|
|
91
|
+
}).catch(e => log(`[ToolWorker] Failed to load worker: ${e}`));
|
|
92
|
+
}).catch(e => log(`[ModelRegistry] Init failed (will use fallback): ${e}`));
|
|
87
93
|
setGlobalClient(ctx.client);
|
|
88
94
|
setClientForCommands(ctx.client);
|
|
89
95
|
const toastHooks = createToastHooks(ctx.client);
|
|
@@ -92,6 +98,24 @@ export const PollinationsPlugin = async (ctx) => {
|
|
|
92
98
|
const toolRegistry = createToolRegistry();
|
|
93
99
|
log(`[Tools] ${Object.keys(toolRegistry).length} tools registered`);
|
|
94
100
|
return {
|
|
101
|
+
"chat.message": async (input) => {
|
|
102
|
+
const m = input.model;
|
|
103
|
+
if (m) {
|
|
104
|
+
if (m.modelID && !m.modelID.includes('pollimock-handler')) {
|
|
105
|
+
sessionModels.set(input.sessionID, `${m.providerID}/${m.modelID}`);
|
|
106
|
+
log(`[Hook] Saved active model ${m.providerID}/${m.modelID} for session ${input.sessionID}`);
|
|
107
|
+
}
|
|
108
|
+
else if (m.modelID && m.modelID.includes('pollimock-handler')) {
|
|
109
|
+
const prev = sessionModels.get(input.sessionID);
|
|
110
|
+
if (prev) {
|
|
111
|
+
log(`[Hook] Virtual model triggered. Reverting to ${prev} in 500ms...`);
|
|
112
|
+
setTimeout(() => {
|
|
113
|
+
ctx.client.tui.executeCommand({ body: { command: `/model ${prev}` } }).catch(console.error);
|
|
114
|
+
}, 500);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
},
|
|
95
119
|
tool: toolRegistry,
|
|
96
120
|
async config(config) {
|
|
97
121
|
log("[Hook] config() called");
|
|
@@ -106,6 +130,12 @@ export const PollinationsPlugin = async (ctx) => {
|
|
|
106
130
|
config.provider = {};
|
|
107
131
|
// Dynamic Provider Name
|
|
108
132
|
const version = require('../package.json').version;
|
|
133
|
+
// Inject Virtual Handler Model
|
|
134
|
+
modelsObj['pollimock-handler'] = {
|
|
135
|
+
id: 'pollimock-handler',
|
|
136
|
+
name: 'Command Handler (Virtual)',
|
|
137
|
+
options: { hidden: true } // Try to hide from UI if OpenCode supports it
|
|
138
|
+
};
|
|
109
139
|
config.provider['pollinations'] = {
|
|
110
140
|
id: 'pollinations',
|
|
111
141
|
name: `Pollinations AI (v${version})`,
|
|
@@ -11,6 +11,10 @@ interface CommandResult {
|
|
|
11
11
|
}
|
|
12
12
|
export declare function setClientForCommands(client: any): void;
|
|
13
13
|
export declare function handleCommand(command: string): Promise<CommandResult>;
|
|
14
|
+
export declare function handleUsageCommand(args: string[]): Promise<CommandResult>;
|
|
15
|
+
export declare function handleModelsCommand(args: string[]): Promise<CommandResult>;
|
|
16
|
+
export declare function handlePricingCommand(): Promise<CommandResult>;
|
|
17
|
+
export declare function handleInfosCommand(): Promise<CommandResult>;
|
|
14
18
|
export declare function createCommandHooks(): {
|
|
15
19
|
'tui.command.execute': (input: any, output: any) => Promise<void>;
|
|
16
20
|
'command.execute.before': (input: any, output: any) => Promise<void>;
|
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({
|
|
@@ -155,6 +155,12 @@ export async function handleCommand(command) {
|
|
|
155
155
|
return handleConfigCommand(args);
|
|
156
156
|
case 'help':
|
|
157
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();
|
|
158
164
|
case 'addKey': // External trigger
|
|
159
165
|
// UI Pollution Fix: User hates appendPrompt.
|
|
160
166
|
// Just return a message telling them to use the tool.
|
|
@@ -225,7 +231,7 @@ async function handleModeCommand(args) {
|
|
|
225
231
|
response: `✅ Mode changé: ${mode}`
|
|
226
232
|
};
|
|
227
233
|
}
|
|
228
|
-
async function handleUsageCommand(args) {
|
|
234
|
+
export async function handleUsageCommand(args) {
|
|
229
235
|
const isFull = args[0] === 'full';
|
|
230
236
|
try {
|
|
231
237
|
const quota = await getQuotaStatus(true);
|
|
@@ -245,10 +251,10 @@ async function handleUsageCommand(args) {
|
|
|
245
251
|
response += `\n> ⚠️ *Votre clé API ne permet pas l'accès aux détails d'usage (Restriction).*`;
|
|
246
252
|
}
|
|
247
253
|
else {
|
|
248
|
-
const
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
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);
|
|
252
258
|
response += `\n### 📊 Détail Période (depuis ${lastReset.toLocaleTimeString()})\n`;
|
|
253
259
|
response += `**Total Requêtes**: ${stats.totalRequests} | **Tokens**: In ${formatTokens(stats.inputTokens)} / Out ${formatTokens(stats.outputTokens)}\n\n`;
|
|
254
260
|
response += `| Modèle | Reqs | Coût | Tokens |\n`;
|
|
@@ -321,6 +327,7 @@ async function handleConnectCommand(args) {
|
|
|
321
327
|
if (enterpriseModels.length > 0) {
|
|
322
328
|
// SUCCESS
|
|
323
329
|
saveConfig({ apiKey: key }); // Don't force mode 'pro'. Let user decide.
|
|
330
|
+
saveKeyToAuthJson(key); // NATIVE SYNC: Hot-reload on OpenCode bypasses restart requirement !
|
|
324
331
|
const masked = key.substring(0, 6) + '...';
|
|
325
332
|
// Count Paid Only models found
|
|
326
333
|
const diamondCount = enterpriseModels.filter(m => m.name.includes('💎')).length;
|
|
@@ -351,7 +358,7 @@ async function handleConnectCommand(args) {
|
|
|
351
358
|
emitStatusToast('success', `Clé Valide! (${enterpriseModels.length} modèles Pro débloqués)`, 'Pollinations Config');
|
|
352
359
|
return {
|
|
353
360
|
handled: true,
|
|
354
|
-
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}`
|
|
355
362
|
};
|
|
356
363
|
}
|
|
357
364
|
else {
|
|
@@ -391,9 +398,29 @@ function handleConfigCommand(args) {
|
|
|
391
398
|
const [key, value] = args;
|
|
392
399
|
if (!key) {
|
|
393
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>\` |`;
|
|
394
421
|
return {
|
|
395
422
|
handled: true,
|
|
396
|
-
response:
|
|
423
|
+
response: markdownResponse
|
|
397
424
|
};
|
|
398
425
|
}
|
|
399
426
|
if (key === 'toast_verbosity' && value) {
|
|
@@ -453,9 +480,27 @@ function handleConfigCommand(args) {
|
|
|
453
480
|
saveConfig({ ...config, costEstimator: enabled });
|
|
454
481
|
return { handled: true, response: `✅ cost_estimator = ${enabled}` };
|
|
455
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
|
+
}
|
|
456
501
|
return {
|
|
457
502
|
handled: true,
|
|
458
|
-
error: `Clé inconnue: ${key}. Clés: status_gui, logs_gui, threshold_tier, threshold_wallet, status_bar, cost_estimator`
|
|
503
|
+
error: `Clé inconnue: ${key}. Clés: status_gui, logs_gui, threshold_tier, threshold_wallet, status_bar, cost_estimator, enablePaidTools, costThreshold, costConfirmation`
|
|
459
504
|
};
|
|
460
505
|
}
|
|
461
506
|
function handleHelpCommand() {
|
|
@@ -469,16 +514,255 @@ function handleHelpCommand() {
|
|
|
469
514
|
|
|
470
515
|
**Configuration**
|
|
471
516
|
- **\`/pollinations config [key] [value]\`**:
|
|
472
|
-
|
|
517
|
+
- \`status_gui\`: none, alert, all
|
|
473
518
|
- \`logs_gui\`: none, error, verbose
|
|
474
519
|
- \`threshold_tier\` / \`threshold_wallet\`: 0-100
|
|
475
520
|
- \`status_bar\`: true/false
|
|
476
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
|
|
477
530
|
|
|
478
531
|
> 💡 **RMBG keys**: Use the \`rmbg_keys\` tool (works with any model).
|
|
479
532
|
`.trim();
|
|
480
533
|
return { handled: true, response: help };
|
|
481
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
|
+
}
|
|
482
766
|
// === INTEGRATION OPENCODE ===
|
|
483
767
|
export function createCommandHooks() {
|
|
484
768
|
return {
|
package/dist/server/config.d.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
export declare function getConfigDir(): string;
|
|
2
|
+
export declare const CONFIG_DIR: string;
|
|
3
|
+
export declare const CONFIG_FILE: string;
|
|
1
4
|
export interface PollinationsConfigV5 {
|
|
2
5
|
version: string | number;
|
|
3
6
|
mode: 'manual' | 'alwaysfree' | 'pro';
|
|
@@ -55,3 +58,5 @@ export declare function saveConfig(updates: Partial<PollinationsConfigV5>): {
|
|
|
55
58
|
statusBar: boolean;
|
|
56
59
|
costEstimator: boolean;
|
|
57
60
|
};
|
|
61
|
+
export declare function saveKeyToAuthJson(key: string): boolean;
|
|
62
|
+
export declare function migrateLegacyConfig(): void;
|