opencode-pollinations-plugin 5.5.5 → 5.6.0-beta.1

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
@@ -160,9 +160,3 @@ 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.
package/dist/index.js CHANGED
@@ -96,7 +96,7 @@ export const PollinationsPlugin = async (ctx) => {
96
96
  config.provider = {};
97
97
  config.provider['pollinations'] = {
98
98
  id: 'pollinations',
99
- name: `Pollinations V${loadConfig().version} (Native)`,
99
+ name: 'Pollinations V5.2 (Native)',
100
100
  options: { baseURL: localBaseUrl },
101
101
  models: modelsObj
102
102
  };
@@ -3,36 +3,6 @@ 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
- function checkEndpoint(ep, key) {
8
- return new Promise((resolve) => {
9
- const req = https.request({
10
- hostname: 'gen.pollinations.ai',
11
- path: ep,
12
- method: 'GET',
13
- headers: { 'Authorization': `Bearer ${key}` }
14
- }, (res) => {
15
- if (res.statusCode === 200)
16
- resolve({ ok: true });
17
- else
18
- resolve({ ok: false, status: res.statusCode });
19
- });
20
- req.on('error', (e) => resolve({ ok: false, status: e.message || 'Error' }));
21
- req.setTimeout(10000, () => req.destroy()); // 10s Timeout
22
- req.end();
23
- });
24
- }
25
- async function checkKeyPermissions(key) {
26
- // SEQUENTIAL CHECK (Avoid Rate Limits on Key Verification)
27
- const endpoints = ['/account/profile', '/account/balance', '/account/usage'];
28
- for (const ep of endpoints) {
29
- const res = await checkEndpoint(ep, key);
30
- if (!res.ok) {
31
- return { ok: false, reason: `${ep} (${res.status})` };
32
- }
33
- }
34
- return { ok: true };
35
- }
36
6
  // === CONSTANTS & PRICING ===
37
7
  const TIER_LIMITS = {
38
8
  spore: { pollen: 1, emoji: '🦠' },
@@ -151,17 +121,9 @@ function handleModeCommand(args) {
151
121
  error: `Mode invalide: ${mode}. Valeurs: manual, alwaysfree, pro`
152
122
  };
153
123
  }
154
- const currentConfig = loadConfig();
155
- // RESTRICTED KEY LOGIC: Block Managed Modes if key is limited
156
- if (currentConfig.keyHasAccessToProfile === false && (mode === 'alwaysfree' || mode === 'pro')) {
157
- return {
158
- handled: true,
159
- 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.`
160
- };
161
- }
162
124
  saveConfig({ mode: mode });
163
- const newConfig = loadConfig();
164
- if (newConfig.gui.status !== 'none') {
125
+ const config = loadConfig();
126
+ if (config.gui.status !== 'none') {
165
127
  emitStatusToast('success', `Mode changé vers: ${mode}`, 'Pollinations Config');
166
128
  }
167
129
  return {
@@ -266,36 +228,12 @@ async function handleConnectCommand(args) {
266
228
  // SUCCESS
267
229
  saveConfig({ apiKey: key }); // Don't force mode 'pro'. Let user decide.
268
230
  const masked = key.substring(0, 6) + '...';
269
- // count Paid Only models found
231
+ // Count Paid Only models found
270
232
  const diamondCount = enterpriseModels.filter(m => m.name.includes('💎')).length;
271
- // CHECK RESTRICTIONS: Strict Check (Usage + Profile + Balance)
272
- let forcedModeMsg = "";
273
- let isLimited = false;
274
- let limitReason = "";
275
- try {
276
- // Strict Probe: Must be able to read ALL accounting data
277
- const check = await checkKeyPermissions(key);
278
- if (!check.ok) {
279
- isLimited = true;
280
- limitReason = check.reason || "Unknown";
281
- }
282
- }
283
- catch (e) {
284
- isLimited = true;
285
- limitReason = e.message;
286
- }
287
- // If Limited -> FORCE MANUAL
288
- if (isLimited) {
289
- saveConfig({ apiKey: key, mode: 'manual', keyHasAccessToProfile: false });
290
- forcedModeMsg = `\n⚠️ **Clé Limitée** (Echec: ${limitReason}) -> Mode **MANUAL** forcé.\n*Requis pour mode Auto: Profile, Balance & Usage.*`;
291
- }
292
- else {
293
- saveConfig({ apiKey: key, keyHasAccessToProfile: true }); // Let user keep current mode or default
294
- }
295
233
  emitStatusToast('success', `Clé Valide! (${enterpriseModels.length} modèles Pro débloqués)`, 'Pollinations Config');
296
234
  return {
297
235
  handled: true,
298
- response: `✅ **Connexion Réussie!**\n- Clé: \`${masked}\`\n- Modèles Débloqués: ${enterpriseModels.length} (dont ${diamondCount} 💎 Paid)${forcedModeMsg}`
236
+ response: `✅ **Connexion Réussie!**\n- Clé: \`${masked}\`\n- Mode: **PRO** (Activé)\n- Modèles Débloqués: ${enterpriseModels.length} (dont ${diamondCount} 💎 Paid)`
299
237
  };
300
238
  }
301
239
  else {
@@ -79,8 +79,12 @@ 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
- // REMOVED: Duplicate Gemini/Flash injection.
83
- // The alias below handles standardizing the ID.
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
+ }
84
88
  // ALIAS for Full ID matching (Fix ProviderModelNotFoundError) - ALWAYS CHECK SEPARATELY
85
89
  const hasGeminiAlias = modelsOutput.find(m => m.id === 'pollinations/free/gemini');
86
90
  if (!hasGeminiAlias) {
@@ -211,21 +211,6 @@ export async function handleChatCompletion(req, res, bodyRaw) {
211
211
  // LOAD QUOTA FOR SAFETY CHECKS
212
212
  const { getQuotaStatus, formatQuotaForToast } = await import('./quota.js');
213
213
  const quota = await getQuotaStatus(false);
214
- // RUNTIME RESTRICTION ENFORCEMENT (V5.5.3)
215
- // If we are in a Managed Mode (Pro/AlwaysFree) BUT the Quota returns 'error'
216
- // (meaning Profile/Usage access failed or network issue), we MUST downgrade to Manual
217
- // to avoid "Quota Unreachable" Fallbacks. The user wants to force Manual.
218
- if ((config.mode === 'alwaysfree' || config.mode === 'pro') && quota.tier === 'error') {
219
- log(`[SafetyNet] Runtime: Quota Access Lost (Tier=error). Forcing MANUAL Mode.`);
220
- // 1. Update In-Memory Config (stops downstream fallbacks)
221
- config.mode = 'manual';
222
- config.keyHasAccessToProfile = false;
223
- // 2. Persist
224
- const { saveConfig } = await import('./config.js');
225
- saveConfig({ mode: 'manual', keyHasAccessToProfile: false });
226
- // 3. Notify
227
- emitStatusToast('warning', "⚠️ Accès Quota Perdu -> Mode MANUAL forcé", "System");
228
- }
229
214
  // A. Resolve Base Target
230
215
  if (actualModel.startsWith('enter/')) {
231
216
  isEnterprise = true;
@@ -689,9 +674,8 @@ export async function handleChatCompletion(req, res, bodyRaw) {
689
674
  if (isFallbackActive)
690
675
  modeLabel += " (FALLBACK)";
691
676
  const fullMsg = `${dashboardMsg} | ⚙️ ${modeLabel}`;
692
- // Only emit if not silenced AND (only for Enterprise/Paid requests OR if Fallback occurred)
693
- // We want to know if our Pro request failed.
694
- if (isEnterprise || isFallbackActive) {
677
+ // Only emit if not silenced AND only for Enterprise/Paid requests
678
+ if (isEnterprise) {
695
679
  emitStatusToast('info', fullMsg, 'Pollinations Status');
696
680
  }
697
681
  }
@@ -10,7 +10,6 @@ export interface QuotaStatus {
10
10
  needsAlert: boolean;
11
11
  tier: string;
12
12
  tierEmoji: string;
13
- isLimitedKey?: boolean;
14
13
  }
15
14
  export declare function getQuotaStatus(forceRefresh?: boolean): Promise<QuotaStatus>;
16
15
  export declare function formatQuotaForToast(quota: QuotaStatus): string;
@@ -38,36 +38,18 @@ 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
- }
60
41
  const now = Date.now();
61
42
  if (!forceRefresh && cachedQuota && (now - lastQuotaFetch) < CACHE_TTL) {
62
43
  return cachedQuota;
63
44
  }
64
45
  try {
65
46
  logQuota("Fetching Quota Data...");
66
- // SEQUENTIAL FETCH (Avoid Rate Limits)
67
- // We fetch one by one. If one fails, we catch and return fallback.
68
- const profileRes = await fetchAPI('/account/profile', config.apiKey);
69
- const balanceRes = await fetchAPI('/account/balance', config.apiKey);
70
- const usageRes = await fetchAPI('/account/usage', config.apiKey);
47
+ // Fetch parallèle using HTTPS helper
48
+ const [profileRes, balanceRes, usageRes] = await Promise.all([
49
+ fetchAPI('/account/profile', config.apiKey),
50
+ fetchAPI('/account/balance', config.apiKey),
51
+ fetchAPI('/account/usage', config.apiKey)
52
+ ]);
71
53
  logQuota(`Fetch Success. Tier: ${profileRes.tier}, Balance: ${balanceRes.balance}`);
72
54
  const profile = profileRes;
73
55
  const balance = balanceRes.balance;
@@ -216,9 +198,6 @@ function calculateCurrentPeriodUsage(usage, resetInfo) {
216
198
  }
217
199
  // === EXPORT POUR LES ALERTES ===
218
200
  export function formatQuotaForToast(quota) {
219
- if (quota.isLimitedKey) {
220
- return "⚠️ Dashboard Limitation: Clé restreinte (Activez Profile/Usage/Balance pour voir le Quota)";
221
- }
222
201
  const tierPercent = quota.tierLimit > 0
223
202
  ? Math.round((quota.tierRemaining / quota.tierLimit) * 100)
224
203
  : 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.5.5",
4
+ "version": "5.6.0-beta.1",
5
5
  "description": "Native Pollinations.ai Provider Plugin for OpenCode",
6
6
  "publisher": "pollinations",
7
7
  "repository": {