opencode-pollinations-plugin 5.1.17 → 5.1.19

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
@@ -2,43 +2,29 @@ import * as http from 'http';
2
2
  import * as fs from 'fs';
3
3
  import { execSync } from 'child_process';
4
4
  import { generatePollinationsConfig } from './server/generate-config.js';
5
- import { loadConfig, subscribeToConfigChange } from './server/config.js';
5
+ import { loadConfig } from './server/config.js';
6
6
  import { handleChatCompletion } from './server/proxy.js';
7
- import { createToastHooks, setGlobalClient, emitStatusToast } from './server/toast.js';
7
+ import { createToastHooks, setGlobalClient } from './server/toast.js';
8
8
  import { createStatusHooks } from './server/status.js';
9
9
  import { createCommandHooks } from './server/commands.js';
10
- const LIFE_LOG = '/tmp/POLLI_LIFECYCLE.log';
11
- const LOC_LOG = '/tmp/POLLI_LOCATION.log';
10
+ const LOG_FILE = '/tmp/opencode_pollinations_v4.log';
12
11
  function log(msg) {
13
12
  try {
14
- fs.appendFileSync(LIFE_LOG, `[${new Date().toISOString()}] ${msg}\n`);
13
+ fs.appendFileSync(LOG_FILE, `[${new Date().toISOString()}] ${msg}\n`);
15
14
  }
16
15
  catch (e) { }
17
16
  }
18
17
  const TRACKING_PORT = 10001;
19
- // IMMEDIATE PROBE
20
- try {
21
- fs.writeFileSync(LOC_LOG, `[${new Date().toISOString()}] ENTRY_POINT_RUNNING: ${__filename}\n`);
22
- log(`[STARTUP] Initializing Plugin v5.1.16...`);
23
- }
24
- catch (e) { }
25
18
  // === ANTI-ZOMBIE ATOMIC CLEANUP ===
26
19
  try {
27
20
  log(`[Init] Checking port ${TRACKING_PORT} for zombies...`);
28
21
  execSync(`fuser -k ${TRACKING_PORT}/tcp || true`);
29
22
  log(`[Init] Port ${TRACKING_PORT} cleaned.`);
30
- execSync('sleep 1');
31
23
  }
32
24
  catch (e) {
33
25
  log(`[Init] Zombie cleanup warning: ${e}`);
34
26
  }
35
27
  // === GESTION DU CYCLE DE VIE PROXY ===
36
- // CONFIG WATCHER (Hot Reload)
37
- subscribeToConfigChange(() => {
38
- const config = loadConfig();
39
- log(`[HotReload] Config changed. Mode: ${config.mode}`);
40
- emitStatusToast('info', `Config Reloaded: ${config.mode}`, 'Pollinations');
41
- });
42
28
  const startProxy = () => {
43
29
  return new Promise((resolve) => {
44
30
  const server = http.createServer(async (req, res) => {
@@ -56,29 +42,13 @@ const startProxy = () => {
56
42
  res.writeHead(200, { 'Content-Type': 'application/json' });
57
43
  res.end(JSON.stringify({
58
44
  status: "ok",
59
- version: "v5.1.17",
60
- mode: config.mode,
61
- pid: process.pid
45
+ version: "v4.0.5",
46
+ mode: config.mode
62
47
  }));
63
48
  return;
64
49
  }
65
- // RESTORED: Models Endpoint
66
- if (req.method === 'GET' && req.url === '/v1/models') {
67
- try {
68
- const { getAggregatedModels } = await import('./server/pollinations-api.js');
69
- const models = await getAggregatedModels();
70
- res.writeHead(200, { 'Content-Type': 'application/json' });
71
- res.end(JSON.stringify(models));
72
- log(`[Proxy] Served ${models.data?.length || 0} models`);
73
- }
74
- catch (e) {
75
- log(`[Proxy] Error fetching models: ${e}`);
76
- res.writeHead(500);
77
- res.end(JSON.stringify({ error: String(e) }));
78
- }
79
- return;
80
- }
81
50
  // SUPPORT FLEXIBLE DES PATHS
51
+ // Le SDK peut envoyer /v1/chat/completions ou juste /chat/completions
82
52
  if (req.method === 'POST' && (req.url === '/v1/chat/completions' || req.url === '/chat/completions')) {
83
53
  const chunks = [];
84
54
  req.on('data', chunk => chunks.push(chunk));
@@ -86,10 +56,9 @@ const startProxy = () => {
86
56
  try {
87
57
  const bodyRaw = Buffer.concat(chunks).toString();
88
58
  await handleChatCompletion(req, res, bodyRaw);
89
- log(`[Proxy] Chat Completion Success`);
90
59
  }
91
60
  catch (e) {
92
- log(`[Proxy] Chat Error: ${e}`);
61
+ log(`Error: ${e}`);
93
62
  if (!res.headersSent) {
94
63
  res.writeHead(500);
95
64
  res.end(JSON.stringify({ error: String(e) }));
@@ -103,12 +72,11 @@ const startProxy = () => {
103
72
  res.end("Not Found");
104
73
  });
105
74
  server.listen(TRACKING_PORT, '127.0.0.1', () => {
106
- log(`[Proxy] Started V5.1.17 on port ${TRACKING_PORT}`);
75
+ log(`[Proxy] Started V4 on port ${TRACKING_PORT}`);
107
76
  resolve(TRACKING_PORT);
108
77
  });
109
78
  server.on('error', (e) => {
110
79
  log(`[Proxy] Fatal Error: ${e}`);
111
- // Retry logic could go here, but for now log is key
112
80
  resolve(0);
113
81
  });
114
82
  });
@@ -151,7 +119,6 @@ export const PollinationsPlugin = async (ctx) => {
151
119
  models: modelsObj
152
120
  };
153
121
  log(`[Hook] Registered ${Object.keys(modelsObj).length} models.`);
154
- emitStatusToast('info', `${Object.keys(modelsObj).length} Models Loaded`, 'Pollinations');
155
122
  },
156
123
  ...toastHooks,
157
124
  ...createStatusHooks(ctx.client), // New Status Bar Logic
@@ -1,5 +1,3 @@
1
- import * as fs from 'fs';
2
- import * as path from 'path';
3
1
  import { loadConfig, saveConfig } from './config.js';
4
2
  import { getQuotaStatus } from './quota.js';
5
3
  import { emitStatusToast } from './toast.js';
@@ -95,8 +93,6 @@ export async function handleCommand(command) {
95
93
  return handleFallbackCommand(args);
96
94
  case 'config':
97
95
  return handleConfigCommand(args);
98
- case 'debug':
99
- return handleDebugCommand();
100
96
  case 'help':
101
97
  return handleHelpCommand();
102
98
  default:
@@ -271,26 +267,6 @@ function handleConfigCommand(args) {
271
267
  error: `Clé inconnue: ${key}. Clés: status_gui, logs_gui, threshold_tier, threshold_wallet, status_bar`
272
268
  };
273
269
  }
274
- function handleDebugCommand() {
275
- const info = {
276
- pid: process.pid,
277
- execPath: process.execPath,
278
- cwd: process.cwd(),
279
- home: process.env.HOME || 'undefined',
280
- node: process.version,
281
- uid: process.getuid ? process.getuid() : 'unknown',
282
- gid: process.getgid ? process.getgid() : 'unknown',
283
- tmp: '/tmp',
284
- port_env: process.env.POLLINATIONS_PORT || 'default(10001)',
285
- log_file_check: fs.existsSync(path.join(process.env.HOME || '', '.config/opencode/plugins/pollinations-v3.log')),
286
- tmp_log_check: fs.existsSync('/tmp/POLLI_LIFECYCLE.log')
287
- };
288
- return {
289
- handled: true,
290
- response: `### 🐞 Debug Probe v5.1.16\n` +
291
- `\`\`\`json\n${JSON.stringify(info, null, 2)}\n\`\`\``
292
- };
293
- }
294
270
  function handleHelpCommand() {
295
271
  const help = `
296
272
  ### 🌸 Pollinations Plugin - Commandes V5
@@ -25,7 +25,7 @@ export interface PollinationsConfigV5 {
25
25
  export declare function subscribeToConfigChange(callback: () => void): void;
26
26
  export declare function loadConfig(): PollinationsConfigV5;
27
27
  export declare function saveConfig(updates: Partial<PollinationsConfigV5>): {
28
- version: string;
28
+ version: number;
29
29
  mode: "manual" | "alwaysfree" | "pro";
30
30
  apiKey?: string;
31
31
  gui: {
@@ -172,15 +172,13 @@ function readConfigFromDisk() {
172
172
  if (!keyFound && config.mode === 'pro') {
173
173
  config.mode = 'manual';
174
174
  }
175
- // CRITICAL: Always enforce the runtime version, never trust the file cache for versioning
176
- config.version = PKG_VERSION;
177
175
  return config;
178
176
  }
179
177
  export function saveConfig(updates) {
180
178
  try {
181
179
  // We must base updates on current state (even if cached is slightly old, we refresh first?)
182
180
  const current = readConfigFromDisk(); // Read disk for safety before write
183
- const updated = { ...current, ...updates, version: PKG_VERSION };
181
+ const updated = { ...current, ...updates, version: 5 };
184
182
  if (!fs.existsSync(CONFIG_DIR_POLLI)) {
185
183
  fs.mkdirSync(CONFIG_DIR_POLLI, { recursive: true });
186
184
  }
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.1.17",
4
+ "version": "5.1.19",
5
5
  "description": "Native Pollinations.ai Provider Plugin for OpenCode",
6
6
  "publisher": "pollinations",
7
7
  "repository": {
@@ -28,7 +28,6 @@
28
28
  ],
29
29
  "scripts": {
30
30
  "build": "tsc",
31
- "prepare": "npm run build",
32
31
  "package": "npx vsce package"
33
32
  },
34
33
  "contributes": {
@@ -1,8 +0,0 @@
1
- export interface RoutingDecision {
2
- targetUrl: string;
3
- actualModel: string;
4
- authHeader?: string;
5
- fallbackUsed: boolean;
6
- fallbackReason?: string;
7
- }
8
- export declare function resolveRouting(requestedModel: string, isAgent?: boolean): Promise<RoutingDecision>;
@@ -1,122 +0,0 @@
1
- import { loadConfig } from './config.js';
2
- import { getQuotaStatus } from './quota.js';
3
- import { emitToast } from './toast.js';
4
- // === MAIN ROUTER ===
5
- export async function resolveRouting(requestedModel, isAgent = false) {
6
- const config = loadConfig();
7
- // Normalisation de l'ID modèle pour l'analyse
8
- // Peut arriver sous forme: "pollinations/free/gemini" OU "free/gemini"
9
- const isEnterprise = requestedModel.includes('/enter/') || requestedModel.startsWith('enter/');
10
- const isFree = requestedModel.includes('/free/') || requestedModel.startsWith('free/');
11
- // Extraction du "baseModel" (ex: "gemini", "openai")
12
- let baseModel = requestedModel;
13
- baseModel = baseModel.replace(/^pollinations\//, ''); // Remove plugin prefix
14
- baseModel = baseModel.replace(/^(enter|free)\//, ''); // Remove tier prefix
15
- // === MODE MANUAL ===
16
- if (config.mode === 'manual') {
17
- if (isEnterprise && config.apiKey) {
18
- return {
19
- targetUrl: 'https://gen.pollinations.ai/v1/chat/completions',
20
- actualModel: baseModel,
21
- authHeader: `Bearer ${config.apiKey}`,
22
- fallbackUsed: false
23
- };
24
- }
25
- // Default Free
26
- return {
27
- targetUrl: 'https://text.pollinations.ai/openai/chat/completions',
28
- actualModel: baseModel,
29
- authHeader: undefined,
30
- fallbackUsed: false
31
- };
32
- }
33
- // === MODES INTELLIGENTS ===
34
- if (!config.apiKey) {
35
- return {
36
- targetUrl: 'https://text.pollinations.ai/openai/chat/completions',
37
- actualModel: baseModel,
38
- authHeader: undefined,
39
- fallbackUsed: false
40
- };
41
- }
42
- const quota = await getQuotaStatus();
43
- handleQuotaAlerts(quota, config);
44
- // === ALWAYSFREE ===
45
- if (config.mode === 'alwaysfree') {
46
- if (isEnterprise) {
47
- if (quota.tierRemaining > 0) {
48
- return {
49
- targetUrl: 'https://gen.pollinations.ai/v1/chat/completions',
50
- actualModel: baseModel,
51
- authHeader: `Bearer ${config.apiKey}`,
52
- fallbackUsed: false
53
- };
54
- }
55
- else {
56
- const fallbackModel = isAgent ? config.fallbackModels.agent : config.fallbackModels.main;
57
- emitToast('warning', `Quota Free épuisé 🛑 → Relai sur ${fallbackModel} gratuit 🔀`, 'Mode AlwaysFree');
58
- return {
59
- targetUrl: 'https://text.pollinations.ai/openai/chat/completions',
60
- actualModel: fallbackModel,
61
- authHeader: undefined,
62
- fallbackUsed: true,
63
- fallbackReason: 'tier_exhausted'
64
- };
65
- }
66
- }
67
- // Free
68
- return {
69
- targetUrl: 'https://text.pollinations.ai/openai/chat/completions',
70
- actualModel: baseModel,
71
- authHeader: undefined,
72
- fallbackUsed: false
73
- };
74
- }
75
- // === PRO ===
76
- if (config.mode === 'pro') {
77
- if (isEnterprise) {
78
- if (quota.canUseEnterprise) {
79
- if (quota.isUsingWallet) {
80
- emitToast('info', `Tier épuisé → Utilisation du Wallet ($${quota.walletBalance.toFixed(2)})`, 'Mode Pro');
81
- }
82
- return {
83
- targetUrl: 'https://gen.pollinations.ai/v1/chat/completions',
84
- actualModel: baseModel,
85
- authHeader: `Bearer ${config.apiKey}`,
86
- fallbackUsed: false
87
- };
88
- }
89
- else {
90
- const fallbackModel = isAgent ? config.fallbackModels.agent : config.fallbackModels.main;
91
- emitToast('error', `💸 Wallet épuisé ! Fallback sur ${fallbackModel}`, 'Mode Pro');
92
- return {
93
- targetUrl: 'https://text.pollinations.ai/openai/chat/completions',
94
- actualModel: fallbackModel,
95
- authHeader: undefined,
96
- fallbackUsed: true,
97
- fallbackReason: 'wallet_exhausted'
98
- };
99
- }
100
- }
101
- // Free
102
- return {
103
- targetUrl: 'https://text.pollinations.ai/openai/chat/completions',
104
- actualModel: baseModel,
105
- authHeader: undefined,
106
- fallbackUsed: false
107
- };
108
- }
109
- // Default
110
- return {
111
- targetUrl: 'https://text.pollinations.ai/openai/chat/completions',
112
- actualModel: baseModel,
113
- authHeader: undefined,
114
- fallbackUsed: false
115
- };
116
- }
117
- function handleQuotaAlerts(quota, config) {
118
- if (quota.needsAlert && quota.tierLimit > 0) {
119
- const tierPercent = Math.round((quota.tierRemaining / quota.tierLimit) * 100);
120
- emitToast('warning', `⚠️ Quota Tier à ${tierPercent}%`, 'Alerte Quota');
121
- }
122
- }