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 +9 -42
- package/dist/server/commands.js +0 -24
- package/dist/server/config.d.ts +1 -1
- package/dist/server/config.js +1 -3
- package/package.json +1 -2
- package/dist/server/router.d.ts +0 -8
- package/dist/server/router.js +0 -122
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
|
|
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
|
-
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: "
|
|
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(`
|
|
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
|
|
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
|
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.d.ts
CHANGED
|
@@ -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:
|
|
28
|
+
version: number;
|
|
29
29
|
mode: "manual" | "alwaysfree" | "pro";
|
|
30
30
|
apiKey?: string;
|
|
31
31
|
gui: {
|
package/dist/server/config.js
CHANGED
|
@@ -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:
|
|
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.
|
|
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": {
|
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
|
-
}
|