opencode-pollinations-plugin 5.1.18 → 5.1.20
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
|
@@ -4,37 +4,27 @@ import { execSync } from 'child_process';
|
|
|
4
4
|
import { generatePollinationsConfig } from './server/generate-config.js';
|
|
5
5
|
import { loadConfig } from './server/config.js';
|
|
6
6
|
import { handleChatCompletion } from './server/proxy.js';
|
|
7
|
-
import { createToastHooks, setGlobalClient
|
|
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
|
|
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(
|
|
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
|
-
// CONFIG WATCHER REMOVED (Legacy)
|
|
38
28
|
const startProxy = () => {
|
|
39
29
|
return new Promise((resolve) => {
|
|
40
30
|
const server = http.createServer(async (req, res) => {
|
|
@@ -52,29 +42,13 @@ const startProxy = () => {
|
|
|
52
42
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
53
43
|
res.end(JSON.stringify({
|
|
54
44
|
status: "ok",
|
|
55
|
-
version: "
|
|
56
|
-
mode: config.mode
|
|
57
|
-
pid: process.pid
|
|
45
|
+
version: "v4.0.5",
|
|
46
|
+
mode: config.mode
|
|
58
47
|
}));
|
|
59
48
|
return;
|
|
60
49
|
}
|
|
61
|
-
// RESTORED: Models Endpoint
|
|
62
|
-
if (req.method === 'GET' && req.url === '/v1/models') {
|
|
63
|
-
try {
|
|
64
|
-
const { getAggregatedModels } = await import('./server/pollinations-api.js');
|
|
65
|
-
const models = await getAggregatedModels();
|
|
66
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
67
|
-
res.end(JSON.stringify(models));
|
|
68
|
-
log(`[Proxy] Served ${models.data?.length || 0} models`);
|
|
69
|
-
}
|
|
70
|
-
catch (e) {
|
|
71
|
-
log(`[Proxy] Error fetching models: ${e}`);
|
|
72
|
-
res.writeHead(500);
|
|
73
|
-
res.end(JSON.stringify({ error: String(e) }));
|
|
74
|
-
}
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
50
|
// SUPPORT FLEXIBLE DES PATHS
|
|
51
|
+
// Le SDK peut envoyer /v1/chat/completions ou juste /chat/completions
|
|
78
52
|
if (req.method === 'POST' && (req.url === '/v1/chat/completions' || req.url === '/chat/completions')) {
|
|
79
53
|
const chunks = [];
|
|
80
54
|
req.on('data', chunk => chunks.push(chunk));
|
|
@@ -82,10 +56,9 @@ const startProxy = () => {
|
|
|
82
56
|
try {
|
|
83
57
|
const bodyRaw = Buffer.concat(chunks).toString();
|
|
84
58
|
await handleChatCompletion(req, res, bodyRaw);
|
|
85
|
-
log(`[Proxy] Chat Completion Success`);
|
|
86
59
|
}
|
|
87
60
|
catch (e) {
|
|
88
|
-
log(`
|
|
61
|
+
log(`Error: ${e}`);
|
|
89
62
|
if (!res.headersSent) {
|
|
90
63
|
res.writeHead(500);
|
|
91
64
|
res.end(JSON.stringify({ error: String(e) }));
|
|
@@ -99,12 +72,11 @@ const startProxy = () => {
|
|
|
99
72
|
res.end("Not Found");
|
|
100
73
|
});
|
|
101
74
|
server.listen(TRACKING_PORT, '127.0.0.1', () => {
|
|
102
|
-
log(`[Proxy] Started
|
|
75
|
+
log(`[Proxy] Started V4 on port ${TRACKING_PORT}`);
|
|
103
76
|
resolve(TRACKING_PORT);
|
|
104
77
|
});
|
|
105
78
|
server.on('error', (e) => {
|
|
106
79
|
log(`[Proxy] Fatal Error: ${e}`);
|
|
107
|
-
// Retry logic could go here, but for now log is key
|
|
108
80
|
resolve(0);
|
|
109
81
|
});
|
|
110
82
|
});
|
|
@@ -128,12 +100,7 @@ export const PollinationsPlugin = async (ctx) => {
|
|
|
128
100
|
return {
|
|
129
101
|
async config(config) {
|
|
130
102
|
log("[Hook] config() called");
|
|
131
|
-
|
|
132
|
-
const incomingKey = config.provider?.pollinations?.options?.apiKey ||
|
|
133
|
-
config.provider?.pollinations_enter?.options?.apiKey;
|
|
134
|
-
if (incomingKey)
|
|
135
|
-
log(`[Hook] Detected API Key update.`);
|
|
136
|
-
const modelsArray = await generatePollinationsConfig(incomingKey);
|
|
103
|
+
const modelsArray = await generatePollinationsConfig();
|
|
137
104
|
const modelsObj = {};
|
|
138
105
|
for (const m of modelsArray) {
|
|
139
106
|
// Ensure ID is relative for mapping ("free/gemini")
|
|
@@ -152,7 +119,6 @@ export const PollinationsPlugin = async (ctx) => {
|
|
|
152
119
|
models: modelsObj
|
|
153
120
|
};
|
|
154
121
|
log(`[Hook] Registered ${Object.keys(modelsObj).length} models.`);
|
|
155
|
-
emitStatusToast('info', `${Object.keys(modelsObj).length} Models Loaded`, 'Pollinations');
|
|
156
122
|
},
|
|
157
123
|
...toastHooks,
|
|
158
124
|
...createStatusHooks(ctx.client), // New Status Bar Logic
|
package/dist/server/commands.js
CHANGED
|
@@ -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
|
package/dist/server/config.js
CHANGED
|
@@ -172,9 +172,7 @@ function readConfigFromDisk() {
|
|
|
172
172
|
if (!keyFound && config.mode === 'pro') {
|
|
173
173
|
config.mode = 'manual';
|
|
174
174
|
}
|
|
175
|
-
|
|
176
|
-
config.version = PKG_VERSION;
|
|
177
|
-
return config;
|
|
175
|
+
return { ...config, version: PKG_VERSION };
|
|
178
176
|
}
|
|
179
177
|
export function saveConfig(updates) {
|
|
180
178
|
try {
|
|
@@ -48,12 +48,10 @@ function formatName(id, censored = true) {
|
|
|
48
48
|
return clean;
|
|
49
49
|
}
|
|
50
50
|
// --- MAIN GENERATOR logic ---
|
|
51
|
-
export async function generatePollinationsConfig(
|
|
51
|
+
export async function generatePollinationsConfig() {
|
|
52
52
|
const config = loadConfig();
|
|
53
53
|
const modelsOutput = [];
|
|
54
|
-
|
|
55
|
-
const effectiveKey = forceApiKey || config.apiKey;
|
|
56
|
-
log(`Starting Configuration (V5.1.18 Hot-Reload)...`);
|
|
54
|
+
log(`Starting Configuration (V4.5 Clean Dynamic)...`);
|
|
57
55
|
// 1. FREE UNIVERSE
|
|
58
56
|
try {
|
|
59
57
|
// Switch to main models endpoint (User provided curl confirms it has 'description')
|
|
@@ -73,10 +71,10 @@ export async function generatePollinationsConfig(forceApiKey) {
|
|
|
73
71
|
modelsOutput.push({ id: "free/gemini", name: "[Free] Gemini Flash (Fallback)", object: "model", variants: {} });
|
|
74
72
|
}
|
|
75
73
|
// 2. ENTERPRISE UNIVERSE
|
|
76
|
-
if (
|
|
74
|
+
if (config.apiKey && config.apiKey.length > 5 && config.apiKey !== 'dummy') {
|
|
77
75
|
try {
|
|
78
76
|
const enterListRaw = await fetchJson('https://gen.pollinations.ai/text/models', {
|
|
79
|
-
'Authorization': `Bearer ${
|
|
77
|
+
'Authorization': `Bearer ${config.apiKey}`
|
|
80
78
|
});
|
|
81
79
|
const enterList = Array.isArray(enterListRaw) ? enterListRaw : (enterListRaw.data || []);
|
|
82
80
|
enterList.forEach((m) => {
|
package/package.json
CHANGED
package/dist/server/router.d.ts
DELETED
|
@@ -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>;
|
package/dist/server/router.js
DELETED
|
@@ -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
|
-
}
|