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 +0 -6
- package/dist/index.js +1 -1
- package/dist/server/commands.js +4 -66
- package/dist/server/generate-config.js +6 -2
- package/dist/server/proxy.js +2 -18
- package/dist/server/quota.d.ts +0 -1
- package/dist/server/quota.js +6 -27
- package/package.json +1 -1
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:
|
|
99
|
+
name: 'Pollinations V5.2 (Native)',
|
|
100
100
|
options: { baseURL: localBaseUrl },
|
|
101
101
|
models: modelsObj
|
|
102
102
|
};
|
package/dist/server/commands.js
CHANGED
|
@@ -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
|
|
164
|
-
if (
|
|
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
|
-
//
|
|
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)
|
|
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
|
-
//
|
|
83
|
-
|
|
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) {
|
package/dist/server/proxy.js
CHANGED
|
@@ -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
|
|
693
|
-
|
|
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
|
}
|
package/dist/server/quota.d.ts
CHANGED
|
@@ -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;
|
package/dist/server/quota.js
CHANGED
|
@@ -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
|
-
//
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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