opencode-pollinations-plugin 6.0.0-beta.24 → 6.0.0-beta.3
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/README.md +7 -7
- package/dist/index.js +26 -183
- package/dist/server/commands.js +19 -9
- package/dist/server/generate-config.d.ts +3 -30
- package/dist/server/generate-config.js +164 -100
- package/dist/server/proxy.js +109 -65
- package/dist/tools/design/gen_diagram.d.ts +2 -0
- package/dist/tools/design/gen_diagram.js +94 -0
- package/dist/tools/design/gen_palette.d.ts +2 -0
- package/dist/tools/design/gen_palette.js +182 -0
- package/dist/tools/design/gen_qrcode.d.ts +2 -0
- package/dist/tools/design/gen_qrcode.js +50 -0
- package/dist/tools/index.d.ts +14 -0
- package/dist/tools/index.js +77 -0
- package/dist/tools/power/extract_audio.d.ts +2 -0
- package/dist/tools/power/extract_audio.js +180 -0
- package/dist/tools/power/extract_frames.d.ts +2 -0
- package/dist/tools/power/extract_frames.js +240 -0
- package/dist/tools/power/file_to_url.d.ts +2 -0
- package/dist/tools/power/file_to_url.js +217 -0
- package/dist/tools/power/remove_background.d.ts +2 -0
- package/dist/tools/power/remove_background.js +106 -0
- package/dist/tools/shared.d.ts +30 -0
- package/dist/tools/shared.js +74 -0
- package/package.json +6 -4
- package/dist/server/models-seed.d.ts +0 -18
- package/dist/server/models-seed.js +0 -55
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# 🌸 Pollinations AI Plugin for OpenCode (v5.
|
|
1
|
+
# 🌸 Pollinations AI Plugin for OpenCode (v5.9.0)
|
|
2
2
|
|
|
3
3
|
<div align="center">
|
|
4
4
|
<img src="https://avatars.githubusercontent.com/u/88394740?s=400&v=4" alt="Pollinations.ai Logo" width="200">
|
|
@@ -10,11 +10,9 @@
|
|
|
10
10
|
|
|
11
11
|
<div align="center">
|
|
12
12
|
|
|
13
|
-

|
|
14
14
|

|
|
15
|
-
 | [🛣️ Roadmap](./ROADMAP.md)
|
|
15
|
+

|
|
18
16
|
|
|
19
17
|
</div>
|
|
20
18
|
|
|
@@ -136,8 +134,9 @@ OpenCode uses NPM as its registry. To publish:
|
|
|
136
134
|
|
|
137
135
|
### 1. The Basics (Free Mode)
|
|
138
136
|
Just type in the chat. You are in **Manual Mode** by default.
|
|
139
|
-
- Model: `openai` (GPT-
|
|
140
|
-
- Model: `mistral` (Mistral
|
|
137
|
+
- Model: `openai-fast` (GPT-OSS 20b)
|
|
138
|
+
- Model: `mistral` (Mistral Small 3.1)
|
|
139
|
+
- ...
|
|
141
140
|
|
|
142
141
|
### 🔑 Configuration (API Key)
|
|
143
142
|
|
|
@@ -157,6 +156,7 @@ Just type in the chat. You are in **Manual Mode** by default.
|
|
|
157
156
|
|
|
158
157
|
## 🔗 Links
|
|
159
158
|
|
|
159
|
+
- **Sign up Pollinations Beta (more and best free tiers access and paids models)**: [pollinations.ai](https://enter.pollinations.ai)
|
|
160
160
|
- **Pollinations Website**: [pollinations.ai](https://pollinations.ai)
|
|
161
161
|
- **Discord Community**: [Join us!](https://discord.gg/pollinations-ai-885844321461485618)
|
|
162
162
|
- **OpenCode Ecosystem**: [opencode.ai](https://opencode.ai/docs/ecosystem#plugins)
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import * as http from 'http';
|
|
2
2
|
import * as fs from 'fs';
|
|
3
3
|
import { generatePollinationsConfig } from './server/generate-config.js';
|
|
4
|
-
import { loadConfig
|
|
4
|
+
import { loadConfig } from './server/config.js';
|
|
5
5
|
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 { createToolRegistry } from './tools/index.js';
|
|
9
10
|
import { createRequire } from 'module';
|
|
10
11
|
const require = createRequire(import.meta.url);
|
|
11
12
|
const LOG_FILE = '/tmp/opencode_pollinations_v4.log';
|
|
@@ -15,17 +16,12 @@ function log(msg) {
|
|
|
15
16
|
}
|
|
16
17
|
catch (e) { }
|
|
17
18
|
}
|
|
18
|
-
//
|
|
19
|
-
const DEFAULT_PORT = 18888;
|
|
20
|
-
const GLOBAL_SERVER_KEY = '__POLLINATIONS_PROXY_SERVER__';
|
|
19
|
+
// Port killing removed: Using dynamic ports.
|
|
21
20
|
const startProxy = () => {
|
|
22
|
-
// Check if server exists in global scope (survives module reloads)
|
|
23
|
-
if (global[GLOBAL_SERVER_KEY]) {
|
|
24
|
-
log(`[Proxy] Reusing existing global server on port ${DEFAULT_PORT}`);
|
|
25
|
-
return Promise.resolve(DEFAULT_PORT);
|
|
26
|
-
}
|
|
27
21
|
return new Promise((resolve) => {
|
|
28
22
|
const server = http.createServer(async (req, res) => {
|
|
23
|
+
// ... (Request Handling) ...
|
|
24
|
+
// We reuse the existing logic structure but simplified startup
|
|
29
25
|
log(`[Proxy] Request: ${req.method} ${req.url}`);
|
|
30
26
|
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
31
27
|
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
|
@@ -67,188 +63,37 @@ const startProxy = () => {
|
|
|
67
63
|
res.writeHead(404);
|
|
68
64
|
res.end("Not Found");
|
|
69
65
|
});
|
|
70
|
-
//
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
log(`[Proxy] Port ${port} in use, falling back to dynamic port`);
|
|
82
|
-
server.removeAllListeners('error');
|
|
83
|
-
tryListen(0, false); // Try dynamic port
|
|
84
|
-
}
|
|
85
|
-
else {
|
|
86
|
-
log(`[Proxy] Fatal Error: ${e}`);
|
|
87
|
-
resolve(0);
|
|
88
|
-
}
|
|
89
|
-
});
|
|
90
|
-
};
|
|
91
|
-
tryListen(DEFAULT_PORT, true);
|
|
66
|
+
// Listen on random port (0) to avoid conflicts (CLI/IDE)
|
|
67
|
+
server.listen(0, '127.0.0.1', () => {
|
|
68
|
+
// @ts-ignore
|
|
69
|
+
const assignedPort = server.address().port;
|
|
70
|
+
log(`[Proxy] Started v${require('../package.json').version} (Dynamic Port) on port ${assignedPort}`);
|
|
71
|
+
resolve(assignedPort);
|
|
72
|
+
});
|
|
73
|
+
server.on('error', (e) => {
|
|
74
|
+
log(`[Proxy] Fatal Error: ${e}`);
|
|
75
|
+
resolve(0);
|
|
76
|
+
});
|
|
92
77
|
});
|
|
93
78
|
};
|
|
94
|
-
// === AUTH HOOK: Native /connect Integration ===
|
|
95
|
-
const createAuthHook = () => ({
|
|
96
|
-
provider: 'pollinations',
|
|
97
|
-
// LOADER: Called by OpenCode when it needs credentials
|
|
98
|
-
// This enables HOT RELOAD - called before each request that needs auth
|
|
99
|
-
loader: async (auth, provider) => {
|
|
100
|
-
log('[AuthHook] loader() called - fetching credentials');
|
|
101
|
-
try {
|
|
102
|
-
const authData = await auth();
|
|
103
|
-
if (authData && 'key' in authData && authData.key) {
|
|
104
|
-
log(`[AuthHook] Got key from OpenCode auth: ${authData.key.substring(0, 8)}...`);
|
|
105
|
-
// Sync to our config for other parts of the plugin
|
|
106
|
-
saveConfig({ apiKey: authData.key });
|
|
107
|
-
return { apiKey: authData.key };
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
catch (e) {
|
|
111
|
-
log(`[AuthHook] loader() error: ${e}`);
|
|
112
|
-
}
|
|
113
|
-
// Fallback to our own config
|
|
114
|
-
const config = loadConfig();
|
|
115
|
-
if (config.apiKey) {
|
|
116
|
-
log(`[AuthHook] Using key from plugin config: ${config.apiKey.substring(0, 8)}...`);
|
|
117
|
-
return { apiKey: config.apiKey };
|
|
118
|
-
}
|
|
119
|
-
log('[AuthHook] No API key available');
|
|
120
|
-
return {};
|
|
121
|
-
},
|
|
122
|
-
// METHODS: Define how user can authenticate
|
|
123
|
-
methods: [{
|
|
124
|
-
type: 'api',
|
|
125
|
-
label: 'API Key',
|
|
126
|
-
prompts: [{
|
|
127
|
-
type: 'text',
|
|
128
|
-
key: 'apiKey',
|
|
129
|
-
message: 'Enter your Pollinations API Key',
|
|
130
|
-
placeholder: 'sk_...',
|
|
131
|
-
validate: (value) => {
|
|
132
|
-
if (!value || value.length < 10) {
|
|
133
|
-
return 'API key must be at least 10 characters';
|
|
134
|
-
}
|
|
135
|
-
if (!value.startsWith('sk_') && !value.startsWith('sk-')) {
|
|
136
|
-
return 'API key should start with sk_ or sk-';
|
|
137
|
-
}
|
|
138
|
-
return undefined; // Valid
|
|
139
|
-
}
|
|
140
|
-
}],
|
|
141
|
-
authorize: async (inputs) => {
|
|
142
|
-
log(`[AuthHook] authorize() called with key: ${inputs?.apiKey?.substring(0, 8)}...`);
|
|
143
|
-
if (!inputs?.apiKey) {
|
|
144
|
-
return { type: 'failed' };
|
|
145
|
-
}
|
|
146
|
-
// Validate key by testing API
|
|
147
|
-
try {
|
|
148
|
-
const response = await fetch('https://gen.pollinations.ai/text/models', {
|
|
149
|
-
headers: { 'Authorization': `Bearer ${inputs.apiKey}` }
|
|
150
|
-
});
|
|
151
|
-
if (response.ok) {
|
|
152
|
-
log('[AuthHook] Key validated successfully');
|
|
153
|
-
// Save to our config for immediate use
|
|
154
|
-
saveConfig({ apiKey: inputs.apiKey });
|
|
155
|
-
return { type: 'success', key: inputs.apiKey };
|
|
156
|
-
}
|
|
157
|
-
else {
|
|
158
|
-
log(`[AuthHook] Key validation failed: ${response.status}`);
|
|
159
|
-
return { type: 'failed' };
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
catch (e) {
|
|
163
|
-
log(`[AuthHook] Key validation error: ${e}`);
|
|
164
|
-
return { type: 'failed' };
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
}]
|
|
168
|
-
});
|
|
169
79
|
// === PLUGIN EXPORT ===
|
|
170
80
|
export const PollinationsPlugin = async (ctx) => {
|
|
171
81
|
log(`Plugin Initializing v${require('../package.json').version}...`);
|
|
172
|
-
// START PROXY
|
|
82
|
+
// START PROXY
|
|
173
83
|
const port = await startProxy();
|
|
174
84
|
const localBaseUrl = `http://127.0.0.1:${port}/v1`;
|
|
175
85
|
setGlobalClient(ctx.client);
|
|
176
86
|
const toastHooks = createToastHooks(ctx.client);
|
|
177
87
|
const commandHooks = createCommandHooks();
|
|
178
|
-
//
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
if (isRefreshing)
|
|
182
|
-
return;
|
|
183
|
-
isRefreshing = true;
|
|
184
|
-
try {
|
|
185
|
-
log('[Event] Refreshing provider config after auth update...');
|
|
186
|
-
const modelsArray = await generatePollinationsConfig();
|
|
187
|
-
const modelsObj = {};
|
|
188
|
-
for (const m of modelsArray) {
|
|
189
|
-
modelsObj[m.id] = m;
|
|
190
|
-
}
|
|
191
|
-
const version = require('../package.json').version;
|
|
192
|
-
// CRITICAL: Fetch current config first to avoid overwriting other providers
|
|
193
|
-
let currentConfig = {};
|
|
194
|
-
try {
|
|
195
|
-
// Try to fetch existing config to preserve other providers
|
|
196
|
-
const response = await ctx.client.fetch('/config');
|
|
197
|
-
if (response.ok) {
|
|
198
|
-
currentConfig = await response.json();
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
catch (err) {
|
|
202
|
-
log(`[Event] Warning: Could not fetch current config: ${err}`);
|
|
203
|
-
}
|
|
204
|
-
// Safe Merge
|
|
205
|
-
if (!currentConfig.provider)
|
|
206
|
-
currentConfig.provider = {};
|
|
207
|
-
currentConfig.provider.pollinations = {
|
|
208
|
-
id: 'openai',
|
|
209
|
-
name: `Pollinations AI (v${version})`,
|
|
210
|
-
options: {
|
|
211
|
-
baseURL: localBaseUrl,
|
|
212
|
-
apiKey: 'plugin-managed',
|
|
213
|
-
},
|
|
214
|
-
models: modelsObj
|
|
215
|
-
};
|
|
216
|
-
// Use Server API to update config with the MERGED object
|
|
217
|
-
await ctx.client.fetch('/config', {
|
|
218
|
-
method: 'PATCH',
|
|
219
|
-
headers: { 'Content-Type': 'application/json' },
|
|
220
|
-
body: JSON.stringify({
|
|
221
|
-
provider: currentConfig.provider
|
|
222
|
-
})
|
|
223
|
-
});
|
|
224
|
-
log(`[Event] Provider config refreshed with ${Object.keys(modelsObj).length} models.`);
|
|
225
|
-
}
|
|
226
|
-
catch (e) {
|
|
227
|
-
log(`[Event] Failed to refresh provider config: ${e}`);
|
|
228
|
-
}
|
|
229
|
-
finally {
|
|
230
|
-
// Debounce: prevent another refresh for 5 seconds
|
|
231
|
-
setTimeout(() => { isRefreshing = false; }, 5000);
|
|
232
|
-
}
|
|
233
|
-
};
|
|
88
|
+
// Build tool registry (conditional on API key presence)
|
|
89
|
+
const toolRegistry = createToolRegistry();
|
|
90
|
+
log(`[Tools] ${Object.keys(toolRegistry).length} tools registered`);
|
|
234
91
|
return {
|
|
235
|
-
|
|
236
|
-
auth: createAuthHook(),
|
|
237
|
-
// EVENT HOOK: Listen ONLY for explicit /connect command
|
|
238
|
-
// We removed installation.updated/server.connected because they caused infinite loops
|
|
239
|
-
event: async ({ event }) => {
|
|
240
|
-
if (event.type === 'tui.command.execute') {
|
|
241
|
-
const cmd = event.command || event.data?.command;
|
|
242
|
-
if (cmd === 'connect' || cmd === '/connect') {
|
|
243
|
-
log('[Event] /connect command detected - will refresh after auth hook completes');
|
|
244
|
-
// Small delay to let auth hook complete
|
|
245
|
-
setTimeout(() => refreshProviderConfig(), 2000);
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
},
|
|
92
|
+
tool: toolRegistry,
|
|
249
93
|
async config(config) {
|
|
250
94
|
log("[Hook] config() called");
|
|
251
|
-
//
|
|
95
|
+
// STARTUP only - No complex hot reload logic
|
|
96
|
+
// The user must restart OpenCode to refresh this list if they change keys.
|
|
252
97
|
const modelsArray = await generatePollinationsConfig();
|
|
253
98
|
const modelsObj = {};
|
|
254
99
|
for (const m of modelsArray) {
|
|
@@ -256,14 +101,12 @@ export const PollinationsPlugin = async (ctx) => {
|
|
|
256
101
|
}
|
|
257
102
|
if (!config.provider)
|
|
258
103
|
config.provider = {};
|
|
104
|
+
// Dynamic Provider Name
|
|
259
105
|
const version = require('../package.json').version;
|
|
260
106
|
config.provider['pollinations'] = {
|
|
261
|
-
id: '
|
|
107
|
+
id: 'pollinations',
|
|
262
108
|
name: `Pollinations AI (v${version})`,
|
|
263
|
-
options: {
|
|
264
|
-
baseURL: localBaseUrl,
|
|
265
|
-
apiKey: 'plugin-managed', // Key is managed by auth hook
|
|
266
|
-
},
|
|
109
|
+
options: { baseURL: localBaseUrl },
|
|
267
110
|
models: modelsObj
|
|
268
111
|
};
|
|
269
112
|
log(`[Hook] Registered ${Object.keys(modelsObj).length} models.`);
|
package/dist/server/commands.js
CHANGED
|
@@ -303,15 +303,15 @@ async function handleConnectCommand(args) {
|
|
|
303
303
|
// 1. Universal Validation (No Syntax Check) - Functional Check
|
|
304
304
|
emitStatusToast('info', 'Vérification de la clé...', 'Pollinations Config');
|
|
305
305
|
try {
|
|
306
|
-
const models = await generatePollinationsConfig(key);
|
|
307
|
-
// 2. Check if we got
|
|
308
|
-
const
|
|
309
|
-
if (
|
|
306
|
+
const models = await generatePollinationsConfig(key, true);
|
|
307
|
+
// 2. Check if we got Enterprise models
|
|
308
|
+
const enterpriseModels = models.filter(m => m.id.startsWith('enter/'));
|
|
309
|
+
if (enterpriseModels.length > 0) {
|
|
310
310
|
// SUCCESS
|
|
311
311
|
saveConfig({ apiKey: key }); // Don't force mode 'pro'. Let user decide.
|
|
312
312
|
const masked = key.substring(0, 6) + '...';
|
|
313
313
|
// Count Paid Only models found
|
|
314
|
-
const diamondCount =
|
|
314
|
+
const diamondCount = enterpriseModels.filter(m => m.name.includes('💎')).length;
|
|
315
315
|
// CHECK RESTRICTIONS: Strict Check (Usage + Profile + Balance)
|
|
316
316
|
let forcedModeMsg = "";
|
|
317
317
|
let isLimited = false;
|
|
@@ -336,10 +336,10 @@ async function handleConnectCommand(args) {
|
|
|
336
336
|
else {
|
|
337
337
|
saveConfig({ apiKey: key, keyHasAccessToProfile: true }); // Let user keep current mode or default
|
|
338
338
|
}
|
|
339
|
-
emitStatusToast('success', `Clé Valide! (${
|
|
339
|
+
emitStatusToast('success', `Clé Valide! (${enterpriseModels.length} modèles Pro débloqués)`, 'Pollinations Config');
|
|
340
340
|
return {
|
|
341
341
|
handled: true,
|
|
342
|
-
response: `✅ **Connexion Réussie!**\n- Clé: \`${masked}\`\n- Modèles Débloqués: ${
|
|
342
|
+
response: `✅ **Connexion Réussie!**\n- Clé: \`${masked}\`\n- Modèles Débloqués: ${enterpriseModels.length} (dont ${diamondCount} 💎 Paid)${forcedModeMsg}`
|
|
343
343
|
};
|
|
344
344
|
}
|
|
345
345
|
else {
|
|
@@ -351,8 +351,18 @@ async function handleConnectCommand(args) {
|
|
|
351
351
|
// Wait, generate-config falls back to providing a list containing "[Enter] GPT-4o (Fallback)" if fetch failed.
|
|
352
352
|
// So we need to detect if it's a "REAL" fetch or a "FALLBACK" fetch.
|
|
353
353
|
// The fallback models have `variants: {}` usually, but real ones might too.
|
|
354
|
-
//
|
|
355
|
-
|
|
354
|
+
// A better check: The fallback list is hardcoded in generate-config.ts catch block.
|
|
355
|
+
// Let's modify generate-config to return EMPTY list on error?
|
|
356
|
+
// Or just check if the returned models work?
|
|
357
|
+
// Simplest: If `generatePollinationsConfig` returns any model starting with `enter/` that includes "(Fallback)" in name, we assume failure?
|
|
358
|
+
// "GPT-4o (Fallback)" is the name.
|
|
359
|
+
const isFallback = models.some(m => m.name.includes('(Fallback)') && m.id.startsWith('enter/'));
|
|
360
|
+
if (isFallback) {
|
|
361
|
+
throw new Error("Clé rejetée par l'API (Accès refusé ou invalide).");
|
|
362
|
+
}
|
|
363
|
+
// If we are here, we got no enter models, or empty list?
|
|
364
|
+
// If key is valid but has no access?
|
|
365
|
+
throw new Error("Aucun modèle Enterprise détecté pour cette clé.");
|
|
356
366
|
}
|
|
357
367
|
}
|
|
358
368
|
catch (e) {
|
|
@@ -1,34 +1,8 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* generate-config.ts - v6.0 Simplified
|
|
3
|
-
*
|
|
4
|
-
* Single endpoint: gen.pollinations.ai/text/models
|
|
5
|
-
* No more Free tier, no cache ETag, no prefixes
|
|
6
|
-
*/
|
|
7
|
-
export interface PollinationsModel {
|
|
8
|
-
name: string;
|
|
9
|
-
description?: string;
|
|
10
|
-
type?: string;
|
|
11
|
-
tools?: boolean;
|
|
12
|
-
reasoning?: boolean;
|
|
13
|
-
context?: number;
|
|
14
|
-
context_window?: number;
|
|
15
|
-
input_modalities?: string[];
|
|
16
|
-
output_modalities?: string[];
|
|
17
|
-
paid_only?: boolean;
|
|
18
|
-
vision?: boolean;
|
|
19
|
-
audio?: boolean;
|
|
20
|
-
pricing?: {
|
|
21
|
-
promptTextTokens?: number;
|
|
22
|
-
completionTextTokens?: number;
|
|
23
|
-
promptImageTokens?: number;
|
|
24
|
-
promptAudioTokens?: number;
|
|
25
|
-
completionAudioTokens?: number;
|
|
26
|
-
};
|
|
27
|
-
[key: string]: any;
|
|
28
|
-
}
|
|
29
1
|
interface OpenCodeModel {
|
|
30
2
|
id: string;
|
|
31
3
|
name: string;
|
|
4
|
+
object: string;
|
|
5
|
+
variants?: any;
|
|
32
6
|
options?: any;
|
|
33
7
|
limit?: {
|
|
34
8
|
context?: number;
|
|
@@ -38,7 +12,6 @@ interface OpenCodeModel {
|
|
|
38
12
|
input?: string[];
|
|
39
13
|
output?: string[];
|
|
40
14
|
};
|
|
41
|
-
tool_call?: boolean;
|
|
42
15
|
}
|
|
43
|
-
export declare function generatePollinationsConfig(forceApiKey?: string): Promise<OpenCodeModel[]>;
|
|
16
|
+
export declare function generatePollinationsConfig(forceApiKey?: string, forceStrict?: boolean): Promise<OpenCodeModel[]>;
|
|
44
17
|
export {};
|