opencode-pollinations-plugin 6.4.9 → 6.5.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.de.md +67 -54
- package/README.es.md +79 -66
- package/README.fr.md +70 -57
- package/README.it.md +78 -65
- package/README.md +33 -29
- package/README.zh.md +77 -64
- package/dist/locales/de.json +82 -47
- package/dist/locales/en.json +84 -49
- package/dist/locales/es.json +82 -47
- package/dist/locales/fr.json +81 -46
- package/dist/locales/it.json +82 -47
- package/dist/locales/zh.json +82 -47
- package/dist/server/commands.js +118 -178
- package/dist/server/config.d.ts +22 -6
- package/dist/server/config.js +45 -5
- package/dist/server/connect-response.js +7 -7
- package/dist/server/generate-config.js +2 -2
- package/dist/server/models/cache.d.ts +23 -10
- package/dist/server/models/cache.js +46 -24
- package/dist/server/models/fetcher.js +2 -0
- package/dist/server/models/types.d.ts +2 -0
- package/dist/server/models/worker.js +4 -4
- package/dist/server/proxy.d.ts +6 -0
- package/dist/server/proxy.js +318 -218
- package/dist/server/quota.d.ts +23 -32
- package/dist/server/quota.js +44 -184
- package/dist/server/scripts/pollinations_pricing.js +6 -3
- package/dist/server/status.js +1 -2
- package/dist/server/toast.js +1 -1
- package/dist/tools/index.d.ts +2 -1
- package/dist/tools/index.js +3 -1
- package/dist/tools/pollinations/artifact-core.d.ts +53 -0
- package/dist/tools/pollinations/artifact-core.js +159 -0
- package/dist/tools/pollinations/beta_discovery.js +2 -1
- package/dist/tools/pollinations/cost-guard.d.ts +2 -2
- package/dist/tools/pollinations/error-parser.d.ts +38 -0
- package/dist/tools/pollinations/error-parser.js +112 -0
- package/dist/tools/pollinations/gen_3d.d.ts +17 -0
- package/dist/tools/pollinations/gen_3d.js +207 -0
- package/dist/tools/pollinations/gen_image.js +29 -10
- package/dist/tools/pollinations/gen_music.js +3 -2
- package/dist/tools/pollinations/gen_video.js +13 -2
- package/dist/tools/pollinations/polli_config.js +15 -21
- package/dist/tools/pollinations/polli_gen_confirm.js +2 -0
- package/dist/tools/pollinations/shared.d.ts +1 -1
- package/dist/tools/pollinations/shared.js +53 -142
- package/dist/tools/pollinations/timeout-policy.d.ts +80 -0
- package/dist/tools/pollinations/timeout-policy.js +124 -0
- package/dist/tools/pollinations/tool-capability-registry.d.ts +51 -0
- package/dist/tools/pollinations/tool-capability-registry.js +215 -0
- package/dist/tools/pollinations/transcribe_audio.js +5 -24
- package/package.json +64 -62
- package/dist/server/tier-info.d.ts +0 -36
- package/dist/server/tier-info.js +0 -107
|
@@ -71,7 +71,7 @@ export async function generatePollinationsConfig(forceApiKey, forceStrict = fals
|
|
|
71
71
|
catch (e) {
|
|
72
72
|
log(`Error fetching Free models: ${e}`);
|
|
73
73
|
// Fallback Robust (Offline support)
|
|
74
|
-
modelsOutput.push({ id: "free/
|
|
74
|
+
modelsOutput.push({ id: "free/openai-fast", name: "GPT-5 Nano (Fallback)", object: "model", variants: {} });
|
|
75
75
|
modelsOutput.push({ id: "free/openai", name: "OpenAI (Fallback)", object: "model", variants: {} });
|
|
76
76
|
modelsOutput.push({ id: "free/gemini", name: "Gemini Flash (Fallback)", object: "model", variants: {} });
|
|
77
77
|
}
|
|
@@ -152,7 +152,7 @@ function getCapabilityIcons(raw) {
|
|
|
152
152
|
function mapModel(raw, prefix, namePrefix) {
|
|
153
153
|
const rawId = raw.id || raw.name;
|
|
154
154
|
const fullId = prefix + rawId; // ex: "free/gemini" or "enter/nomnom" (prefix passed is "enter/")
|
|
155
|
-
let baseName = raw.description;
|
|
155
|
+
let baseName = raw.title || raw.description;
|
|
156
156
|
if (!baseName || baseName === rawId) {
|
|
157
157
|
baseName = formatName(rawId, raw.censored !== false);
|
|
158
158
|
}
|
|
@@ -3,33 +3,46 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Central access point for all model metadata. Backed by the fetcher
|
|
5
5
|
* with a configurable TTL. Falls back to static data if fetch fails.
|
|
6
|
+
*
|
|
7
|
+
* v6.5: every read path (get/list/all) triggers a coalesced, non-blocking
|
|
8
|
+
* freshness check, so long sessions always converge to the live catalog.
|
|
6
9
|
*/
|
|
7
10
|
import type { PollinationsModel, ModelCategory, ModelRegistryInterface } from './types.js';
|
|
8
|
-
|
|
11
|
+
export type RegistryFetcher = (apiKey?: string) => Promise<PollinationsModel[]>;
|
|
12
|
+
export interface RegistryOptions {
|
|
13
|
+
ttlMs?: number;
|
|
14
|
+
fetcher?: RegistryFetcher;
|
|
15
|
+
diskCache?: boolean;
|
|
16
|
+
}
|
|
17
|
+
export declare class ModelRegistryImpl implements ModelRegistryInterface {
|
|
9
18
|
private models;
|
|
10
19
|
private lastRefresh;
|
|
11
20
|
private ttl;
|
|
12
21
|
private ready;
|
|
13
22
|
private refreshing;
|
|
14
|
-
|
|
15
|
-
|
|
23
|
+
private fetcher;
|
|
24
|
+
private useDiskCache;
|
|
25
|
+
constructor(options?: RegistryOptions);
|
|
26
|
+
/** Get a single model by category and name. Triggers lazy freshness check. */
|
|
16
27
|
get(category: ModelCategory, name: string): PollinationsModel | undefined;
|
|
17
|
-
/** Also search by alias */
|
|
28
|
+
/** Also search by alias. Triggers lazy freshness check. */
|
|
18
29
|
getByNameOrAlias(category: ModelCategory, name: string): PollinationsModel | undefined;
|
|
19
|
-
/** List all models in a category */
|
|
30
|
+
/** List all models in a category. Triggers lazy freshness check. */
|
|
20
31
|
list(category: ModelCategory): PollinationsModel[];
|
|
21
32
|
/** Check if registry has been populated */
|
|
22
33
|
isReady(): boolean;
|
|
23
34
|
/** Check if cache is stale */
|
|
24
35
|
isStale(): boolean;
|
|
25
|
-
/**
|
|
36
|
+
/** Timestamp of last successful refresh (for tests/diagnostics). */
|
|
37
|
+
lastRefreshAt(): number;
|
|
38
|
+
/** Force refresh from API. Concurrent calls are coalesced on one fetch. */
|
|
26
39
|
refresh(apiKey?: string): Promise<void>;
|
|
27
|
-
|
|
40
|
+
private performRefresh;
|
|
41
|
+
/** Get all models across all categories. Triggers lazy freshness check. */
|
|
28
42
|
all(): PollinationsModel[];
|
|
29
|
-
/** Auto-refresh if stale (non-blocking) */
|
|
30
|
-
ensureFresh(): void
|
|
43
|
+
/** Auto-refresh if stale (non-blocking). Returns the refresh promise for tests. */
|
|
44
|
+
ensureFresh(): Promise<void>;
|
|
31
45
|
/** Get count per category (for logging) */
|
|
32
46
|
stats(): Record<ModelCategory, number>;
|
|
33
47
|
}
|
|
34
48
|
export declare const ModelRegistry: ModelRegistryImpl;
|
|
35
|
-
export {};
|
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Central access point for all model metadata. Backed by the fetcher
|
|
5
5
|
* with a configurable TTL. Falls back to static data if fetch fails.
|
|
6
|
+
*
|
|
7
|
+
* v6.5: every read path (get/list/all) triggers a coalesced, non-blocking
|
|
8
|
+
* freshness check, so long sessions always converge to the live catalog.
|
|
6
9
|
*/
|
|
7
10
|
import * as fs from 'fs';
|
|
8
11
|
import * as path from 'path';
|
|
@@ -60,31 +63,41 @@ function saveCacheToDisk(models, timestamp) {
|
|
|
60
63
|
}
|
|
61
64
|
}
|
|
62
65
|
// ─── Registry Implementation ─────────────────────────────────────────────
|
|
63
|
-
class ModelRegistryImpl {
|
|
66
|
+
export class ModelRegistryImpl {
|
|
64
67
|
models = [];
|
|
65
68
|
lastRefresh = 0;
|
|
66
69
|
ttl = DEFAULT_TTL;
|
|
67
70
|
ready = false;
|
|
68
|
-
refreshing =
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
71
|
+
refreshing = null;
|
|
72
|
+
fetcher;
|
|
73
|
+
useDiskCache;
|
|
74
|
+
constructor(options = {}) {
|
|
75
|
+
this.ttl = options.ttlMs ?? DEFAULT_TTL;
|
|
76
|
+
this.fetcher = options.fetcher ?? ((apiKey) => fetchAllModels(apiKey));
|
|
77
|
+
this.useDiskCache = options.diskCache ?? true;
|
|
78
|
+
if (this.useDiskCache) {
|
|
79
|
+
const diskCache = loadCacheFromDisk();
|
|
80
|
+
if (diskCache && (Date.now() - diskCache.timestamp) < this.ttl) {
|
|
81
|
+
this.models = diskCache.models;
|
|
82
|
+
this.lastRefresh = diskCache.timestamp;
|
|
83
|
+
this.ready = true;
|
|
84
|
+
log(`[ModelRegistry] Loaded ${this.models.length} models from disk cache.`);
|
|
85
|
+
}
|
|
76
86
|
}
|
|
77
87
|
}
|
|
78
|
-
/** Get a single model by category and name */
|
|
88
|
+
/** Get a single model by category and name. Triggers lazy freshness check. */
|
|
79
89
|
get(category, name) {
|
|
90
|
+
this.ensureFresh().catch(() => { });
|
|
80
91
|
return this.models.find(m => m.category === category && m.name === name);
|
|
81
92
|
}
|
|
82
|
-
/** Also search by alias */
|
|
93
|
+
/** Also search by alias. Triggers lazy freshness check. */
|
|
83
94
|
getByNameOrAlias(category, name) {
|
|
95
|
+
this.ensureFresh().catch(() => { });
|
|
84
96
|
return this.models.find(m => m.category === category && (m.name === name || m.aliases.includes(name)));
|
|
85
97
|
}
|
|
86
|
-
/** List all models in a category */
|
|
98
|
+
/** List all models in a category. Triggers lazy freshness check. */
|
|
87
99
|
list(category) {
|
|
100
|
+
this.ensureFresh().catch(() => { });
|
|
88
101
|
return this.models.filter(m => m.category === category);
|
|
89
102
|
}
|
|
90
103
|
/** Check if registry has been populated */
|
|
@@ -95,19 +108,29 @@ class ModelRegistryImpl {
|
|
|
95
108
|
isStale() {
|
|
96
109
|
return Date.now() - this.lastRefresh > this.ttl;
|
|
97
110
|
}
|
|
98
|
-
/**
|
|
99
|
-
|
|
111
|
+
/** Timestamp of last successful refresh (for tests/diagnostics). */
|
|
112
|
+
lastRefreshAt() {
|
|
113
|
+
return this.lastRefresh;
|
|
114
|
+
}
|
|
115
|
+
/** Force refresh from API. Concurrent calls are coalesced on one fetch. */
|
|
116
|
+
refresh(apiKey) {
|
|
100
117
|
if (this.refreshing)
|
|
101
|
-
return; //
|
|
102
|
-
this.refreshing =
|
|
118
|
+
return this.refreshing; // coalesce concurrent refreshes
|
|
119
|
+
this.refreshing = this.performRefresh(apiKey).finally(() => {
|
|
120
|
+
this.refreshing = null;
|
|
121
|
+
});
|
|
122
|
+
return this.refreshing;
|
|
123
|
+
}
|
|
124
|
+
async performRefresh(apiKey) {
|
|
103
125
|
try {
|
|
104
126
|
const key = apiKey || loadConfig().apiKey;
|
|
105
|
-
const fetched = await
|
|
127
|
+
const fetched = await this.fetcher(key);
|
|
106
128
|
if (fetched.length > 0) {
|
|
107
129
|
this.models = fetched;
|
|
108
130
|
this.lastRefresh = Date.now();
|
|
109
131
|
this.ready = true;
|
|
110
|
-
|
|
132
|
+
if (this.useDiskCache)
|
|
133
|
+
saveCacheToDisk(this.models, this.lastRefresh);
|
|
111
134
|
log(`[ModelRegistry] Refreshed: ${this.models.length} models cached to disk.`);
|
|
112
135
|
}
|
|
113
136
|
else {
|
|
@@ -132,19 +155,18 @@ class ModelRegistryImpl {
|
|
|
132
155
|
log(`[ModelRegistry] Refresh failed, keeping cache: ${e}`);
|
|
133
156
|
}
|
|
134
157
|
}
|
|
135
|
-
finally {
|
|
136
|
-
this.refreshing = false;
|
|
137
|
-
}
|
|
138
158
|
}
|
|
139
|
-
/** Get all models across all categories */
|
|
159
|
+
/** Get all models across all categories. Triggers lazy freshness check. */
|
|
140
160
|
all() {
|
|
161
|
+
this.ensureFresh().catch(() => { });
|
|
141
162
|
return [...this.models];
|
|
142
163
|
}
|
|
143
|
-
/** Auto-refresh if stale (non-blocking) */
|
|
164
|
+
/** Auto-refresh if stale (non-blocking). Returns the refresh promise for tests. */
|
|
144
165
|
ensureFresh() {
|
|
145
166
|
if (this.isStale()) {
|
|
146
|
-
this.refresh()
|
|
167
|
+
return this.refresh(); // coalesced; refresh() never rejects (handles offline)
|
|
147
168
|
}
|
|
169
|
+
return Promise.resolve();
|
|
148
170
|
}
|
|
149
171
|
/** Get count per category (for logging) */
|
|
150
172
|
stats() {
|
|
@@ -79,6 +79,8 @@ function mapRawToModel(raw, fallbackCategory, averageCost) {
|
|
|
79
79
|
};
|
|
80
80
|
const model = {
|
|
81
81
|
name: raw.name || raw.id || 'unknown',
|
|
82
|
+
title: raw.title || undefined,
|
|
83
|
+
brand: raw.brand || undefined,
|
|
82
84
|
description: raw.description || raw.name || '',
|
|
83
85
|
category,
|
|
84
86
|
aliases: raw.aliases || [],
|
|
@@ -54,7 +54,7 @@ export class ToolRegistryWorker {
|
|
|
54
54
|
const videoModels = ModelRegistry.list('video');
|
|
55
55
|
if (videoModels.length > 0 && polliGenVideoTool.description) {
|
|
56
56
|
let videoTable = `\n\n**🎬 Modèles Vidéo Détectés (${videoModels.length}) :**\n`;
|
|
57
|
-
videoTable += `*(Légende: [💎 Paid] =
|
|
57
|
+
videoTable += `*(Légende: [💎 Paid] = Pollen acheté (consomme le Wallet USD), [🌿 Free] = Quest Pollen (consomme d'abord le Quota Gratuit Journalier))*\n`;
|
|
58
58
|
videoTable += `| Modèle | Source I/O | Audio | 1 pollen ≈ | Specs |\n`;
|
|
59
59
|
videoTable += `|--------|------------|-------|------------|-------|\n`;
|
|
60
60
|
for (const m of videoModels) {
|
|
@@ -76,7 +76,7 @@ export class ToolRegistryWorker {
|
|
|
76
76
|
const imageModels = ModelRegistry.list('image');
|
|
77
77
|
if (imageModels.length > 0 && polliGenImageTool.description) {
|
|
78
78
|
let imageTable = `\n\n**🎨 Modèles Image Détectés (${imageModels.length}) :**\n`;
|
|
79
|
-
imageTable += `*(Légende: [💎 Paid] =
|
|
79
|
+
imageTable += `*(Légende: [💎 Paid] = Pollen acheté (consomme le Wallet USD), [🌿 Free] = Quest Pollen (consomme d'abord le Quota Gratuit Journalier))*\n`;
|
|
80
80
|
imageTable += `| Modèle | I2I | Qualité | 1 pollen ≈ |\n`;
|
|
81
81
|
imageTable += `|--------|-----|---------|------------|\n`;
|
|
82
82
|
// Afficher max 20 modèles pour éviter de saturer le prompt LLM
|
|
@@ -101,7 +101,7 @@ export class ToolRegistryWorker {
|
|
|
101
101
|
const audioModels = ModelRegistry.list('audio');
|
|
102
102
|
if (audioModels.length > 0 && polliGenAudioTool.description && polliGenMusicTool.description) {
|
|
103
103
|
let audioTable = `\n\n**🎵 Modèles Audio/Music Détectés (${audioModels.length}) :**\n`;
|
|
104
|
-
audioTable += `*(Légende: [💎 Paid] =
|
|
104
|
+
audioTable += `*(Légende: [💎 Paid] = Pollen acheté (consomme le Wallet USD), [🌿 Free] = Quest Pollen (consomme d'abord le Quota Gratuit Journalier))*\n`;
|
|
105
105
|
audioTable += `| Modèle | Durée max | Qualité |\n`;
|
|
106
106
|
audioTable += `|--------|-----------|---------|\n`;
|
|
107
107
|
for (const m of audioModels) {
|
|
@@ -134,7 +134,7 @@ export class ToolRegistryWorker {
|
|
|
134
134
|
});
|
|
135
135
|
if (searchModels.length > 0) {
|
|
136
136
|
let searchTable = `\n\n**🌍 Modèles de Recherche & Grounding Détectés (${searchModels.length}) :**\n`;
|
|
137
|
-
searchTable += `*(Légende: [💎 Paid] =
|
|
137
|
+
searchTable += `*(Légende: [💎 Paid] = Pollen acheté, [🌿 Free] = Quest (Quota Gratuit). Obligatoire de choisir un modèle exact au lieu de 'deep' ou 'rapid' !)*\n`;
|
|
138
138
|
searchTable += `| Modèle | Description / Specs |\n`;
|
|
139
139
|
searchTable += `|--------|---------------------|\n`;
|
|
140
140
|
for (const m of searchModels) {
|
package/dist/server/proxy.d.ts
CHANGED
|
@@ -1,2 +1,8 @@
|
|
|
1
1
|
import * as http from 'http';
|
|
2
|
+
type RetrySignal = 'abort' | 'network' | number;
|
|
3
|
+
export declare function classifyRetry(signal: RetrySignal): 'RETRY' | 'NO_RETRY';
|
|
4
|
+
export declare function fetchWithRetry(url: string, options: any, retries?: number): Promise<Response>;
|
|
5
|
+
/** Normalize a single raw SSE `data:` payload line (JSON). Non-JSON passes through. */
|
|
6
|
+
export declare function normalizeChunkLine(payload: string): string;
|
|
2
7
|
export declare function handleChatCompletion(req: http.IncomingMessage, res: http.ServerResponse, bodyRaw: string): Promise<void>;
|
|
8
|
+
export {};
|