opencode-pollinations-plugin 5.6.0-beta.0 → 5.6.0-beta.2

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/dist/index.js CHANGED
@@ -6,6 +6,8 @@ import { handleChatCompletion } from './server/proxy.js';
6
6
  import { createToastHooks, setGlobalClient } from './server/toast.js';
7
7
  import { createStatusHooks } from './server/status.js';
8
8
  import { createCommandHooks } from './server/commands.js';
9
+ import { createRequire } from 'module';
10
+ const require = createRequire(import.meta.url);
9
11
  const LOG_FILE = '/tmp/opencode_pollinations_v4.log';
10
12
  function log(msg) {
11
13
  try {
@@ -94,9 +96,11 @@ export const PollinationsPlugin = async (ctx) => {
94
96
  }
95
97
  if (!config.provider)
96
98
  config.provider = {};
99
+ // Dynamic Provider Name
100
+ const version = require('../../package.json').version;
97
101
  config.provider['pollinations'] = {
98
102
  id: 'pollinations',
99
- name: 'Pollinations V5.2 (Native)',
103
+ name: `Pollinations AI (v${version})`,
100
104
  options: { baseURL: localBaseUrl },
101
105
  models: modelsObj
102
106
  };
@@ -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,45 +228,34 @@ 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 {
302
240
  // FAILURE (Valid JSON but no Enterprise models - likely Invalid Key or Free plan only?)
241
+ // If key is invalid, generatePollinationsConfig usually returns fallback free models BUT
242
+ // we specifically checked 'enter/'. If 0 enterprise models found for a *provided* key, it's suspicious.
243
+ // Actually config generator returns Free models + Enter models if key works.
244
+ // If key is BAD, fetchJson throws/logs error, and returns fallbacks (Enter GPT-4o Fallback).
245
+ // Wait, generate-config falls back to providing a list containing "[Enter] GPT-4o (Fallback)" if fetch failed.
246
+ // So we need to detect if it's a "REAL" fetch or a "FALLBACK" fetch.
247
+ // The fallback models have `variants: {}` usually, but real ones might too.
248
+ // A better check: The fallback list is hardcoded in generate-config.ts catch block.
249
+ // Let's modify generate-config to return EMPTY list on error?
250
+ // Or just check if the returned models work?
251
+ // Simplest: If `generatePollinationsConfig` returns any model starting with `enter/` that includes "(Fallback)" in name, we assume failure?
252
+ // "GPT-4o (Fallback)" is the name.
303
253
  const isFallback = models.some(m => m.name.includes('(Fallback)') && m.id.startsWith('enter/'));
304
254
  if (isFallback) {
305
255
  throw new Error("Clé rejetée par l'API (Accès refusé ou invalide).");
306
256
  }
307
257
  // If we are here, we got no enter models, or empty list?
258
+ // If key is valid but has no access?
308
259
  throw new Error("Aucun modèle Enterprise détecté pour cette clé.");
309
260
  }
310
261
  }
@@ -328,6 +279,7 @@ function handleConfigCommand(args) {
328
279
  };
329
280
  }
330
281
  if (key === 'toast_verbosity' && value) {
282
+ // BACKWARD COMPAT (Maps to Status GUI)
331
283
  if (!['none', 'alert', 'all'].includes(value)) {
332
284
  return { handled: true, error: 'Valeurs: none, alert, all' };
333
285
  }
@@ -85,11 +85,11 @@ export async function generatePollinationsConfig(forceApiKey, forceStrict = fals
85
85
  log(`[ConfigGen] Force-injecting free/gemini.`);
86
86
  modelsOutput.push({ id: "free/gemini", name: "[Free] Gemini Flash (Force)", object: "model", variants: {} });
87
87
  }
88
- // ALIAS for Full ID matching (Fix ProviderModelNotFoundError) - ALWAYS CHECK SEPARATELY
89
- const hasGeminiAlias = modelsOutput.find(m => m.id === 'pollinations/free/gemini');
90
- if (!hasGeminiAlias) {
91
- modelsOutput.push({ id: "pollinations/free/gemini", name: "[Free] Gemini Flash (Alias)", object: "model", variants: {} });
92
- }
88
+ // ALIAS Removed for Clean Config
89
+ // const hasGeminiAlias = modelsOutput.find(m => m.id === 'pollinations/free/gemini');
90
+ // if (!hasGeminiAlias) {
91
+ // modelsOutput.push({ id: "pollinations/free/gemini", name: "[Free] Gemini Flash (Alias)", object: "model", variants: {} });
92
+ // }
93
93
  // 2. ENTERPRISE UNIVERSE
94
94
  if (effectiveKey && effectiveKey.length > 5 && effectiveKey !== 'dummy') {
95
95
  try {
@@ -45,11 +45,11 @@ export async function getQuotaStatus(forceRefresh = false) {
45
45
  try {
46
46
  logQuota("Fetching Quota Data...");
47
47
  // Fetch parallèle using HTTPS helper
48
- // SEQUENTIAL FETCH (Avoid Rate Limits)
49
- // We fetch one by one. If one fails, we catch and return fallback.
50
- const profileRes = await fetchAPI('/account/profile', config.apiKey);
51
- const balanceRes = await fetchAPI('/account/balance', config.apiKey);
52
- const usageRes = await fetchAPI('/account/usage', config.apiKey);
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
+ ]);
53
53
  logQuota(`Fetch Success. Tier: ${profileRes.tier}, Balance: ${balanceRes.balance}`);
54
54
  const profile = profileRes;
55
55
  const balance = balanceRes.balance;
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.6.0-beta.0",
4
+ "version": "5.6.0-beta.2",
5
5
  "description": "Native Pollinations.ai Provider Plugin for OpenCode",
6
6
  "publisher": "pollinations",
7
7
  "repository": {