opencode-pollinations-plugin 5.1.23 → 5.2.0
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 +10 -11
- package/dist/index.js +11 -30
- package/dist/server/config.d.ts +0 -4
- package/dist/server/config.js +14 -106
- package/dist/server/index.js +1 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -116,17 +116,16 @@ Just type in the chat. You are in **Manual Mode** by default.
|
|
|
116
116
|
- Model: `openai` (GPT-4o Mini equivalent)
|
|
117
117
|
- Model: `mistral` (Mistral Nemo)
|
|
118
118
|
|
|
119
|
-
###
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
4. **Reload Window**.
|
|
119
|
+
### 🔑 Configuration (API Key)
|
|
120
|
+
|
|
121
|
+
1. Run the setup command:
|
|
122
|
+
```bash
|
|
123
|
+
/connect
|
|
124
|
+
```
|
|
125
|
+
2. Choose "pollinations" and enter your key if you have one (or leave blank for free tier).
|
|
126
|
+
3. **IMPORTANT**: You must **restart OpenCode** for the model list to update with your new tier (e.g. to see Paid models).
|
|
127
|
+
|
|
128
|
+
### 🤖 Models
|
|
130
129
|
|
|
131
130
|
## 🔗 Links
|
|
132
131
|
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@ 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
7
|
import { createToastHooks, setGlobalClient } from './server/toast.js';
|
|
8
8
|
import { createStatusHooks } from './server/status.js';
|
|
@@ -42,13 +42,11 @@ const startProxy = () => {
|
|
|
42
42
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
43
43
|
res.end(JSON.stringify({
|
|
44
44
|
status: "ok",
|
|
45
|
-
version: "
|
|
45
|
+
version: "v5.2.0",
|
|
46
46
|
mode: config.mode
|
|
47
47
|
}));
|
|
48
48
|
return;
|
|
49
49
|
}
|
|
50
|
-
// SUPPORT FLEXIBLE DES PATHS
|
|
51
|
-
// Le SDK peut envoyer /v1/chat/completions ou juste /chat/completions
|
|
52
50
|
if (req.method === 'POST' && (req.url === '/v1/chat/completions' || req.url === '/chat/completions')) {
|
|
53
51
|
const chunks = [];
|
|
54
52
|
req.on('data', chunk => chunks.push(chunk));
|
|
@@ -72,7 +70,7 @@ const startProxy = () => {
|
|
|
72
70
|
res.end("Not Found");
|
|
73
71
|
});
|
|
74
72
|
server.listen(TRACKING_PORT, '127.0.0.1', () => {
|
|
75
|
-
log(`[Proxy] Started
|
|
73
|
+
log(`[Proxy] Started V5.2 on port ${TRACKING_PORT}`);
|
|
76
74
|
resolve(TRACKING_PORT);
|
|
77
75
|
});
|
|
78
76
|
server.on('error', (e) => {
|
|
@@ -83,52 +81,35 @@ const startProxy = () => {
|
|
|
83
81
|
};
|
|
84
82
|
// === PLUGIN EXPORT ===
|
|
85
83
|
export const PollinationsPlugin = async (ctx) => {
|
|
86
|
-
log("Plugin Initializing
|
|
87
|
-
|
|
88
|
-
log(`[DEBUG] CTX Keys: ${Object.keys(ctx).join(', ')}`);
|
|
89
|
-
}
|
|
90
|
-
catch (e) {
|
|
91
|
-
log(`[DEBUG] Error inspecting ctx: ${e}`);
|
|
92
|
-
}
|
|
84
|
+
log("Plugin Initializing V5.2.0 (Stable)...");
|
|
85
|
+
// START PROXY
|
|
93
86
|
const port = await startProxy();
|
|
94
|
-
// IMPORTANT: On ajoute /v1 à la base URL pour guider le SDK,
|
|
95
|
-
// mais le proxy accepte aussi sans.
|
|
96
87
|
const localBaseUrl = `http://127.0.0.1:${port}/v1`;
|
|
97
|
-
setGlobalClient(ctx.client);
|
|
88
|
+
setGlobalClient(ctx.client);
|
|
98
89
|
const toastHooks = createToastHooks(ctx.client);
|
|
99
90
|
const commandHooks = createCommandHooks();
|
|
100
91
|
return {
|
|
101
92
|
async config(config) {
|
|
102
93
|
log("[Hook] config() called");
|
|
103
|
-
//
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
if (incomingKey) {
|
|
107
|
-
log(`[Hook] Detected API Key update.`);
|
|
108
|
-
updateCache({ apiKey: incomingKey, mode: 'pro' });
|
|
109
|
-
}
|
|
110
|
-
const modelsArray = await generatePollinationsConfig(incomingKey);
|
|
94
|
+
// STARTUP only - No complex hot reload logic
|
|
95
|
+
// The user must restart OpenCode to refresh this list if they change keys.
|
|
96
|
+
const modelsArray = await generatePollinationsConfig();
|
|
111
97
|
const modelsObj = {};
|
|
112
98
|
for (const m of modelsArray) {
|
|
113
|
-
// Ensure ID is relative for mapping ("free/gemini")
|
|
114
|
-
// BUT Provider needs full ID ? No, the object key is the relative ID
|
|
115
|
-
// OpenCode: provider.models[id]
|
|
116
|
-
// id comes from generatePollinationsConfig which returns "free/gemini"
|
|
117
|
-
// So modelsObj["free/gemini"] = ... matches.
|
|
118
99
|
modelsObj[m.id] = m;
|
|
119
100
|
}
|
|
120
101
|
if (!config.provider)
|
|
121
102
|
config.provider = {};
|
|
122
103
|
config.provider['pollinations'] = {
|
|
123
104
|
id: 'pollinations',
|
|
124
|
-
name: 'Pollinations V5.
|
|
105
|
+
name: 'Pollinations V5.2 (Native)',
|
|
125
106
|
options: { baseURL: localBaseUrl },
|
|
126
107
|
models: modelsObj
|
|
127
108
|
};
|
|
128
109
|
log(`[Hook] Registered ${Object.keys(modelsObj).length} models.`);
|
|
129
110
|
},
|
|
130
111
|
...toastHooks,
|
|
131
|
-
...createStatusHooks(ctx.client),
|
|
112
|
+
...createStatusHooks(ctx.client),
|
|
132
113
|
...commandHooks
|
|
133
114
|
};
|
|
134
115
|
};
|
package/dist/server/config.d.ts
CHANGED
|
@@ -21,11 +21,8 @@ export interface PollinationsConfigV5 {
|
|
|
21
21
|
};
|
|
22
22
|
enablePaidTools: boolean;
|
|
23
23
|
statusBar: boolean;
|
|
24
|
-
_loadedAt?: number;
|
|
25
24
|
}
|
|
26
|
-
export declare function subscribeToConfigChange(callback: () => void): void;
|
|
27
25
|
export declare function loadConfig(): PollinationsConfigV5;
|
|
28
|
-
export declare function updateCache(newConfig: Partial<PollinationsConfigV5>): void;
|
|
29
26
|
export declare function saveConfig(updates: Partial<PollinationsConfigV5>): {
|
|
30
27
|
version: string;
|
|
31
28
|
mode: "manual" | "alwaysfree" | "pro";
|
|
@@ -49,5 +46,4 @@ export declare function saveConfig(updates: Partial<PollinationsConfigV5>): {
|
|
|
49
46
|
};
|
|
50
47
|
enablePaidTools: boolean;
|
|
51
48
|
statusBar: boolean;
|
|
52
|
-
_loadedAt?: number;
|
|
53
49
|
};
|
package/dist/server/config.js
CHANGED
|
@@ -9,7 +9,7 @@ const CONFIG_DIR_OPENCODE = path.join(HOMEDIR, '.config', 'opencode');
|
|
|
9
9
|
const OPENCODE_CONFIG_FILE = path.join(CONFIG_DIR_OPENCODE, 'opencode.json');
|
|
10
10
|
const AUTH_FILE = path.join(HOMEDIR, '.local', 'share', 'opencode', 'auth.json');
|
|
11
11
|
// LOAD PACKAGE VERSION
|
|
12
|
-
let PKG_VERSION = '5.
|
|
12
|
+
let PKG_VERSION = '5.2.0';
|
|
13
13
|
try {
|
|
14
14
|
const pkgPath = path.join(__dirname, '../../package.json');
|
|
15
15
|
if (fs.existsSync(pkgPath)) {
|
|
@@ -21,27 +21,15 @@ catch (e) { }
|
|
|
21
21
|
const DEFAULT_CONFIG_V5 = {
|
|
22
22
|
version: PKG_VERSION,
|
|
23
23
|
mode: 'manual',
|
|
24
|
-
gui: {
|
|
25
|
-
|
|
26
|
-
logs: 'none'
|
|
27
|
-
},
|
|
28
|
-
thresholds: {
|
|
29
|
-
tier: 10, // Alert if < 10%
|
|
30
|
-
wallet: 5 // Switch if < 5% (Wallet Protection)
|
|
31
|
-
},
|
|
24
|
+
gui: { status: 'alert', logs: 'none' },
|
|
25
|
+
thresholds: { tier: 10, wallet: 5 },
|
|
32
26
|
fallbacks: {
|
|
33
|
-
free: {
|
|
34
|
-
|
|
35
|
-
agent: 'free/openai-fast' // Agent gratuit rapide
|
|
36
|
-
},
|
|
37
|
-
enter: {
|
|
38
|
-
agent: 'free/gemini' // Agent de secours (Free Gemini)
|
|
39
|
-
}
|
|
27
|
+
free: { main: 'free/mistral', agent: 'free/openai-fast' },
|
|
28
|
+
enter: { agent: 'free/gemini' }
|
|
40
29
|
},
|
|
41
30
|
enablePaidTools: false,
|
|
42
31
|
statusBar: true
|
|
43
32
|
};
|
|
44
|
-
// Debug Helper
|
|
45
33
|
function logConfig(msg) {
|
|
46
34
|
try {
|
|
47
35
|
if (!fs.existsSync('/tmp/opencode_pollinations_config_debug.log')) {
|
|
@@ -51,95 +39,20 @@ function logConfig(msg) {
|
|
|
51
39
|
}
|
|
52
40
|
catch (e) { }
|
|
53
41
|
}
|
|
54
|
-
//
|
|
55
|
-
|
|
56
|
-
const listeners = [];
|
|
57
|
-
export function subscribeToConfigChange(callback) {
|
|
58
|
-
listeners.push(callback);
|
|
59
|
-
}
|
|
60
|
-
function notifyListeners() {
|
|
61
|
-
listeners.forEach(cb => {
|
|
62
|
-
try {
|
|
63
|
-
cb();
|
|
64
|
-
}
|
|
65
|
-
catch (e) {
|
|
66
|
-
logConfig(`Listener Error: ${e}`);
|
|
67
|
-
}
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
// Watchers (Debounced)
|
|
71
|
-
const watchedFiles = new Set();
|
|
72
|
-
function watchFileSafe(filePath) {
|
|
73
|
-
if (watchedFiles.has(filePath))
|
|
74
|
-
return;
|
|
75
|
-
try {
|
|
76
|
-
if (!fs.existsSync(filePath))
|
|
77
|
-
return;
|
|
78
|
-
fs.watchFile(filePath, { interval: 2000 }, (curr, prev) => {
|
|
79
|
-
if (curr.mtime !== prev.mtime) {
|
|
80
|
-
logConfig(`File Changed: ${path.basename(filePath)}. Reloading Config...`);
|
|
81
|
-
cachedConfig = readConfigFromDisk();
|
|
82
|
-
notifyListeners();
|
|
83
|
-
}
|
|
84
|
-
});
|
|
85
|
-
watchedFiles.add(filePath);
|
|
86
|
-
}
|
|
87
|
-
catch (e) {
|
|
88
|
-
logConfig(`Watch Error for ${filePath}: ${e}`);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
42
|
+
// SIMPLE LOAD (Direct Disk Read - No Caching, No Watchers)
|
|
43
|
+
// This ensures the Proxy ALWAYS sees the latest state from auth.json
|
|
91
44
|
export function loadConfig() {
|
|
92
|
-
|
|
93
|
-
if (cachedConfig)
|
|
94
|
-
return cachedConfig;
|
|
95
|
-
// 2. Or Read Fresh
|
|
96
|
-
cachedConfig = readConfigFromDisk();
|
|
97
|
-
// 3. Initiate Watchers (Lazy)
|
|
98
|
-
watchFileSafe(CONFIG_FILE);
|
|
99
|
-
watchFileSafe(AUTH_FILE);
|
|
100
|
-
watchFileSafe(OPENCODE_CONFIG_FILE);
|
|
101
|
-
// SMART CACHE: Check mtime to ensure freshness (Hot Reload Fix)
|
|
102
|
-
try {
|
|
103
|
-
if (fs.existsSync(AUTH_FILE)) {
|
|
104
|
-
const stats = fs.statSync(AUTH_FILE);
|
|
105
|
-
// If cache is null or file is newer than our last load
|
|
106
|
-
if (!cachedConfig || !cachedConfig._loadedAt || stats.mtimeMs > cachedConfig._loadedAt) {
|
|
107
|
-
logConfig(`[SmartCache] Auth file changed (mtime). Reloading...`);
|
|
108
|
-
cachedConfig = readConfigFromDisk();
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
catch (e) {
|
|
113
|
-
logConfig(`[SmartCache] Error checking mtime: ${e}`);
|
|
114
|
-
}
|
|
115
|
-
return cachedConfig;
|
|
45
|
+
return readConfigFromDisk();
|
|
116
46
|
}
|
|
117
|
-
export function updateCache(newConfig) {
|
|
118
|
-
const current = loadConfig();
|
|
119
|
-
cachedConfig = { ...current, ...newConfig, _loadedAt: Date.now() }; // Update timestamp
|
|
120
|
-
logConfig(`Cache Force-Updated via Hook. Mode: ${cachedConfig.mode}`);
|
|
121
|
-
}
|
|
122
|
-
// INTERNAL READER (The old loadConfig logic)
|
|
123
47
|
function readConfigFromDisk() {
|
|
124
48
|
let config = { ...DEFAULT_CONFIG_V5 };
|
|
125
49
|
let keyFound = false;
|
|
126
|
-
// 1.
|
|
50
|
+
// 1. Custom Config
|
|
127
51
|
try {
|
|
128
52
|
if (fs.existsSync(CONFIG_FILE)) {
|
|
129
53
|
const raw = fs.readFileSync(CONFIG_FILE, 'utf-8');
|
|
130
54
|
const custom = JSON.parse(raw);
|
|
131
|
-
|
|
132
|
-
if (!custom.version || custom.version < 5) {
|
|
133
|
-
// If old, we don't migrate inside read to avoid write-loops.
|
|
134
|
-
// We just read what we can.
|
|
135
|
-
if (custom.apiKey)
|
|
136
|
-
config.apiKey = custom.apiKey;
|
|
137
|
-
if (custom.mode)
|
|
138
|
-
config.mode = custom.mode;
|
|
139
|
-
}
|
|
140
|
-
else {
|
|
141
|
-
config = { ...config, ...custom };
|
|
142
|
-
}
|
|
55
|
+
config = { ...config, ...custom };
|
|
143
56
|
if (config.apiKey)
|
|
144
57
|
keyFound = true;
|
|
145
58
|
}
|
|
@@ -147,13 +60,12 @@ function readConfigFromDisk() {
|
|
|
147
60
|
catch (e) {
|
|
148
61
|
logConfig(`Error loading config: ${e}`);
|
|
149
62
|
}
|
|
150
|
-
// 2.
|
|
63
|
+
// 2. Auth Store (Priority)
|
|
151
64
|
if (!keyFound) {
|
|
152
65
|
try {
|
|
153
66
|
if (fs.existsSync(AUTH_FILE)) {
|
|
154
67
|
const raw = fs.readFileSync(AUTH_FILE, 'utf-8');
|
|
155
68
|
const authData = JSON.parse(raw);
|
|
156
|
-
// Supports flat key or nested object
|
|
157
69
|
const entry = authData['pollinations'] || authData['pollinations_enter'] || authData['pollinations_api_key'];
|
|
158
70
|
if (entry) {
|
|
159
71
|
const key = (typeof entry === 'object' && entry.key) ? entry.key : entry;
|
|
@@ -161,7 +73,6 @@ function readConfigFromDisk() {
|
|
|
161
73
|
config.apiKey = key;
|
|
162
74
|
config.mode = 'pro';
|
|
163
75
|
keyFound = true;
|
|
164
|
-
logConfig(`Hot-Loaded API Key from Auth Store`);
|
|
165
76
|
}
|
|
166
77
|
}
|
|
167
78
|
}
|
|
@@ -170,7 +81,7 @@ function readConfigFromDisk() {
|
|
|
170
81
|
logConfig(`Error reading auth.json: ${e}`);
|
|
171
82
|
}
|
|
172
83
|
}
|
|
173
|
-
// 3.
|
|
84
|
+
// 3. OpenCode Config (Fallback)
|
|
174
85
|
if (!keyFound) {
|
|
175
86
|
try {
|
|
176
87
|
if (fs.existsSync(OPENCODE_CONFIG_FILE)) {
|
|
@@ -187,22 +98,19 @@ function readConfigFromDisk() {
|
|
|
187
98
|
}
|
|
188
99
|
catch (e) { }
|
|
189
100
|
}
|
|
190
|
-
// Default mode correction
|
|
191
101
|
if (!keyFound && config.mode === 'pro') {
|
|
192
102
|
config.mode = 'manual';
|
|
193
103
|
}
|
|
194
|
-
return { ...config, version: PKG_VERSION
|
|
104
|
+
return { ...config, version: PKG_VERSION };
|
|
195
105
|
}
|
|
196
106
|
export function saveConfig(updates) {
|
|
197
107
|
try {
|
|
198
|
-
|
|
199
|
-
const current = readConfigFromDisk(); // Read disk for safety before write
|
|
108
|
+
const current = readConfigFromDisk();
|
|
200
109
|
const updated = { ...current, ...updates, version: PKG_VERSION };
|
|
201
110
|
if (!fs.existsSync(CONFIG_DIR_POLLI)) {
|
|
202
111
|
fs.mkdirSync(CONFIG_DIR_POLLI, { recursive: true });
|
|
203
112
|
}
|
|
204
113
|
fs.writeFileSync(CONFIG_FILE, JSON.stringify(updated, null, 2));
|
|
205
|
-
cachedConfig = updated; // Update Cache immediately
|
|
206
114
|
return updated;
|
|
207
115
|
}
|
|
208
116
|
catch (e) {
|
package/dist/server/index.js
CHANGED
|
@@ -2,18 +2,9 @@ import * as http from 'http';
|
|
|
2
2
|
import * as fs from 'fs';
|
|
3
3
|
import * as path from 'path';
|
|
4
4
|
import { getAggregatedModels } from './pollinations-api.js';
|
|
5
|
-
import { loadConfig, saveConfig
|
|
5
|
+
import { loadConfig, saveConfig } from './config.js';
|
|
6
6
|
import { handleChatCompletion } from './proxy.js';
|
|
7
|
-
import { emitStatusToast } from './toast.js';
|
|
8
7
|
const LOG_FILE = path.join(process.env.HOME || '/tmp', '.config/opencode/plugins/pollinations-v3.log');
|
|
9
|
-
// Hot Reload Listener
|
|
10
|
-
subscribeToConfigChange(() => {
|
|
11
|
-
const config = loadConfig();
|
|
12
|
-
const mode = config.mode.toUpperCase();
|
|
13
|
-
const hasKey = !!config.apiKey;
|
|
14
|
-
log(`[HOT RELOAD] Config Updated. Mode: ${mode}, HasKey: ${hasKey}`);
|
|
15
|
-
emitStatusToast('info', `Configuration Updated (Hot Reload) | Mode: ${mode}`, 'Pollinations System');
|
|
16
|
-
});
|
|
17
8
|
// Simple file logger
|
|
18
9
|
function log(msg) {
|
|
19
10
|
const ts = new Date().toISOString();
|
package/package.json
CHANGED