opencode-pollinations-plugin 5.4.4 → 5.4.6

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
@@ -1,6 +1,5 @@
1
1
  import * as http from 'http';
2
2
  import * as fs from 'fs';
3
- import { execSync } from 'child_process';
4
3
  import { generatePollinationsConfig } from './server/generate-config.js';
5
4
  import { loadConfig } from './server/config.js';
6
5
  import { handleChatCompletion } from './server/proxy.js';
@@ -14,15 +13,7 @@ function log(msg) {
14
13
  }
15
14
  catch (e) { }
16
15
  }
17
- const TRACKING_PORT = 10001;
18
- // === ANTI-ZOMBIE (LINUX/MAC LEGACY STABLE) ===
19
- try {
20
- log(`[Init] Cleaning port ${TRACKING_PORT}...`);
21
- execSync(`fuser -k ${TRACKING_PORT}/tcp || true`);
22
- }
23
- catch (e) {
24
- // Ignore on non-Linux or if no process found
25
- }
16
+ // Port killing removed: Using dynamic ports.
26
17
  const startProxy = () => {
27
18
  return new Promise((resolve) => {
28
19
  const server = http.createServer(async (req, res) => {
@@ -69,9 +60,12 @@ const startProxy = () => {
69
60
  res.writeHead(404);
70
61
  res.end("Not Found");
71
62
  });
72
- server.listen(TRACKING_PORT, '127.0.0.1', () => {
73
- log(`[Proxy] Started V5.3.2 (Legacy Port Logic) on port ${TRACKING_PORT}`);
74
- resolve(TRACKING_PORT);
63
+ // Listen on random port (0) to avoid conflicts (CLI/IDE)
64
+ server.listen(0, '127.0.0.1', () => {
65
+ // @ts-ignore
66
+ const assignedPort = server.address().port;
67
+ log(`[Proxy] Started V5.4.5 (Dynamic Port) on port ${assignedPort}`);
68
+ resolve(assignedPort);
75
69
  });
76
70
  server.on('error', (e) => {
77
71
  log(`[Proxy] Fatal Error: ${e}`);
@@ -79,7 +79,10 @@ export async function generatePollinationsConfig(forceApiKey) {
79
79
  if (!hasGemini) {
80
80
  log(`[ConfigGen] Force-injecting free/gemini.`);
81
81
  modelsOutput.push({ id: "free/gemini", name: "[Free] Gemini Flash (Force)", object: "model", variants: {} });
82
- // ALIAS for Full ID matching (Fix ProviderModelNotFoundError)
82
+ }
83
+ // ALIAS for Full ID matching (Fix ProviderModelNotFoundError) - ALWAYS CHECK SEPARATELY
84
+ const hasGeminiAlias = modelsOutput.find(m => m.id === 'pollinations/free/gemini');
85
+ if (!hasGeminiAlias) {
83
86
  modelsOutput.push({ id: "pollinations/free/gemini", name: "[Free] Gemini Flash (Alias)", object: "model", variants: {} });
84
87
  }
85
88
  // 2. ENTERPRISE UNIVERSE
@@ -461,21 +461,34 @@ export async function handleChatCompletion(req, res, bodyRaw) {
461
461
  });
462
462
  if (!fetchRes.ok) {
463
463
  log(`Upstream Error: ${fetchRes.status} ${fetchRes.statusText}`);
464
- // TRANSPARENT FALLBACK ON 4xx Errors (Payment, Rate Limit, Auth) IF Enterprise
465
- if ((fetchRes.status === 402 || fetchRes.status === 429 || fetchRes.status === 401 || fetchRes.status === 403) && isEnterprise) {
464
+ // TRANSPARENT FALLBACK LOGIC
465
+ // 1. Enterprise Safety Net (Quota/Auth/RateLimit)
466
+ // 2. Gemini Tools Fix (Gemini + Tools -> 401 -> Fallback to OpenAI)
467
+ const isEnterpriseFallback = (fetchRes.status === 402 || fetchRes.status === 429 || fetchRes.status === 401 || fetchRes.status === 403) && isEnterprise;
468
+ const isGeminiToolsFallback = fetchRes.status === 401 && actualModel.includes('gemini') && !isEnterprise && proxyBody.tools && proxyBody.tools.length > 0;
469
+ if (isEnterpriseFallback || isGeminiToolsFallback) {
466
470
  log(`[SafetyNet] Upstream Rejection (${fetchRes.status}). Triggering Transparent Fallback.`);
467
- // 1. Switch Config
468
- actualModel = config.fallbacks.free.main.replace('free/', '');
469
- isEnterprise = false;
470
- isFallbackActive = true;
471
- if (fetchRes.status === 402)
472
- fallbackReason = "Insufficient Funds (Upstream 402)";
473
- else if (fetchRes.status === 429)
474
- fallbackReason = "Rate Limit (Upstream 429)";
475
- else if (fetchRes.status === 401)
476
- fallbackReason = "Invalid API Key (Upstream 401)";
477
- else
478
- fallbackReason = `Access Denied (${fetchRes.status})`;
471
+ if (isEnterpriseFallback) {
472
+ // 1a. Enterprise -> Free Fallback
473
+ actualModel = config.fallbacks.free.main.replace('free/', '');
474
+ isEnterprise = false;
475
+ isFallbackActive = true;
476
+ if (fetchRes.status === 402)
477
+ fallbackReason = "Insufficient Funds (Upstream 402)";
478
+ else if (fetchRes.status === 429)
479
+ fallbackReason = "Rate Limit (Upstream 429)";
480
+ else if (fetchRes.status === 401)
481
+ fallbackReason = "Invalid API Key (Upstream 401)";
482
+ else
483
+ fallbackReason = `Access Denied (${fetchRes.status})`;
484
+ }
485
+ else {
486
+ // 1b. Gemini Tools -> OpenAI Fallback
487
+ log(`[Fix] Gemini Tools 401 detected. Falling back to 'openai' model.`);
488
+ actualModel = 'openai'; // Assume gpt-4o-mini or similar capable of tools
489
+ isFallbackActive = true;
490
+ fallbackReason = "Gemini Tools Auth Failed (Fallback to OpenAI)";
491
+ }
479
492
  // 2. Notify
480
493
  emitStatusToast('warning', `⚠️ Safety Net: ${actualModel} (${fallbackReason})`, 'Pollinations Safety');
481
494
  emitLogToast('warning', `Recovering from ${fetchRes.status} -> Switching to ${actualModel}`, 'Safety Net');
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.4",
4
+ "version": "5.4.6",
5
5
  "description": "Native Pollinations.ai Provider Plugin for OpenCode",
6
6
  "publisher": "pollinations",
7
7
  "repository": {