opencode-pollinations-plugin 5.8.3 → 5.8.4-beta.1
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/server/generate-config.js +48 -17
- package/package.json +2 -2
|
@@ -28,23 +28,39 @@ function log(msg) {
|
|
|
28
28
|
catch (e) { }
|
|
29
29
|
}
|
|
30
30
|
// --- NETWORK HELPER ---
|
|
31
|
+
function fetchHead(url) {
|
|
32
|
+
return new Promise((resolve) => {
|
|
33
|
+
// Use Node.js native https check for minimal overhead
|
|
34
|
+
const req = https.request(url, { method: 'HEAD', timeout: 5000 }, (res) => {
|
|
35
|
+
resolve(res.headers['etag'] || null);
|
|
36
|
+
});
|
|
37
|
+
req.on('error', () => resolve(null));
|
|
38
|
+
req.on('timeout', () => { req.destroy(); resolve(null); });
|
|
39
|
+
req.end();
|
|
40
|
+
});
|
|
41
|
+
}
|
|
31
42
|
function fetchJson(url, headers = {}) {
|
|
32
43
|
return new Promise((resolve, reject) => {
|
|
33
44
|
const finalHeaders = {
|
|
34
45
|
...headers,
|
|
35
|
-
'User-Agent': 'Mozilla/5.0 (compatible; OpenCode/5.8.
|
|
46
|
+
'User-Agent': 'Mozilla/5.0 (compatible; OpenCode/5.8.4; +https://opencode.ai)'
|
|
36
47
|
};
|
|
37
48
|
const req = https.get(url, { headers: finalHeaders }, (res) => {
|
|
49
|
+
const etag = res.headers['etag'];
|
|
38
50
|
let data = '';
|
|
39
51
|
res.on('data', chunk => data += chunk);
|
|
40
52
|
res.on('end', () => {
|
|
41
53
|
try {
|
|
42
54
|
const json = JSON.parse(data);
|
|
55
|
+
// HACK: Attach ETag to the object to pass it up
|
|
56
|
+
if (etag && typeof json === 'object') {
|
|
57
|
+
Object.defineProperty(json, '_etag', { value: etag, enumerable: false, writable: true });
|
|
58
|
+
}
|
|
43
59
|
resolve(json);
|
|
44
60
|
}
|
|
45
61
|
catch (e) {
|
|
46
62
|
log(`JSON Parse Error for ${url}: ${e}`);
|
|
47
|
-
resolve([]); // Fail safe
|
|
63
|
+
resolve([]); // Fail safe
|
|
48
64
|
}
|
|
49
65
|
});
|
|
50
66
|
});
|
|
@@ -58,7 +74,6 @@ function fetchJson(url, headers = {}) {
|
|
|
58
74
|
});
|
|
59
75
|
});
|
|
60
76
|
}
|
|
61
|
-
// --- CACHE MANAGER ---
|
|
62
77
|
function loadCache() {
|
|
63
78
|
try {
|
|
64
79
|
if (fs.existsSync(CACHE_FILE)) {
|
|
@@ -71,10 +86,11 @@ function loadCache() {
|
|
|
71
86
|
}
|
|
72
87
|
return null;
|
|
73
88
|
}
|
|
74
|
-
function saveCache(models) {
|
|
89
|
+
function saveCache(models, etag) {
|
|
75
90
|
try {
|
|
76
91
|
const data = {
|
|
77
92
|
timestamp: Date.now(),
|
|
93
|
+
etag: etag,
|
|
78
94
|
models: models
|
|
79
95
|
};
|
|
80
96
|
if (!fs.existsSync(CONFIG_DIR_POLLI))
|
|
@@ -89,25 +105,43 @@ function saveCache(models) {
|
|
|
89
105
|
export async function generatePollinationsConfig(forceApiKey, forceStrict = false) {
|
|
90
106
|
const config = loadConfig();
|
|
91
107
|
const modelsOutput = [];
|
|
92
|
-
log(`Starting Configuration (v5.8.
|
|
108
|
+
log(`Starting Configuration (v5.8.4-Beta1)...`);
|
|
93
109
|
const effectiveKey = forceApiKey || config.apiKey;
|
|
94
|
-
// 1. FREE UNIVERSE (Cache System)
|
|
110
|
+
// 1. FREE UNIVERSE (Smart Cache System)
|
|
95
111
|
let freeModelsList = [];
|
|
96
112
|
let isOffline = false;
|
|
97
113
|
let cache = loadCache();
|
|
98
114
|
const CACHE_TTL = 7 * 24 * 3600 * 1000; // 7 days
|
|
99
|
-
// Decision
|
|
115
|
+
// Decision Logic
|
|
100
116
|
const now = Date.now();
|
|
101
117
|
let shouldFetch = !cache || (now - cache.timestamp > CACHE_TTL);
|
|
118
|
+
// ETag Check: If cache is valid but we want to be proactive
|
|
119
|
+
if (!shouldFetch && cache && cache.etag) {
|
|
120
|
+
try {
|
|
121
|
+
log('Smart Refresh: Checking for updates (HEAD)...');
|
|
122
|
+
const remoteEtag = await fetchHead('https://text.pollinations.ai/models');
|
|
123
|
+
if (remoteEtag && remoteEtag !== cache.etag) {
|
|
124
|
+
log(`Update Detected! (Remote: ${remoteEtag} != Local: ${cache.etag}). Forcing refresh.`);
|
|
125
|
+
shouldFetch = true;
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
log('Cache is clean (ETag match). No refresh needed.');
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
catch (e) {
|
|
132
|
+
log(`Smart Refresh check failed: ${e}. Ignoring.`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
102
135
|
if (shouldFetch) {
|
|
103
|
-
log('
|
|
136
|
+
log('Fetching fresh Free models...');
|
|
104
137
|
try {
|
|
105
138
|
const raw = await fetchJson('https://text.pollinations.ai/models');
|
|
106
139
|
const list = Array.isArray(raw) ? raw : (raw.data || []);
|
|
140
|
+
const newEtag = raw._etag; // Get hidden ETag
|
|
107
141
|
if (list.length > 0) {
|
|
108
142
|
freeModelsList = list;
|
|
109
|
-
saveCache(list);
|
|
110
|
-
log(`Fetched and cached ${list.length} models.`);
|
|
143
|
+
saveCache(list, newEtag);
|
|
144
|
+
log(`Fetched and cached ${list.length} models (ETag: ${newEtag || 'N/A'}).`);
|
|
111
145
|
}
|
|
112
146
|
else {
|
|
113
147
|
throw new Error('API returned empty list');
|
|
@@ -116,7 +150,7 @@ export async function generatePollinationsConfig(forceApiKey, forceStrict = fals
|
|
|
116
150
|
catch (e) {
|
|
117
151
|
log(`Fetch failed: ${e}.`);
|
|
118
152
|
isOffline = true;
|
|
119
|
-
// Fallback
|
|
153
|
+
// Fallback Logic
|
|
120
154
|
if (cache && cache.models.length > 0) {
|
|
121
155
|
log('Using cached models (Offline).');
|
|
122
156
|
freeModelsList = cache.models;
|
|
@@ -128,15 +162,13 @@ export async function generatePollinationsConfig(forceApiKey, forceStrict = fals
|
|
|
128
162
|
}
|
|
129
163
|
}
|
|
130
164
|
else {
|
|
131
|
-
log('
|
|
165
|
+
log('Using cached models (Skipped fetch).');
|
|
132
166
|
freeModelsList = cache.models;
|
|
133
167
|
}
|
|
134
168
|
// Map Free Models
|
|
135
169
|
freeModelsList.forEach((m) => {
|
|
136
|
-
//
|
|
137
|
-
//
|
|
138
|
-
// when valid list is reached but date > 8 days (deprecated) or fallback used?
|
|
139
|
-
// Let's mark it only if we tried to fetch and failed.
|
|
170
|
+
// Tag (Offline) only if we explicitly failed a fetch attempt or are using Fallback SEED when fetch failed.
|
|
171
|
+
// If we use cache because it's valid (Skipped fetch), we don't tag (Offline).
|
|
140
172
|
const suffix = isOffline ? ' (Offline)' : '';
|
|
141
173
|
const mapped = mapModel(m, 'free/', `[Free] `, suffix);
|
|
142
174
|
modelsOutput.push(mapped);
|
|
@@ -160,7 +192,6 @@ export async function generatePollinationsConfig(forceApiKey, forceStrict = fals
|
|
|
160
192
|
log(`Error fetching Enterprise models: ${e}`);
|
|
161
193
|
if (forceStrict)
|
|
162
194
|
throw e;
|
|
163
|
-
// Fallback Enter (could be cached too in future)
|
|
164
195
|
modelsOutput.push({ id: "enter/gpt-4o", name: "[Enter] GPT-4o (Fallback)", object: "model", variants: {} });
|
|
165
196
|
}
|
|
166
197
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-pollinations-plugin",
|
|
3
3
|
"displayName": "Pollinations AI (V5.6)",
|
|
4
|
-
"version": "5.8.
|
|
4
|
+
"version": "5.8.4-beta.1",
|
|
5
5
|
"description": "Native Pollinations.ai Provider Plugin for OpenCode",
|
|
6
6
|
"publisher": "pollinations",
|
|
7
7
|
"repository": {
|
|
@@ -55,4 +55,4 @@
|
|
|
55
55
|
"@types/node": "^20.0.0",
|
|
56
56
|
"typescript": "^5.0.0"
|
|
57
57
|
}
|
|
58
|
-
}
|
|
58
|
+
}
|