opencode-pollinations-plugin 5.4.16 → 5.5.0

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 CHANGED
@@ -1,4 +1,4 @@
1
- # 🌸 Pollinations AI Plugin for OpenCode (v5.4.8)
1
+ # 🌸 Pollinations AI Plugin for OpenCode (v5.4.16)
2
2
 
3
3
  <div align="center">
4
4
  <img src="https://avatars.githubusercontent.com/u/88394740?s=400&v=4" alt="Pollinations.ai Logo" width="200">
@@ -10,7 +10,7 @@
10
10
 
11
11
  <div align="center">
12
12
 
13
- ![Version](https://img.shields.io/badge/version-5.4.8-blue.svg)
13
+ ![Version](https://img.shields.io/badge/version-5.4.16-blue.svg)
14
14
  ![License](https://img.shields.io/badge/license-MIT-green.svg)
15
15
  ![Status](https://img.shields.io/badge/status-Stable-success.svg)
16
16
 
@@ -46,14 +46,14 @@ Pollinations.ai is an open-source platform built by and for the community. We pr
46
46
  </p>
47
47
 
48
48
  <p align="center">
49
- <img src="docs/images/free_add.png" alt="Free Chat Example" width="800">
49
+ <img src="https://github.com/fkom13/opencode-pollinations-plugin/raw/main/docs/images/free_add.png" alt="Free Chat Example" width="800">
50
50
  <br>
51
51
  <em>Free Universe Chat (Supported by Pollinations Ads)</em>
52
52
  </p>
53
53
 
54
54
  <p align="center">
55
- <img src="docs/images/plan_1.png" alt="Plan Build Step 1" width="400">
56
- <img src="docs/images/plan_2.png" alt="Plan Build Step 2" width="400">
55
+ <img src="https://github.com/fkom13/opencode-pollinations-plugin/raw/main/docs/images/plan_1.png" alt="Plan Build Step 1" width="400">
56
+ <img src="https://github.com/fkom13/opencode-pollinations-plugin/raw/main/docs/images/plan_2.png" alt="Plan Build Step 2" width="400">
57
57
  <br>
58
58
  <em>Integrated Plan Building Workflow</em>
59
59
  </p>
@@ -160,3 +160,9 @@ Just type in the chat. You are in **Manual Mode** by default.
160
160
  ## 📜 License
161
161
 
162
162
  MIT License. Created by [fkom13](https://github.com/fkom13) & The Pollinations Community.
163
+ ### 🛡️ Clés API Limitées
164
+
165
+ Si vous utilisez un token API restreint (ex: création d'une clé **sans** les permissions `Profile`, `Balance` ou `Usage`), le plugin passera automatiquement en mode **Manual**.
166
+
167
+ - **Mode Forcé** : `Manual` (Les modes `Pro` et `AlwaysFree` sont désactivés pour ces clés car ils nécessitent l'accès au quota pour fonctionner).
168
+ - **Conséquence** : Pas de Dashboard, pas de Safety Nets, mais génération fonctionnelle sur les modèles autorisés.
@@ -3,6 +3,39 @@ import { getQuotaStatus } from './quota.js';
3
3
  import { emitStatusToast } from './toast.js';
4
4
  import { getDetailedUsage } from './pollinations-api.js';
5
5
  import { generatePollinationsConfig } from './generate-config.js';
6
+ import * as https from 'https';
7
+ // --- HELPER: STRICT PERMISSION CHECK ---
8
+ function checkKeyPermissions(key) {
9
+ return new Promise((resolve) => {
10
+ // We need Usage, Profile AND Balance for "Managed Modes"
11
+ // If any of these fail (403), the key is Limited.
12
+ const endpoints = ['/account/profile', '/account/balance', '/account/usage'];
13
+ let successCount = 0;
14
+ let completed = 0;
15
+ endpoints.forEach(ep => {
16
+ const req = https.request({
17
+ hostname: 'gen.pollinations.ai',
18
+ path: ep,
19
+ method: 'GET',
20
+ headers: { 'Authorization': `Bearer ${key}` }
21
+ }, (res) => {
22
+ completed++;
23
+ if (res.statusCode === 200)
24
+ successCount++;
25
+ if (completed === endpoints.length) {
26
+ resolve(successCount === endpoints.length);
27
+ }
28
+ });
29
+ req.on('error', () => {
30
+ completed++;
31
+ if (completed === endpoints.length)
32
+ resolve(successCount === endpoints.length);
33
+ });
34
+ req.setTimeout(5000, () => req.destroy());
35
+ req.end();
36
+ });
37
+ });
38
+ }
6
39
  // === CONSTANTS & PRICING ===
7
40
  const TIER_LIMITS = {
8
41
  spore: { pollen: 1, emoji: '🦠' },
@@ -121,9 +154,17 @@ function handleModeCommand(args) {
121
154
  error: `Mode invalide: ${mode}. Valeurs: manual, alwaysfree, pro`
122
155
  };
123
156
  }
157
+ const currentConfig = loadConfig();
158
+ // RESTRICTED KEY LOGIC: Block Managed Modes if key is limited
159
+ if (currentConfig.keyHasAccessToProfile === false && (mode === 'alwaysfree' || mode === 'pro')) {
160
+ return {
161
+ handled: true,
162
+ error: `❌ **Mode Refusé**: Votre clé API est "Limitée" (Pas d'accès Profile/Usage).\n\nLes modes gérés (Pro/AlwaysFree) nécessitent un accès au Quota pour fonctionner.\nRestez en mode **Manual** ou utilisez une clé avec permissions complètes.`
163
+ };
164
+ }
124
165
  saveConfig({ mode: mode });
125
- const config = loadConfig();
126
- if (config.gui.status !== 'none') {
166
+ const newConfig = loadConfig();
167
+ if (newConfig.gui.status !== 'none') {
127
168
  emitStatusToast('success', `Mode changé vers: ${mode}`, 'Pollinations Config');
128
169
  }
129
170
  return {
@@ -228,12 +269,31 @@ async function handleConnectCommand(args) {
228
269
  // SUCCESS
229
270
  saveConfig({ apiKey: key }); // Don't force mode 'pro'. Let user decide.
230
271
  const masked = key.substring(0, 6) + '...';
231
- // Count Paid Only models found
272
+ // count Paid Only models found
232
273
  const diamondCount = enterpriseModels.filter(m => m.name.includes('💎')).length;
274
+ // CHECK RESTRICTIONS: Strict Check (Usage + Profile + Balance)
275
+ let forcedModeMsg = "";
276
+ let isLimited = false;
277
+ try {
278
+ // Strict Probe: Must be able to read ALL accounting data
279
+ const hasFullAccess = await checkKeyPermissions(key);
280
+ isLimited = !hasFullAccess;
281
+ }
282
+ catch (e) {
283
+ isLimited = true;
284
+ }
285
+ // If Limited -> FORCE MANUAL
286
+ if (isLimited) {
287
+ saveConfig({ apiKey: key, mode: 'manual', keyHasAccessToProfile: false });
288
+ forcedModeMsg = "\n⚠️ **Clé Limitée** (Permissions insuffisantes) -> Mode **MANUAL** forcé.\n*Requis pour mode Auto: Profile, Balance & Usage.*";
289
+ }
290
+ else {
291
+ saveConfig({ apiKey: key, keyHasAccessToProfile: true }); // Let user keep current mode or default
292
+ }
233
293
  emitStatusToast('success', `Clé Valide! (${enterpriseModels.length} modèles Pro débloqués)`, 'Pollinations Config');
234
294
  return {
235
295
  handled: true,
236
- response: `✅ **Connexion Réussie!**\n- Clé: \`${masked}\`\n- Mode: **PRO** (Activé)\n- Modèles Débloqués: ${enterpriseModels.length} (dont ${diamondCount} 💎 Paid)`
296
+ response: `✅ **Connexion Réussie!**\n- Clé: \`${masked}\`\n- Modèles Débloqués: ${enterpriseModels.length} (dont ${diamondCount} 💎 Paid)${forcedModeMsg}`
237
297
  };
238
298
  }
239
299
  else {
@@ -79,12 +79,8 @@ export async function generatePollinationsConfig(forceApiKey, forceStrict = fals
79
79
  modelsOutput.push({ id: "free/gemini", name: "[Free] Gemini Flash (Fallback)", object: "model", variants: {} });
80
80
  }
81
81
  // 1.5 FORCE ENSURE CRITICAL MODELS
82
- // Sometimes the API list changes or is cached weirdly. We force vital models.
83
- const hasGemini = modelsOutput.find(m => m.id === 'free/gemini');
84
- if (!hasGemini) {
85
- log(`[ConfigGen] Force-injecting free/gemini.`);
86
- modelsOutput.push({ id: "free/gemini", name: "[Free] Gemini Flash (Force)", object: "model", variants: {} });
87
- }
82
+ // REMOVED: Duplicate Gemini/Flash injection.
83
+ // The alias below handles standardizing the ID.
88
84
  // ALIAS for Full ID matching (Fix ProviderModelNotFoundError) - ALWAYS CHECK SEPARATELY
89
85
  const hasGeminiAlias = modelsOutput.find(m => m.id === 'pollinations/free/gemini');
90
86
  if (!hasGeminiAlias) {
@@ -674,8 +674,9 @@ export async function handleChatCompletion(req, res, bodyRaw) {
674
674
  if (isFallbackActive)
675
675
  modeLabel += " (FALLBACK)";
676
676
  const fullMsg = `${dashboardMsg} | ⚙️ ${modeLabel}`;
677
- // Only emit if not silenced AND only for Enterprise/Paid requests
678
- if (isEnterprise) {
677
+ // Only emit if not silenced AND (only for Enterprise/Paid requests OR if Fallback occurred)
678
+ // We want to know if our Pro request failed.
679
+ if (isEnterprise || isFallbackActive) {
679
680
  emitStatusToast('info', fullMsg, 'Pollinations Status');
680
681
  }
681
682
  }
@@ -10,6 +10,7 @@ export interface QuotaStatus {
10
10
  needsAlert: boolean;
11
11
  tier: string;
12
12
  tierEmoji: string;
13
+ isLimitedKey?: boolean;
13
14
  }
14
15
  export declare function getQuotaStatus(forceRefresh?: boolean): Promise<QuotaStatus>;
15
16
  export declare function formatQuotaForToast(quota: QuotaStatus): string;
@@ -38,6 +38,25 @@ export async function getQuotaStatus(forceRefresh = false) {
38
38
  tierEmoji: '❌'
39
39
  };
40
40
  }
41
+ // CHECK LIMITED KEY (v5.5)
42
+ // If commands.ts detected this key has no profile access, return specific status immediately.
43
+ // We do NOT attempt to fetch quota to avoid 403 spam.
44
+ if (config.keyHasAccessToProfile === false) {
45
+ return {
46
+ tierRemaining: 0,
47
+ tierUsed: 0,
48
+ tierLimit: 0,
49
+ walletBalance: 0,
50
+ nextResetAt: new Date(),
51
+ timeUntilReset: 0,
52
+ canUseEnterprise: true, // GENERATION IS ALLOWED
53
+ isUsingWallet: false,
54
+ needsAlert: false,
55
+ tier: 'limited',
56
+ tierEmoji: '🗝️',
57
+ isLimitedKey: true
58
+ };
59
+ }
41
60
  const now = Date.now();
42
61
  if (!forceRefresh && cachedQuota && (now - lastQuotaFetch) < CACHE_TTL) {
43
62
  return cachedQuota;
@@ -198,6 +217,9 @@ function calculateCurrentPeriodUsage(usage, resetInfo) {
198
217
  }
199
218
  // === EXPORT POUR LES ALERTES ===
200
219
  export function formatQuotaForToast(quota) {
220
+ if (quota.isLimitedKey) {
221
+ return "⚠️ Dashboard Limitation: Clé restreinte (Activez Profile/Usage/Balance pour voir le Quota)";
222
+ }
201
223
  const tierPercent = quota.tierLimit > 0
202
224
  ? Math.round((quota.tierRemaining / quota.tierLimit) * 100)
203
225
  : 0;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "opencode-pollinations-plugin",
3
3
  "displayName": "Pollinations AI (V5.1)",
4
- "version": "5.4.16",
4
+ "version": "5.5.0",
5
5
  "description": "Native Pollinations.ai Provider Plugin for OpenCode",
6
6
  "publisher": "pollinations",
7
7
  "repository": {