opencode-pollinations-plugin 6.4.10 → 6.5.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/README.de.md +67 -55
- package/README.es.md +79 -67
- package/README.fr.md +70 -58
- package/README.it.md +78 -66
- package/README.md +33 -29
- package/README.zh.md +77 -65
- package/dist/locales/de.json +82 -50
- package/dist/locales/en.json +84 -52
- package/dist/locales/es.json +82 -50
- package/dist/locales/fr.json +81 -49
- package/dist/locales/it.json +82 -50
- package/dist/locales/zh.json +82 -50
- package/dist/server/commands.js +112 -110
- package/dist/server/config.d.ts +24 -6
- package/dist/server/config.js +45 -4
- package/dist/server/connect-response.js +7 -7
- package/dist/server/generate-config.d.ts +7 -0
- package/dist/server/generate-config.js +22 -16
- package/dist/server/models/cache.d.ts +23 -10
- package/dist/server/models/cache.js +46 -24
- package/dist/server/models/fetcher.js +9 -0
- package/dist/server/models/types.d.ts +8 -0
- package/dist/server/models/worker.js +32 -8
- package/dist/server/proxy.d.ts +6 -0
- package/dist/server/proxy.js +297 -210
- package/dist/server/quota.d.ts +23 -32
- package/dist/server/quota.js +45 -185
- 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 +8 -3
- 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 +18 -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 +6 -4
- package/dist/server/tier-info.d.ts +0 -36
- package/dist/server/tier-info.js +0 -107
package/dist/server/commands.js
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import * as https from 'https';
|
|
2
2
|
import { loadConfig, saveConfig, saveKeyToAuthJson } from './config.js';
|
|
3
|
-
import { getQuotaStatus, fetchUsageForPeriod } from './quota.js';
|
|
3
|
+
import { getQuotaStatus, fetchUsageForPeriod, calculateResetInfo } from './quota.js';
|
|
4
4
|
import { emitStatusToast } from './toast.js';
|
|
5
5
|
import { generatePollinationsConfig } from './generate-config.js';
|
|
6
6
|
import { ModelRegistry } from './models/index.js';
|
|
7
7
|
import { t } from '../locales/index.js';
|
|
8
|
-
import { formatTierTable } from './tier-info.js';
|
|
9
8
|
import { buildQuestsReport } from '../tools/pollinations/polli_quests.js';
|
|
10
9
|
function checkEndpoint(ep, key) {
|
|
11
10
|
return new Promise((resolve) => {
|
|
@@ -66,27 +65,10 @@ function formatTokens(tokens) {
|
|
|
66
65
|
return `${(tokens / 1_000).toFixed(1)}K`;
|
|
67
66
|
return tokens.toString();
|
|
68
67
|
}
|
|
69
|
-
function formatDuration(ms) {
|
|
70
|
-
const hours = Math.floor(ms / (1000 * 60 * 60));
|
|
71
|
-
const minutes = Math.floor((ms % (1000 * 60 * 60)) / (1000 * 60));
|
|
72
|
-
return `${hours}h ${minutes}m`;
|
|
73
|
-
}
|
|
74
|
-
function progressBar(value, max) {
|
|
75
|
-
const percentage = max > 0 ? Math.round((value / max) * 10) : 0;
|
|
76
|
-
const filled = '█'.repeat(percentage);
|
|
77
|
-
const empty = '░'.repeat(10 - percentage);
|
|
78
|
-
return `\`${filled}${empty}\` (${(value / max * 100).toFixed(0)}%)`;
|
|
79
|
-
}
|
|
80
68
|
function parseUsageTimestamp(timestamp) {
|
|
81
69
|
return new Date(timestamp.replace(' ', 'T') + 'Z');
|
|
82
70
|
}
|
|
83
|
-
function
|
|
84
|
-
// Hourly quota system (reset at :00). The "current period" is the last hour,
|
|
85
|
-
// matching the tier window computed in quota.ts. (Previously 24h — stale daily model.)
|
|
86
|
-
const lastReset = new Date(nextResetAt.getTime() - 60 * 60 * 1000);
|
|
87
|
-
return lastReset;
|
|
88
|
-
}
|
|
89
|
-
function calculateCurrentPeriodStats(usage, lastReset, tierLimit) {
|
|
71
|
+
function calculateCurrentPeriodStats(usage, lastReset) {
|
|
90
72
|
let tierUsed = 0;
|
|
91
73
|
let packUsed = 0;
|
|
92
74
|
let totalRequests = 0;
|
|
@@ -115,7 +97,7 @@ function calculateCurrentPeriodStats(usage, lastReset, tierLimit) {
|
|
|
115
97
|
}
|
|
116
98
|
return {
|
|
117
99
|
tierUsed,
|
|
118
|
-
tierRemaining:
|
|
100
|
+
tierRemaining: 0,
|
|
119
101
|
packUsed,
|
|
120
102
|
totalRequests,
|
|
121
103
|
inputTokens,
|
|
@@ -182,50 +164,57 @@ async function handleModeCommand(args) {
|
|
|
182
164
|
response: t('commands.mode.current', { mode: config.mode })
|
|
183
165
|
};
|
|
184
166
|
}
|
|
185
|
-
|
|
167
|
+
// v6.5 Quest/Paid modes + legacy aliases (alwaysfree → quest_only, pro → paid).
|
|
168
|
+
const LEGACY_MODE_ALIASES = {
|
|
169
|
+
'alwaysfree': 'quest_only',
|
|
170
|
+
'pro': 'paid',
|
|
171
|
+
};
|
|
172
|
+
const resolvedMode = LEGACY_MODE_ALIASES[mode] || mode;
|
|
173
|
+
if (!['manual', 'quest', 'quest_only', 'paid'].includes(resolvedMode)) {
|
|
186
174
|
return {
|
|
187
175
|
handled: true,
|
|
188
176
|
error: t('commands.mode.invalid', { mode })
|
|
189
177
|
};
|
|
190
178
|
}
|
|
191
179
|
const checkConfig = loadConfig();
|
|
192
|
-
// JIT VERIFICATION for
|
|
193
|
-
if (
|
|
194
|
-
const checkConfig = loadConfig(); // Reload to be sure
|
|
180
|
+
// JIT VERIFICATION for Quest/Paid modes (requires a valid key)
|
|
181
|
+
if (resolvedMode === 'quest' || resolvedMode === 'quest_only' || resolvedMode === 'paid') {
|
|
195
182
|
const key = checkConfig.apiKey;
|
|
196
183
|
if (!key) {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
if (mode === 'pro')
|
|
200
|
-
return { handled: true, error: t('commands.mode.pro_requires_key') };
|
|
201
|
-
}
|
|
202
|
-
emitStatusToast('info', t('commands.mode.verifying'), 'Mode Pro');
|
|
203
|
-
try {
|
|
204
|
-
// Force verify permissions NOW
|
|
205
|
-
const check = await checkKeyPermissions(key);
|
|
206
|
-
if (!check.ok) {
|
|
207
|
-
saveConfig({ mode: 'manual', keyHasAccessToProfile: false });
|
|
208
|
-
return {
|
|
209
|
-
handled: true,
|
|
210
|
-
error: t('commands.mode.denied', { status: check.status || '?', reason: check.reason || '?' })
|
|
211
|
-
};
|
|
184
|
+
if (resolvedMode === 'paid' || resolvedMode === 'quest_only') {
|
|
185
|
+
return { handled: true, error: t('commands.mode.key_required', { mode: resolvedMode }) };
|
|
212
186
|
}
|
|
213
|
-
// Valid -> Ensure flag is true
|
|
214
|
-
saveConfig({ keyHasAccessToProfile: true });
|
|
215
187
|
}
|
|
216
|
-
|
|
217
|
-
|
|
188
|
+
else {
|
|
189
|
+
emitStatusToast('info', t('commands.mode.verifying'), 'Mode');
|
|
190
|
+
try {
|
|
191
|
+
// Force verify permissions NOW
|
|
192
|
+
const check = await checkKeyPermissions(key);
|
|
193
|
+
if (!check.ok) {
|
|
194
|
+
saveConfig({ mode: 'manual', keyHasAccessToProfile: false });
|
|
195
|
+
return {
|
|
196
|
+
handled: true,
|
|
197
|
+
error: t('commands.mode.denied', { status: check.status || '?', reason: check.reason || '?' })
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
// Valid -> Ensure flag is true
|
|
201
|
+
saveConfig({ keyHasAccessToProfile: true });
|
|
202
|
+
}
|
|
203
|
+
catch (e) {
|
|
204
|
+
return { handled: true, error: t('commands.mode.verify_error', { error: e.message }) };
|
|
205
|
+
}
|
|
218
206
|
}
|
|
219
207
|
}
|
|
220
|
-
// Allow switch
|
|
221
|
-
saveConfig({ mode:
|
|
208
|
+
// Allow switch
|
|
209
|
+
saveConfig({ mode: resolvedMode });
|
|
222
210
|
const config = loadConfig();
|
|
223
211
|
if (config.gui.status !== 'none') {
|
|
224
|
-
emitStatusToast('success', t('commands.mode.success', { mode }), 'Pollinations Config');
|
|
212
|
+
emitStatusToast('success', t('commands.mode.success', { mode: resolvedMode }), 'Pollinations Config');
|
|
225
213
|
}
|
|
214
|
+
const aliasNote = LEGACY_MODE_ALIASES[mode] ? ` ${t('commands.mode.legacy_alias', { legacy: mode, mode: resolvedMode })}` : '';
|
|
226
215
|
return {
|
|
227
216
|
handled: true,
|
|
228
|
-
response: t('commands.mode.success', { mode })
|
|
217
|
+
response: t('commands.mode.success', { mode: resolvedMode }) + aliasNote
|
|
229
218
|
};
|
|
230
219
|
}
|
|
231
220
|
export async function handleUsageCommand(args) {
|
|
@@ -233,36 +222,31 @@ export async function handleUsageCommand(args) {
|
|
|
233
222
|
try {
|
|
234
223
|
const quota = await getQuotaStatus(true);
|
|
235
224
|
const config = loadConfig();
|
|
236
|
-
const
|
|
237
|
-
const timeUntilReset = quota.nextResetAt.getTime() - Date.now();
|
|
238
|
-
const durationStr = formatDuration(Math.max(0, timeUntilReset));
|
|
225
|
+
const resetInfo = calculateResetInfo();
|
|
239
226
|
let response = t('commands.usage.title', { mode: config.mode.toUpperCase() });
|
|
240
227
|
response += t('commands.usage.resources');
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
const total = quota.tierRemaining + quota.questStash + quota.walletBalance;
|
|
245
|
-
const questPollen = quota.tierRemaining + quota.questStash;
|
|
246
|
-
const paidPollen = quota.walletBalance;
|
|
228
|
+
const quest = quota.questBalance;
|
|
229
|
+
const paid = quota.walletBalance;
|
|
230
|
+
const total = quota.totalBalance || (quest + paid);
|
|
247
231
|
response += t('commands.usage.split', {
|
|
248
|
-
quest:
|
|
249
|
-
paid:
|
|
232
|
+
quest: quest.toFixed(2),
|
|
233
|
+
paid: paid.toFixed(2),
|
|
250
234
|
total: total.toFixed(2),
|
|
251
235
|
});
|
|
252
|
-
response += t('commands.usage.
|
|
236
|
+
response += t('commands.usage.quest_note');
|
|
253
237
|
if (isFull && config.apiKey) {
|
|
254
238
|
if (config.keyHasAccessToProfile === false) {
|
|
255
239
|
response += t('commands.usage.restricted_key');
|
|
256
240
|
}
|
|
257
241
|
else {
|
|
258
|
-
const lastReset =
|
|
242
|
+
const lastReset = resetInfo.lastReset;
|
|
259
243
|
const usageData = await fetchUsageForPeriod(config.apiKey, lastReset);
|
|
260
244
|
if (usageData && usageData.length > 0) {
|
|
261
|
-
const stats = calculateCurrentPeriodStats(usageData, lastReset
|
|
245
|
+
const stats = calculateCurrentPeriodStats(usageData, lastReset);
|
|
262
246
|
response += t('commands.usage.period_detail', { time: lastReset.toLocaleTimeString() });
|
|
263
247
|
response += t('commands.usage.total_reqs', { reqs: stats.totalRequests, inTok: formatTokens(stats.inputTokens), outTok: formatTokens(stats.outputTokens) });
|
|
264
248
|
// Exact consumption split by meter_source (tier = Quest Pollen, pack = Paid).
|
|
265
|
-
// This is the ONLY reliable split the API exposes
|
|
249
|
+
// This is the ONLY reliable split the API exposes.
|
|
266
250
|
response += t('commands.usage.source_split', {
|
|
267
251
|
tier: formatPollen(stats.tierUsed),
|
|
268
252
|
pack: formatPollen(stats.packUsed),
|
|
@@ -595,20 +579,23 @@ ${t('commands.config.intro')}
|
|
|
595
579
|
${t('commands.config.table_headers')}
|
|
596
580
|
${t('commands.config.table_divider')}
|
|
597
581
|
| **apiKey** | \`${k}\` | ${t('commands.config.api_key_role')} | \`/poll connect <key>\` |
|
|
598
|
-
| **mode** | \`${config.mode}\` | ${t('commands.config.mode_role')} | \`/poll mode <
|
|
582
|
+
| **mode** | \`${config.mode}\` | ${t('commands.config.mode_role')} | \`/poll mode <quest/quest_only/paid/manual>\` |
|
|
599
583
|
| **enablePaidTools**| \`${config.enablePaidTools ?? true}\` | ${t('commands.config.enablePaidTools_role')} | \`/poll config enablePaidTools <true/false>\` |
|
|
600
584
|
| **costConfirmationRequired**| \`${config.costConfirmationRequired ?? true}\` | ${t('commands.config.costConfirmationRequired_role')} | \`/poll config costConfirmationRequired <true/false>\` |
|
|
601
585
|
| **costThreshold**| \`${config.costThreshold ?? 0.15} 🌻\` | ${t('commands.config.costThreshold_role')} | \`/poll config costThreshold <X>\` |
|
|
602
586
|
| **cost_estimator**| \`${config.costEstimator ?? true}\` | ${t('commands.config.cost_estimator_role')} | \`/poll config cost_estimator <true/false>\` |
|
|
603
|
-
| **refillOverride**| \`${config.refillOverride ?? 'auto (déduit)'}\` | Quest Pollen refill horaire | \`/poll config refillOverride <0.01/0.15/0.4/0.8/10>\` |
|
|
604
|
-
| **questStashInFreeMode**| \`${config.questStashInFreeMode ?? true}\` | Compte le stash dans alwaysfree | \`/poll config questStashInFreeMode <true/false>\` |
|
|
605
587
|
| **fallbacks.free.main** | \`${config.fallbacks?.free?.main || 'free/mistral'}\` | ${t('commands.config.fallback_main_role')} | \`/poll fallback <main> <agent>\` |
|
|
606
588
|
| **fallbacks.free.agent** | \`${config.fallbacks?.free?.agent || 'free/openai-fast'}\`| ${t('commands.config.fallback_agent_role')} | \`/poll fallback <main> <agent>\` |
|
|
607
589
|
| **fallbacks.enter.agent** | \`${config.fallbacks?.enter?.agent || 'free/openai-fast'}\`| ${t('commands.config.fallback_enter_role')} | *${t('commands.config.managed_auto')}* |
|
|
608
590
|
| **status_gui** | \`${config.gui?.status || 'all'}\` | ${t('commands.config.status_gui_role')} | \`/poll config status_gui <all/alert/none>\` |
|
|
609
591
|
| **logs_gui** | \`${config.gui?.logs || 'error'}\` | ${t('commands.config.logs_gui_role')} | \`/poll config logs_gui <verbose/error/none>\` |
|
|
610
|
-
| **
|
|
611
|
-
| **threshold_wallet** | \`${config.thresholds?.wallet
|
|
592
|
+
| **threshold_quest** | \`${config.thresholds?.quest ?? 0.05} 🌻\` | ${t('commands.config.threshold_quest_role')} | \`/poll config threshold_quest <pollen>\` |
|
|
593
|
+
| **threshold_wallet** | \`${config.thresholds?.wallet ?? 0.5} 🌻\` | ${t('commands.config.threshold_wallet_role')} | \`/poll config threshold_wallet <pollen>\` |
|
|
594
|
+
| **timeouts.default** | \`${config.timeouts?.default ?? 300}s\` | ${t('commands.config.timeouts_default_role')} | \`/poll config timeouts.default <s>\` |
|
|
595
|
+
| **timeouts.longRunning** | \`${config.timeouts?.longRunning ?? 900}s\` | ${t('commands.config.timeouts_long_role')} | \`/poll config timeouts.longRunning <s>\` |
|
|
596
|
+
| **timeouts.max** | \`${config.timeouts?.max ?? 3600}s\` | ${t('commands.config.timeouts_max_role')} | \`/poll config timeouts.max <s>\` |
|
|
597
|
+
| **timeouts.video** | \`${config.timeouts?.capabilities?.video ?? 1800}s\` | ${t('commands.config.timeouts_video_role')} | \`/poll config timeouts.video <s>\` |
|
|
598
|
+
| **timeouts.threeD** | \`${config.timeouts?.capabilities?.threeD ?? 1800}s\` | ${t('commands.config.timeouts_3d_role')} | \`/poll config timeouts.threeD <s>\` |
|
|
612
599
|
| **status_bar** | \`${config.statusBar ?? true}\` | ${t('commands.config.status_bar_role')} | \`/poll config status_bar <true/false>\` |
|
|
613
600
|
| **lang** | \`${config.lang || 'en'}\` | ${t('commands.config.lang_role')} | \`/poll config lang <en/fr/es/de/it>\` |`;
|
|
614
601
|
return {
|
|
@@ -651,23 +638,63 @@ ${t('commands.config.table_divider')}
|
|
|
651
638
|
saveConfig({ gui: { ...config.gui, logs: value } });
|
|
652
639
|
return { handled: true, response: `✅ logs_gui = ${value}` };
|
|
653
640
|
}
|
|
654
|
-
if (key === '
|
|
655
|
-
const threshold =
|
|
656
|
-
if (isNaN(threshold) || threshold < 0
|
|
657
|
-
return { handled: true, error: 'Valeur
|
|
641
|
+
if (key === 'threshold_quest' && value) {
|
|
642
|
+
const threshold = parseFloat(value);
|
|
643
|
+
if (isNaN(threshold) || threshold < 0) {
|
|
644
|
+
return { handled: true, error: 'Valeur numérique positive requise (en pollen). Ex: 0.05' };
|
|
658
645
|
}
|
|
659
646
|
const config = loadConfig();
|
|
660
|
-
saveConfig({ thresholds: { ...config.thresholds,
|
|
661
|
-
return { handled: true, response: `✅
|
|
647
|
+
saveConfig({ thresholds: { ...config.thresholds, quest: threshold } });
|
|
648
|
+
return { handled: true, response: `✅ threshold_quest = ${threshold} 🌻` };
|
|
662
649
|
}
|
|
663
650
|
if (key === 'threshold_wallet' && value) {
|
|
664
|
-
const threshold =
|
|
665
|
-
if (isNaN(threshold) || threshold < 0
|
|
666
|
-
return { handled: true, error: 'Valeur
|
|
651
|
+
const threshold = parseFloat(value);
|
|
652
|
+
if (isNaN(threshold) || threshold < 0) {
|
|
653
|
+
return { handled: true, error: 'Valeur numérique positive requise (en pollen). Ex: 0.5' };
|
|
667
654
|
}
|
|
668
655
|
const config = loadConfig();
|
|
669
656
|
saveConfig({ thresholds: { ...config.thresholds, wallet: threshold } });
|
|
670
|
-
return { handled: true, response: `✅ threshold_wallet = ${threshold}
|
|
657
|
+
return { handled: true, response: `✅ threshold_wallet = ${threshold} 🌻` };
|
|
658
|
+
}
|
|
659
|
+
// v6.5 timeout hierarchy: timeouts.default / longRunning / max /
|
|
660
|
+
// timeouts.<capability> (image|video|audio|threeD|realtime|embed) /
|
|
661
|
+
// timeouts.model.<name> / reset
|
|
662
|
+
if (key.startsWith('timeouts') && value) {
|
|
663
|
+
const parts = key.split('.');
|
|
664
|
+
const config = loadConfig();
|
|
665
|
+
const timeouts = { ...(config.timeouts || {}) };
|
|
666
|
+
if (parts.length === 2 && ['default', 'longRunning', 'max'].includes(parts[1])) {
|
|
667
|
+
const seconds = parseFloat(value);
|
|
668
|
+
if (isNaN(seconds) || seconds < 1 || seconds > 3600) {
|
|
669
|
+
return { handled: true, error: 'Valeur en secondes requise (1-3600)' };
|
|
670
|
+
}
|
|
671
|
+
timeouts[parts[1]] = seconds;
|
|
672
|
+
saveConfig({ timeouts });
|
|
673
|
+
return { handled: true, response: `✅ timeouts.${parts[1]} = ${seconds}s` };
|
|
674
|
+
}
|
|
675
|
+
const CAP_KEYS = ['image', 'video', 'audio', 'threeD', 'realtime', 'embed'];
|
|
676
|
+
if (parts.length === 2 && CAP_KEYS.includes(parts[1])) {
|
|
677
|
+
const seconds = parseFloat(value);
|
|
678
|
+
if (isNaN(seconds) || seconds < 10 || seconds > 3600) {
|
|
679
|
+
return { handled: true, error: 'Valeur en secondes requise (10-3600)' };
|
|
680
|
+
}
|
|
681
|
+
timeouts.capabilities = { ...(timeouts.capabilities || {}), [parts[1]]: seconds };
|
|
682
|
+
saveConfig({ timeouts });
|
|
683
|
+
return { handled: true, response: `✅ timeouts.${parts[1]} = ${seconds}s` };
|
|
684
|
+
}
|
|
685
|
+
if (parts.length === 3 && parts[1] === 'model') {
|
|
686
|
+
const seconds = parseFloat(value);
|
|
687
|
+
if (isNaN(seconds) || seconds < 10 || seconds > 3600) {
|
|
688
|
+
return { handled: true, error: 'Valeur en secondes requise (10-3600)' };
|
|
689
|
+
}
|
|
690
|
+
timeouts.overrides = { ...(timeouts.overrides || {}), [parts[2]]: seconds };
|
|
691
|
+
saveConfig({ timeouts });
|
|
692
|
+
return { handled: true, response: `✅ timeouts.model.${parts[2]} = ${seconds}s` };
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
if (key === 'timeouts.reset') {
|
|
696
|
+
saveConfig({ timeouts: undefined });
|
|
697
|
+
return { handled: true, response: '✅ timeouts = defaults (reset)' };
|
|
671
698
|
}
|
|
672
699
|
if (key === 'status_bar' && value) {
|
|
673
700
|
const enabled = value === 'true';
|
|
@@ -698,29 +725,9 @@ ${t('commands.config.table_divider')}
|
|
|
698
725
|
saveConfig({ costConfirmationRequired: enabled });
|
|
699
726
|
return { handled: true, response: `✅ costConfirmationRequired = ${enabled}` };
|
|
700
727
|
}
|
|
701
|
-
if (key === 'refillOverride' && value) {
|
|
702
|
-
if (value === 'auto') {
|
|
703
|
-
saveConfig({ refillOverride: undefined });
|
|
704
|
-
return { handled: true, response: '✅ refillOverride = auto (déduction automatique)' };
|
|
705
|
-
}
|
|
706
|
-
const override = parseFloat(value);
|
|
707
|
-
if (isNaN(override) || ![0.01, 0.15, 0.4, 0.8, 10].includes(override)) {
|
|
708
|
-
return { handled: true, error: 'Valeurs supportées: auto, 0.01, 0.15, 0.4, 0.8, 10' };
|
|
709
|
-
}
|
|
710
|
-
saveConfig({ refillOverride: override });
|
|
711
|
-
return { handled: true, response: `✅ refillOverride = ${override} 🌻/h` };
|
|
712
|
-
}
|
|
713
|
-
if (key === 'questStashInFreeMode' && value) {
|
|
714
|
-
if (value !== 'true' && value !== 'false') {
|
|
715
|
-
return { handled: true, error: 'Valeurs supportées: true, false' };
|
|
716
|
-
}
|
|
717
|
-
const enabled = value === 'true';
|
|
718
|
-
saveConfig({ questStashInFreeMode: enabled });
|
|
719
|
-
return { handled: true, response: `✅ questStashInFreeMode = ${enabled}${enabled ? ' (le stash Quest compte comme free)' : ' (seul le refill horaire compte)'}` };
|
|
720
|
-
}
|
|
721
728
|
return {
|
|
722
729
|
handled: true,
|
|
723
|
-
error: `Clé inconnue: ${key}. Clés: status_gui, logs_gui,
|
|
730
|
+
error: `Clé inconnue: ${key}. Clés: status_gui, logs_gui, threshold_quest, threshold_wallet, status_bar, cost_estimator, enablePaidTools, costThreshold, costConfirmationRequired, lang`
|
|
724
731
|
};
|
|
725
732
|
}
|
|
726
733
|
function handleHelpCommand() {
|
|
@@ -729,15 +736,13 @@ function handleHelpCommand() {
|
|
|
729
736
|
{ key: 'lang', values: 'en, fr, es, de, it, zh', i18n: 'commands.help.config.lang' },
|
|
730
737
|
{ key: 'status_gui', values: 'none, alert, all', i18n: 'commands.help.config.status_gui' },
|
|
731
738
|
{ key: 'logs_gui', values: 'none, error, verbose', i18n: 'commands.help.config.logs_gui' },
|
|
732
|
-
{ key: '
|
|
733
|
-
{ key: 'threshold_wallet', values: '0
|
|
739
|
+
{ key: 'threshold_quest', values: 'pollen (e.g. 0.05)', i18n: 'commands.help.config.threshold_quest' },
|
|
740
|
+
{ key: 'threshold_wallet', values: 'pollen (e.g. 0.5)', i18n: 'commands.help.config.threshold_wallet' },
|
|
734
741
|
{ key: 'status_bar', values: 'true/false', i18n: 'commands.help.config.status_bar' },
|
|
735
742
|
{ key: 'cost_estimator', values: 'true/false', i18n: 'commands.help.config.cost_estimator' },
|
|
736
743
|
{ key: 'enablePaidTools', values: 'true/false', i18n: 'commands.help.config.enablePaidTools' },
|
|
737
744
|
{ key: 'costThreshold', values: 'number (pollen)', i18n: 'commands.help.config.costThreshold' },
|
|
738
745
|
{ key: 'costConfirmationRequired', values: 'true/false', i18n: 'commands.help.config.costConfirmationRequired' },
|
|
739
|
-
{ key: 'refillOverride', values: '0.01, 0.15, 0.4, 0.8, 10, auto', i18n: 'commands.help.config.refillOverride' },
|
|
740
|
-
{ key: 'questStashInFreeMode', values: 'true/false', i18n: 'commands.help.config.questStashInFreeMode' },
|
|
741
746
|
];
|
|
742
747
|
const configSection = configKeys.map(k => ` - \`${k.key}\`: ${t(k.i18n)} (\`${k.values}\`)`).join('\n');
|
|
743
748
|
const help = `
|
|
@@ -920,9 +925,8 @@ export async function handleInfosCommand() {
|
|
|
920
925
|
// Ignorer
|
|
921
926
|
}
|
|
922
927
|
}
|
|
923
|
-
//
|
|
924
|
-
|
|
925
|
-
const tierTable = formatTierTable(userLang);
|
|
928
|
+
// v6.5: Quest/Paid page (the old tier/refill table was removed upstream —
|
|
929
|
+
// hourly refills no longer exist).
|
|
926
930
|
const response = `${t('commands.infos.title', { name })}
|
|
927
931
|
${t('commands.infos.features_title')}
|
|
928
932
|
${t('commands.infos.features_free')}
|
|
@@ -935,11 +939,9 @@ ${t('commands.infos.get_started')}
|
|
|
935
939
|
|
|
936
940
|
${t('commands.infos.about')}
|
|
937
941
|
|
|
938
|
-
${t('commands.infos.
|
|
939
|
-
|
|
940
|
-
${tierTable}
|
|
942
|
+
${t('commands.infos.quest_paid_title')}
|
|
941
943
|
|
|
942
|
-
${t('commands.infos.
|
|
944
|
+
${t('commands.infos.quest_paid_body')}
|
|
943
945
|
|
|
944
946
|
${t('commands.infos.quests')}
|
|
945
947
|
|
package/dist/server/config.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
export declare function getConfigDir(): string;
|
|
2
2
|
export declare const CONFIG_DIR: string;
|
|
3
3
|
export declare const CONFIG_FILE: string;
|
|
4
|
+
export type BillingMode = 'quest' | 'quest_only' | 'paid' | 'manual';
|
|
4
5
|
export interface PollinationsConfigV5 {
|
|
5
6
|
version: string | number;
|
|
6
|
-
mode:
|
|
7
|
+
mode: BillingMode;
|
|
7
8
|
apiKey?: string;
|
|
8
9
|
keyHasAccessToProfile?: boolean;
|
|
9
10
|
gui: {
|
|
@@ -11,7 +12,7 @@ export interface PollinationsConfigV5 {
|
|
|
11
12
|
logs: 'none' | 'error' | 'verbose';
|
|
12
13
|
};
|
|
13
14
|
thresholds: {
|
|
14
|
-
|
|
15
|
+
quest: number;
|
|
15
16
|
wallet: number;
|
|
16
17
|
};
|
|
17
18
|
fallbacks: {
|
|
@@ -24,18 +25,27 @@ export interface PollinationsConfigV5 {
|
|
|
24
25
|
};
|
|
25
26
|
};
|
|
26
27
|
enablePaidTools: boolean;
|
|
28
|
+
enableDeveloperTools: boolean;
|
|
27
29
|
costThreshold: number;
|
|
28
30
|
costConfirmationRequired: boolean;
|
|
29
31
|
statusBar: boolean;
|
|
30
32
|
costEstimator: boolean;
|
|
33
|
+
lang?: string;
|
|
34
|
+
timeouts?: {
|
|
35
|
+
default?: number;
|
|
36
|
+
longRunning?: number;
|
|
37
|
+
max?: number;
|
|
38
|
+
capabilities?: Record<string, number>;
|
|
39
|
+
overrides?: Record<string, number>;
|
|
40
|
+
};
|
|
31
41
|
refillOverride?: number;
|
|
32
42
|
questStashInFreeMode?: boolean;
|
|
33
|
-
lang?: string;
|
|
34
43
|
}
|
|
44
|
+
export declare function migrateV65Config(raw: any): any;
|
|
35
45
|
export declare function loadConfig(): PollinationsConfigV5;
|
|
36
46
|
export declare function saveConfig(updates: Partial<PollinationsConfigV5>): {
|
|
37
47
|
version: string;
|
|
38
|
-
mode:
|
|
48
|
+
mode: BillingMode;
|
|
39
49
|
apiKey?: string;
|
|
40
50
|
keyHasAccessToProfile?: boolean;
|
|
41
51
|
gui: {
|
|
@@ -43,7 +53,7 @@ export declare function saveConfig(updates: Partial<PollinationsConfigV5>): {
|
|
|
43
53
|
logs: "none" | "error" | "verbose";
|
|
44
54
|
};
|
|
45
55
|
thresholds: {
|
|
46
|
-
|
|
56
|
+
quest: number;
|
|
47
57
|
wallet: number;
|
|
48
58
|
};
|
|
49
59
|
fallbacks: {
|
|
@@ -56,13 +66,21 @@ export declare function saveConfig(updates: Partial<PollinationsConfigV5>): {
|
|
|
56
66
|
};
|
|
57
67
|
};
|
|
58
68
|
enablePaidTools: boolean;
|
|
69
|
+
enableDeveloperTools: boolean;
|
|
59
70
|
costThreshold: number;
|
|
60
71
|
costConfirmationRequired: boolean;
|
|
61
72
|
statusBar: boolean;
|
|
62
73
|
costEstimator: boolean;
|
|
74
|
+
lang?: string;
|
|
75
|
+
timeouts?: {
|
|
76
|
+
default?: number;
|
|
77
|
+
longRunning?: number;
|
|
78
|
+
max?: number;
|
|
79
|
+
capabilities?: Record<string, number>;
|
|
80
|
+
overrides?: Record<string, number>;
|
|
81
|
+
};
|
|
63
82
|
refillOverride?: number;
|
|
64
83
|
questStashInFreeMode?: boolean;
|
|
65
|
-
lang?: string;
|
|
66
84
|
};
|
|
67
85
|
export declare function saveKeyToAuthJson(key: string): boolean;
|
|
68
86
|
export declare function migrateLegacyConfig(): void;
|
package/dist/server/config.js
CHANGED
|
@@ -69,22 +69,63 @@ catch (e) {
|
|
|
69
69
|
}
|
|
70
70
|
const DEFAULT_CONFIG_V5 = {
|
|
71
71
|
version: PKG_VERSION,
|
|
72
|
-
mode: '
|
|
72
|
+
mode: 'quest',
|
|
73
73
|
gui: { status: 'alert', logs: 'none' },
|
|
74
|
-
thresholds: {
|
|
74
|
+
thresholds: { quest: 0.05, wallet: 0.5 },
|
|
75
75
|
fallbacks: {
|
|
76
76
|
free: { main: 'free/openai-fast', agent: 'free/openai-fast' },
|
|
77
77
|
enter: { agent: 'free/openai-fast' }
|
|
78
78
|
},
|
|
79
79
|
enablePaidTools: false,
|
|
80
|
+
enableDeveloperTools: false,
|
|
80
81
|
costThreshold: 0.15, // Default 0.15 🌻
|
|
81
82
|
costConfirmationRequired: true, // Ask confirmation when cost exceeds threshold
|
|
82
83
|
keyHasAccessToProfile: true, // Default true for legacy keys
|
|
83
84
|
statusBar: true,
|
|
84
85
|
costEstimator: true, // Show cost estimates by default
|
|
85
|
-
questStashInFreeMode: true, // Count quest stash as free in alwaysfree
|
|
86
86
|
lang: 'en', // Default language is English
|
|
87
87
|
};
|
|
88
|
+
// v6.5 migration: legacy mode/threshold names → Quest/Paid semantics.
|
|
89
|
+
// alwaysfree → quest_only (QUEST_ELIGIBLE_ONLY), pro → paid (PAID_ALLOWED).
|
|
90
|
+
// Legacy tier percentage thresholds are replaced by absolute pollen floors.
|
|
91
|
+
export function migrateV65Config(raw) {
|
|
92
|
+
if (!raw || typeof raw !== 'object')
|
|
93
|
+
return raw;
|
|
94
|
+
const legacyModeMap = {
|
|
95
|
+
'alwaysfree': 'quest_only',
|
|
96
|
+
'pro': 'paid',
|
|
97
|
+
'manual': 'manual',
|
|
98
|
+
'quest': 'quest',
|
|
99
|
+
'quest_only': 'quest_only',
|
|
100
|
+
'paid': 'paid',
|
|
101
|
+
};
|
|
102
|
+
if (raw.mode && legacyModeMap[raw.mode]) {
|
|
103
|
+
const mapped = legacyModeMap[raw.mode];
|
|
104
|
+
if (mapped !== raw.mode) {
|
|
105
|
+
logConfig(`Migrating legacy mode '${raw.mode}' → '${mapped}'`);
|
|
106
|
+
raw.mode = mapped;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (raw.thresholds && typeof raw.thresholds === 'object') {
|
|
110
|
+
if (raw.thresholds.tier !== undefined && raw.thresholds.quest === undefined) {
|
|
111
|
+
// Old percentage-of-tier threshold cannot be converted to a
|
|
112
|
+
// meaningful absolute floor: use the v6.5 default.
|
|
113
|
+
logConfig('Migrating legacy thresholds.tier → thresholds.quest (default 0.05)');
|
|
114
|
+
raw.thresholds.quest = 0.05;
|
|
115
|
+
delete raw.thresholds.tier;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// Purge dead refill concepts from persisted config.
|
|
119
|
+
if ('refillOverride' in raw) {
|
|
120
|
+
logConfig('Removing deprecated refillOverride');
|
|
121
|
+
delete raw.refillOverride;
|
|
122
|
+
}
|
|
123
|
+
if ('questStashInFreeMode' in raw) {
|
|
124
|
+
logConfig('Removing deprecated questStashInFreeMode');
|
|
125
|
+
delete raw.questStashInFreeMode;
|
|
126
|
+
}
|
|
127
|
+
return raw;
|
|
128
|
+
}
|
|
88
129
|
import { log as logSystem } from './logger.js';
|
|
89
130
|
function logConfig(msg) {
|
|
90
131
|
logSystem(`[Config] ${msg}`);
|
|
@@ -225,7 +266,7 @@ function readConfigFromDisk() {
|
|
|
225
266
|
// Let's REMOVE this auto-downgrade too to be strictly "Decoupled".
|
|
226
267
|
// If user is in PRO without key, they get "Missing Key" error, which is correct.
|
|
227
268
|
}
|
|
228
|
-
return { ...config, version: PKG_VERSION };
|
|
269
|
+
return migrateV65Config({ ...config, version: PKG_VERSION });
|
|
229
270
|
}
|
|
230
271
|
export function saveConfig(updates) {
|
|
231
272
|
try {
|
|
@@ -4,21 +4,21 @@ export async function buildConnectResponse(config) {
|
|
|
4
4
|
const hasKey = !!config.apiKey;
|
|
5
5
|
const mode = config.mode;
|
|
6
6
|
let name = "Developer";
|
|
7
|
-
let
|
|
8
|
-
let
|
|
7
|
+
let questEmoji = '🎁';
|
|
8
|
+
let questText = 'Quest/Paid';
|
|
9
9
|
if (hasKey) {
|
|
10
10
|
try {
|
|
11
|
-
//
|
|
11
|
+
// v6.5: Quest/Paid semantics (tier/refill model removed upstream).
|
|
12
12
|
const quota = await getQuotaStatus(true);
|
|
13
|
-
|
|
14
|
-
tierEmoji = quota.tierEmoji || '👤';
|
|
13
|
+
questText = `Quest ~${quota.questBalance.toFixed(2)} | Paid ~${quota.walletBalance.toFixed(2)}`;
|
|
15
14
|
}
|
|
16
15
|
catch (e) {
|
|
17
16
|
// Ignorer l'erreur réseau et garder les valeurs par défaut
|
|
18
17
|
}
|
|
19
18
|
try {
|
|
20
19
|
const res = await fetch('https://gen.pollinations.ai/account/profile', {
|
|
21
|
-
headers: { 'Authorization': `Bearer ${config.apiKey}` }
|
|
20
|
+
headers: { 'Authorization': `Bearer ${config.apiKey}` },
|
|
21
|
+
signal: AbortSignal.timeout(5000),
|
|
22
22
|
});
|
|
23
23
|
if (res.ok) {
|
|
24
24
|
const data = await res.json();
|
|
@@ -33,7 +33,7 @@ export async function buildConnectResponse(config) {
|
|
|
33
33
|
if (hasKey) {
|
|
34
34
|
return `${t('connect_response.title_key', { name, mode })}
|
|
35
35
|
|
|
36
|
-
> **Your
|
|
36
|
+
> **Your Pollen:** ${questEmoji} ${questText}
|
|
37
37
|
|
|
38
38
|
---
|
|
39
39
|
|
|
@@ -15,6 +15,13 @@ interface OpenCodeModel {
|
|
|
15
15
|
attachment?: boolean;
|
|
16
16
|
tool_call?: boolean;
|
|
17
17
|
reasoning?: boolean;
|
|
18
|
+
reasoning_options?: Array<{
|
|
19
|
+
type: 'effort' | 'toggle';
|
|
20
|
+
values?: string[];
|
|
21
|
+
}>;
|
|
22
|
+
interleaved?: {
|
|
23
|
+
field: 'reasoning' | 'reasoning_content' | 'reasoning_details';
|
|
24
|
+
};
|
|
18
25
|
temperature?: boolean;
|
|
19
26
|
}
|
|
20
27
|
export declare function generatePollinationsConfig(forceApiKey?: string, forceStrict?: boolean): Promise<OpenCodeModel[]>;
|
|
@@ -206,22 +206,28 @@ function mapModel(raw, prefix, namePrefix) {
|
|
|
206
206
|
modelObj.modalities.input.push('image');
|
|
207
207
|
}
|
|
208
208
|
}
|
|
209
|
-
// 2. REASONING
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
if (
|
|
220
|
-
modelObj.
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
209
|
+
// 2. NATIVE REASONING — capabilities come from the live Model Registry.
|
|
210
|
+
// OpenCode keeps reasoning as structured parts; it must never be merged into content.
|
|
211
|
+
const supportsReasoning = raw.reasoning === true || raw.capabilities?.includes('reasoning');
|
|
212
|
+
modelObj.reasoning = supportsReasoning;
|
|
213
|
+
if (supportsReasoning) {
|
|
214
|
+
const advertised = Array.isArray(raw.reasoning_options) ? raw.reasoning_options : [];
|
|
215
|
+
const effectiveReasoningOptions = advertised.length > 0
|
|
216
|
+
? advertised
|
|
217
|
+
: [{ type: 'effort', values: ['low', 'high'] }];
|
|
218
|
+
modelObj.reasoning_options = effectiveReasoningOptions;
|
|
219
|
+
if (raw.interleaved?.field) {
|
|
220
|
+
modelObj.interleaved = raw.interleaved;
|
|
221
|
+
}
|
|
222
|
+
const effortValues = effectiveReasoningOptions
|
|
223
|
+
.filter(option => option.type === 'effort')
|
|
224
|
+
.flatMap(option => option.values || []);
|
|
225
|
+
for (const effort of effortValues) {
|
|
226
|
+
modelObj.variants[effort] = { reasoningEffort: effort };
|
|
227
|
+
}
|
|
228
|
+
if (effectiveReasoningOptions.some(option => option.type === 'toggle')) {
|
|
229
|
+
modelObj.variants.off = { reasoningEffort: 'none' };
|
|
230
|
+
modelObj.variants.on = {};
|
|
225
231
|
}
|
|
226
232
|
}
|
|
227
233
|
// 3. SAFETY VARIANTS — maxTokens for models with known limits
|