opencode-pollinations-plugin 6.1.0-beta.9 → 6.2.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 +130 -0
- package/README.es.md +130 -0
- package/README.fr.md +130 -0
- package/README.it.md +130 -0
- package/README.md +87 -73
- package/dist/index.js +52 -161
- package/dist/locales/de.json +374 -0
- package/dist/locales/en.json +373 -0
- package/dist/locales/es.json +374 -0
- package/dist/locales/fr.json +373 -0
- package/dist/locales/index.d.ts +1 -0
- package/dist/locales/index.js +37 -0
- package/dist/locales/it.json +374 -0
- package/dist/server/commands.d.ts +6 -0
- package/dist/server/commands.js +394 -125
- package/dist/server/config.d.ts +34 -23
- package/dist/server/config.js +200 -108
- package/dist/server/connect-response.d.ts +2 -0
- package/dist/server/connect-response.js +59 -0
- package/dist/server/generate-config.d.ts +3 -30
- package/dist/server/generate-config.js +164 -106
- package/dist/server/index.d.ts +2 -1
- package/dist/server/index.js +124 -149
- package/dist/server/logger.d.ts +8 -0
- package/dist/server/logger.js +38 -0
- package/dist/server/models/cache.d.ts +35 -0
- package/dist/server/models/cache.js +160 -0
- package/dist/server/models/fetcher.d.ts +18 -0
- package/dist/server/models/fetcher.js +194 -0
- package/dist/server/models/index.d.ts +6 -0
- package/dist/server/models/index.js +5 -0
- package/dist/server/models/manual.d.ts +15 -0
- package/dist/server/models/manual.js +92 -0
- package/dist/server/models/types.d.ts +55 -0
- package/dist/server/models/types.js +7 -0
- package/dist/server/models/worker.d.ts +22 -0
- package/dist/server/models/worker.js +174 -0
- package/dist/server/pollinations-api.d.ts +11 -0
- package/dist/server/pollinations-api.js +21 -8
- package/dist/server/proxy.js +222 -307
- package/dist/server/quota.d.ts +2 -0
- package/dist/server/quota.js +89 -86
- package/dist/server/scripts/pollinations_pricing.d.ts +8 -0
- package/dist/server/scripts/pollinations_pricing.js +246 -0
- package/dist/server/scripts/test_cost_endpoints.d.ts +1 -0
- package/dist/server/scripts/test_cost_endpoints.js +61 -0
- package/dist/server/scripts/test_dynamic_pricing.d.ts +1 -0
- package/dist/server/scripts/test_dynamic_pricing.js +39 -0
- package/dist/server/scripts/test_freetier_audit.d.ts +11 -0
- package/dist/server/scripts/test_freetier_audit.js +215 -0
- package/dist/server/scripts/test_parallel_cost.d.ts +1 -0
- package/dist/server/scripts/test_parallel_cost.js +104 -0
- package/dist/server/toast.d.ts +7 -1
- package/dist/server/toast.js +43 -10
- 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/ffmpeg.d.ts +24 -0
- package/dist/tools/ffmpeg.js +54 -0
- package/dist/tools/index.d.ts +25 -0
- package/dist/tools/index.js +86 -0
- package/dist/tools/pollinations/beta_discovery.d.ts +9 -0
- package/dist/tools/pollinations/beta_discovery.js +201 -0
- package/dist/tools/pollinations/cost-guard.d.ts +38 -0
- package/dist/tools/pollinations/cost-guard.js +136 -0
- package/dist/tools/pollinations/deepsearch.d.ts +7 -0
- package/dist/tools/pollinations/deepsearch.js +80 -0
- package/dist/tools/pollinations/gen_audio.d.ts +18 -0
- package/dist/tools/pollinations/gen_audio.js +220 -0
- package/dist/tools/pollinations/gen_image.d.ts +11 -0
- package/dist/tools/pollinations/gen_image.js +211 -0
- package/dist/tools/pollinations/gen_music.d.ts +14 -0
- package/dist/tools/pollinations/gen_music.js +157 -0
- package/dist/tools/pollinations/gen_video.d.ts +16 -0
- package/dist/tools/pollinations/gen_video.js +249 -0
- package/dist/tools/pollinations/polli_config.d.ts +2 -0
- package/dist/tools/pollinations/polli_config.js +95 -0
- package/dist/tools/pollinations/polli_gen_confirm.d.ts +2 -0
- package/dist/tools/pollinations/polli_gen_confirm.js +48 -0
- package/dist/tools/pollinations/polli_status.d.ts +2 -0
- package/dist/tools/pollinations/polli_status.js +31 -0
- package/dist/tools/pollinations/polli_web_search.d.ts +15 -0
- package/dist/tools/pollinations/polli_web_search.js +126 -0
- package/dist/tools/pollinations/search_crawl_scrape.d.ts +7 -0
- package/dist/tools/pollinations/search_crawl_scrape.js +85 -0
- package/dist/tools/pollinations/shared.d.ts +181 -0
- package/dist/tools/pollinations/shared.js +758 -0
- package/dist/tools/pollinations/test_estimators.d.ts +1 -0
- package/dist/tools/pollinations/test_estimators.js +22 -0
- package/dist/tools/pollinations/transcribe_audio.d.ts +13 -0
- package/dist/tools/pollinations/transcribe_audio.js +171 -0
- package/dist/tools/power/extract_audio.d.ts +2 -0
- package/dist/tools/power/extract_audio.js +179 -0
- package/dist/tools/power/extract_frames.d.ts +2 -0
- package/dist/tools/power/extract_frames.js +237 -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 +404 -0
- package/dist/tools/power/rmbg_keys.d.ts +2 -0
- package/dist/tools/power/rmbg_keys.js +79 -0
- package/dist/tools/shared.d.ts +30 -0
- package/dist/tools/shared.js +80 -0
- package/package.json +9 -3
- package/dist/server/models-seed.d.ts +0 -18
- package/dist/server/models-seed.js +0 -55
package/dist/server/commands.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import * as https from 'https';
|
|
2
|
-
import { loadConfig, saveConfig } from './config.js';
|
|
3
|
-
import { getQuotaStatus } from './quota.js';
|
|
2
|
+
import { loadConfig, saveConfig, saveKeyToAuthJson } from './config.js';
|
|
3
|
+
import { getQuotaStatus, fetchUsageForPeriod } from './quota.js';
|
|
4
4
|
import { emitStatusToast } from './toast.js';
|
|
5
|
-
import { getDetailedUsage, getAggregatedModels } from './pollinations-api.js';
|
|
6
5
|
import { generatePollinationsConfig } from './generate-config.js';
|
|
6
|
+
import { ModelRegistry } from './models/index.js';
|
|
7
|
+
import { t } from '../locales/index.js';
|
|
7
8
|
function checkEndpoint(ep, key) {
|
|
8
9
|
return new Promise((resolve) => {
|
|
9
10
|
const req = https.request({
|
|
@@ -45,18 +46,16 @@ function checkEndpoint(ep, key) {
|
|
|
45
46
|
});
|
|
46
47
|
}
|
|
47
48
|
export async function checkKeyPermissions(key) {
|
|
48
|
-
//
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
if (!res.ok) {
|
|
53
|
-
return { ok: false, reason: `${ep} (${res.status})` };
|
|
54
|
-
}
|
|
49
|
+
// SINGLE CHECK to reduce latency and avoid rate-limits (HIGH-01)
|
|
50
|
+
const res = await checkEndpoint('/account/profile', key);
|
|
51
|
+
if (!res.ok) {
|
|
52
|
+
return { ok: false, reason: `/account/profile (${res.status})` };
|
|
55
53
|
}
|
|
56
54
|
return { ok: true };
|
|
57
55
|
}
|
|
58
56
|
// === CONSTANTS & PRICING ===
|
|
59
57
|
const TIER_LIMITS = {
|
|
58
|
+
microbe: { pollen: 0.1, emoji: '🦠' },
|
|
60
59
|
spore: { pollen: 1, emoji: '🦠' },
|
|
61
60
|
seed: { pollen: 3, emoji: '🌱' },
|
|
62
61
|
flower: { pollen: 10, emoji: '🌸' },
|
|
@@ -130,6 +129,10 @@ function calculateCurrentPeriodStats(usage, lastReset, tierLimit) {
|
|
|
130
129
|
};
|
|
131
130
|
}
|
|
132
131
|
// === COMMAND HANDLER ===
|
|
132
|
+
let globalClient = null;
|
|
133
|
+
export function setClientForCommands(client) {
|
|
134
|
+
globalClient = client;
|
|
135
|
+
}
|
|
133
136
|
export async function handleCommand(command) {
|
|
134
137
|
const parts = command.trim().split(/\s+/);
|
|
135
138
|
if (!parts[0].startsWith('/poll')) {
|
|
@@ -145,48 +148,59 @@ export async function handleCommand(command) {
|
|
|
145
148
|
case 'connect':
|
|
146
149
|
return await handleConnectCommand(args);
|
|
147
150
|
case 'fallback':
|
|
148
|
-
return
|
|
151
|
+
return handleFallbackCommand(args);
|
|
149
152
|
case 'config':
|
|
150
153
|
return handleConfigCommand(args);
|
|
151
154
|
case 'help':
|
|
152
155
|
return handleHelpCommand();
|
|
156
|
+
case 'models':
|
|
157
|
+
return await handleModelsCommand(args);
|
|
158
|
+
case 'pricing':
|
|
159
|
+
return await handlePricingCommand();
|
|
160
|
+
case 'infos':
|
|
161
|
+
return await handleInfosCommand();
|
|
162
|
+
case 'addKey': // External trigger
|
|
163
|
+
// UI Pollution Fix: User hates appendPrompt.
|
|
164
|
+
// Just return a message telling them to use the tool.
|
|
165
|
+
return {
|
|
166
|
+
handled: true,
|
|
167
|
+
response: t('commands.generic.add_key_hint')
|
|
168
|
+
};
|
|
153
169
|
default:
|
|
154
170
|
return {
|
|
155
171
|
handled: true,
|
|
156
|
-
|
|
172
|
+
response: t('commands.generic.unknown_command', { cmd: subCommand })
|
|
157
173
|
};
|
|
158
174
|
}
|
|
159
175
|
}
|
|
160
176
|
// === SUB-COMMANDS ===
|
|
161
177
|
async function handleModeCommand(args) {
|
|
162
|
-
|
|
178
|
+
const mode = args[0];
|
|
163
179
|
if (!mode) {
|
|
164
180
|
const config = loadConfig();
|
|
165
181
|
return {
|
|
166
182
|
handled: true,
|
|
167
|
-
response:
|
|
183
|
+
response: t('commands.mode.current', { mode: config.mode })
|
|
168
184
|
};
|
|
169
185
|
}
|
|
170
|
-
if (
|
|
171
|
-
mode = 'economy'; // Alias support
|
|
172
|
-
if (!['manual', 'economy', 'pro'].includes(mode)) {
|
|
186
|
+
if (!['manual', 'alwaysfree', 'pro'].includes(mode)) {
|
|
173
187
|
return {
|
|
174
188
|
handled: true,
|
|
175
|
-
error:
|
|
189
|
+
error: t('commands.mode.invalid', { mode })
|
|
176
190
|
};
|
|
177
191
|
}
|
|
178
192
|
const checkConfig = loadConfig();
|
|
179
|
-
// JIT VERIFICATION for PRO and
|
|
180
|
-
if (mode === 'pro' || mode === '
|
|
193
|
+
// JIT VERIFICATION for PRO and ALWAYSFREE Mode
|
|
194
|
+
if (mode === 'pro' || mode === 'alwaysfree') {
|
|
181
195
|
const checkConfig = loadConfig(); // Reload to be sure
|
|
182
196
|
const key = checkConfig.apiKey;
|
|
183
197
|
if (!key) {
|
|
184
198
|
// If NO key, allow alwaysfree? Yes.
|
|
185
199
|
// If HAS key, verify it? Yes.
|
|
186
200
|
if (mode === 'pro')
|
|
187
|
-
return { handled: true, error:
|
|
201
|
+
return { handled: true, error: t('commands.mode.pro_requires_key') };
|
|
188
202
|
}
|
|
189
|
-
emitStatusToast('info', '
|
|
203
|
+
emitStatusToast('info', t('commands.mode.verifying'), 'Mode Pro');
|
|
190
204
|
try {
|
|
191
205
|
// Force verify permissions NOW
|
|
192
206
|
const check = await checkKeyPermissions(key);
|
|
@@ -194,28 +208,28 @@ async function handleModeCommand(args) {
|
|
|
194
208
|
saveConfig({ mode: 'manual', keyHasAccessToProfile: false });
|
|
195
209
|
return {
|
|
196
210
|
handled: true,
|
|
197
|
-
error:
|
|
211
|
+
error: t('commands.mode.denied', { status: check.status || '?', reason: check.reason || '?' })
|
|
198
212
|
};
|
|
199
213
|
}
|
|
200
214
|
// Valid -> Ensure flag is true
|
|
201
215
|
saveConfig({ keyHasAccessToProfile: true });
|
|
202
216
|
}
|
|
203
217
|
catch (e) {
|
|
204
|
-
return { handled: true, error:
|
|
218
|
+
return { handled: true, error: t('commands.mode.verify_error', { error: e.message }) };
|
|
205
219
|
}
|
|
206
220
|
}
|
|
207
|
-
// Allow switch (if
|
|
221
|
+
// Allow switch (if alwaysfree or manual, or verified pro)
|
|
208
222
|
saveConfig({ mode: mode });
|
|
209
223
|
const config = loadConfig();
|
|
210
224
|
if (config.gui.status !== 'none') {
|
|
211
|
-
emitStatusToast('success',
|
|
225
|
+
emitStatusToast('success', t('commands.mode.success', { mode }), 'Pollinations Config');
|
|
212
226
|
}
|
|
213
227
|
return {
|
|
214
228
|
handled: true,
|
|
215
|
-
response:
|
|
229
|
+
response: t('commands.mode.success', { mode })
|
|
216
230
|
};
|
|
217
231
|
}
|
|
218
|
-
async function handleUsageCommand(args) {
|
|
232
|
+
export async function handleUsageCommand(args) {
|
|
219
233
|
const isFull = args[0] === 'full';
|
|
220
234
|
try {
|
|
221
235
|
const quota = await getQuotaStatus(true);
|
|
@@ -223,41 +237,41 @@ async function handleUsageCommand(args) {
|
|
|
223
237
|
const resetDate = quota.nextResetAt.toLocaleString('fr-FR', { hour: '2-digit', minute: '2-digit' });
|
|
224
238
|
const timeUntilReset = quota.nextResetAt.getTime() - Date.now();
|
|
225
239
|
const durationStr = formatDuration(Math.max(0, timeUntilReset));
|
|
226
|
-
let response =
|
|
227
|
-
response +=
|
|
228
|
-
response +=
|
|
229
|
-
response +=
|
|
230
|
-
response +=
|
|
231
|
-
response +=
|
|
232
|
-
response +=
|
|
240
|
+
let response = t('commands.usage.title', { mode: config.mode.toUpperCase() });
|
|
241
|
+
response += t('commands.usage.resources');
|
|
242
|
+
response += t('commands.usage.tier', { emoji: quota.tierEmoji, tier: quota.tier.toUpperCase(), limit: quota.tierLimit });
|
|
243
|
+
response += t('commands.usage.quota', { remaining: formatPollen(quota.tierLimit - quota.tierRemaining), limit: formatPollen(quota.tierLimit) });
|
|
244
|
+
response += t('commands.usage.usage_bar', { bar: progressBar(quota.tierLimit - quota.tierRemaining, quota.tierLimit) });
|
|
245
|
+
response += t('commands.usage.wallet', { balance: quota.walletBalance.toFixed(2) });
|
|
246
|
+
response += t('commands.usage.reset', { date: resetDate, duration: durationStr });
|
|
233
247
|
if (isFull && config.apiKey) {
|
|
234
248
|
if (config.keyHasAccessToProfile === false) {
|
|
235
|
-
response +=
|
|
249
|
+
response += t('commands.usage.restricted_key');
|
|
236
250
|
}
|
|
237
251
|
else {
|
|
238
|
-
const
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
const stats = calculateCurrentPeriodStats(usageData
|
|
242
|
-
response +=
|
|
243
|
-
response +=
|
|
244
|
-
response +=
|
|
245
|
-
response +=
|
|
252
|
+
const lastReset = calculateResetDate(quota.nextResetAt);
|
|
253
|
+
const usageData = await fetchUsageForPeriod(config.apiKey, lastReset);
|
|
254
|
+
if (usageData && usageData.length > 0) {
|
|
255
|
+
const stats = calculateCurrentPeriodStats(usageData, lastReset, quota.tierLimit);
|
|
256
|
+
response += t('commands.usage.period_detail', { time: lastReset.toLocaleTimeString() });
|
|
257
|
+
response += t('commands.usage.total_reqs', { reqs: stats.totalRequests, inTok: formatTokens(stats.inputTokens), outTok: formatTokens(stats.outputTokens) });
|
|
258
|
+
response += t('commands.usage.table_head1');
|
|
259
|
+
response += t('commands.usage.table_head2');
|
|
246
260
|
const sorted = Array.from(stats.models.entries()).sort((a, b) => b[1].cost - a[1].cost);
|
|
247
261
|
for (const [model, data] of sorted) {
|
|
248
262
|
response += `| \`${model}\` | ${data.requests} | ${formatPollen(data.cost)} | ${formatTokens(data.inputTokens + data.outputTokens)} |\n`;
|
|
249
263
|
}
|
|
250
264
|
}
|
|
251
265
|
else {
|
|
252
|
-
response +=
|
|
266
|
+
response += t('commands.usage.no_history');
|
|
253
267
|
}
|
|
254
268
|
}
|
|
255
269
|
}
|
|
256
270
|
else if (isFull) {
|
|
257
|
-
response +=
|
|
271
|
+
response += t('commands.usage.full_requires_key');
|
|
258
272
|
}
|
|
259
273
|
else {
|
|
260
|
-
response +=
|
|
274
|
+
response += t('commands.usage.hint_full');
|
|
261
275
|
}
|
|
262
276
|
return { handled: true, response: response.trim() };
|
|
263
277
|
}
|
|
@@ -265,44 +279,33 @@ async function handleUsageCommand(args) {
|
|
|
265
279
|
return { handled: true, error: `Erreur: ${e}` };
|
|
266
280
|
}
|
|
267
281
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
282
|
+
function handleFallbackCommand(args) {
|
|
283
|
+
const [main, agent] = args;
|
|
284
|
+
if (!main) {
|
|
285
|
+
const config = loadConfig();
|
|
286
|
+
const freeConfig = `Free: main=${config.fallbacks.free.main}, agent=${config.fallbacks.free.agent}`;
|
|
287
|
+
const enterConfig = `Enter: agent=${config.fallbacks.enter.agent}`;
|
|
272
288
|
return {
|
|
273
289
|
handled: true,
|
|
274
|
-
|
|
290
|
+
response: t('commands.fallback.current', { free: freeConfig, enter: enterConfig })
|
|
275
291
|
};
|
|
276
292
|
}
|
|
277
|
-
//
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
if (targetMode !== 'economy' && targetMode !== 'pro') {
|
|
281
|
-
return { handled: true, error: 'Target mode invalide (economy ou pro)' };
|
|
282
|
-
}
|
|
283
|
-
// DYNAMIC MODEL VALIDATION (BUG 2 FIX)
|
|
284
|
-
try {
|
|
285
|
-
const { data: models } = await getAggregatedModels();
|
|
286
|
-
const validIds = models.map(m => m.id.replace(/^pollinations\/(free|enter)\//, ''));
|
|
287
|
-
if (!validIds.includes(targetModel)) {
|
|
288
|
-
const suggestions = validIds.slice(0, 10).join(', ');
|
|
289
|
-
return {
|
|
290
|
-
handled: true,
|
|
291
|
-
error: `⚠️ Modèle inconnu: "${targetModel}".\nExemples valides: ${suggestions}...`
|
|
292
|
-
};
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
catch (e) {
|
|
296
|
-
// Fail-open si API down
|
|
297
|
-
console.warn('[Fallback] Model validation failed, allowing:', e);
|
|
298
|
-
}
|
|
293
|
+
// Default behavior for "/poll fallback <model> <agent>" is setting FREE fallbacks
|
|
294
|
+
// User needs to use commands (maybe add /poll fallback enter ...) later
|
|
295
|
+
// For now, map to Free Fallback as it's the primary Safety Net
|
|
299
296
|
const config = loadConfig();
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
297
|
+
saveConfig({
|
|
298
|
+
fallbacks: {
|
|
299
|
+
...config.fallbacks,
|
|
300
|
+
free: {
|
|
301
|
+
main: main,
|
|
302
|
+
agent: agent || config.fallbacks.free.agent
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
});
|
|
303
306
|
return {
|
|
304
307
|
handled: true,
|
|
305
|
-
response:
|
|
308
|
+
response: t('commands.fallback.success', { main, agent: agent || config.fallbacks.free.agent })
|
|
306
309
|
};
|
|
307
310
|
}
|
|
308
311
|
async function handleConnectCommand(args) {
|
|
@@ -310,21 +313,22 @@ async function handleConnectCommand(args) {
|
|
|
310
313
|
if (!key) {
|
|
311
314
|
return {
|
|
312
315
|
handled: true,
|
|
313
|
-
error:
|
|
316
|
+
error: t('commands.connect.usage')
|
|
314
317
|
};
|
|
315
318
|
}
|
|
316
319
|
// 1. Universal Validation (No Syntax Check) - Functional Check
|
|
317
|
-
emitStatusToast('info', '
|
|
320
|
+
emitStatusToast('info', t('commands.connect.verifying'), 'Pollinations Config');
|
|
318
321
|
try {
|
|
319
|
-
const models = await generatePollinationsConfig(key);
|
|
320
|
-
// 2. Check if we got
|
|
321
|
-
const
|
|
322
|
-
if (
|
|
322
|
+
const models = await generatePollinationsConfig(key, true);
|
|
323
|
+
// 2. Check if we got Enterprise models
|
|
324
|
+
const enterpriseModels = models.filter(m => m.id.startsWith('enter/'));
|
|
325
|
+
if (enterpriseModels.length > 0) {
|
|
323
326
|
// SUCCESS
|
|
324
327
|
saveConfig({ apiKey: key }); // Don't force mode 'pro'. Let user decide.
|
|
328
|
+
saveKeyToAuthJson(key); // NATIVE SYNC: Hot-reload on OpenCode bypasses restart requirement !
|
|
325
329
|
const masked = key.substring(0, 6) + '...';
|
|
326
330
|
// Count Paid Only models found
|
|
327
|
-
const diamondCount =
|
|
331
|
+
const diamondCount = enterpriseModels.filter(m => m.name.includes('💎')).length;
|
|
328
332
|
// CHECK RESTRICTIONS: Strict Check (Usage + Profile + Balance)
|
|
329
333
|
let forcedModeMsg = "";
|
|
330
334
|
let isLimited = false;
|
|
@@ -344,15 +348,15 @@ async function handleConnectCommand(args) {
|
|
|
344
348
|
// If Limited -> FORCE MANUAL
|
|
345
349
|
if (isLimited) {
|
|
346
350
|
saveConfig({ apiKey: key, mode: 'manual', keyHasAccessToProfile: false });
|
|
347
|
-
forcedModeMsg =
|
|
351
|
+
forcedModeMsg = t('commands.connect.limited', { reason: limitReason });
|
|
348
352
|
}
|
|
349
353
|
else {
|
|
350
354
|
saveConfig({ apiKey: key, keyHasAccessToProfile: true }); // Let user keep current mode or default
|
|
351
355
|
}
|
|
352
|
-
emitStatusToast('success',
|
|
356
|
+
emitStatusToast('success', t('commands.connect.success_toast', { count: enterpriseModels.length }), 'Pollinations Config');
|
|
353
357
|
return {
|
|
354
358
|
handled: true,
|
|
355
|
-
response:
|
|
359
|
+
response: t('commands.connect.success_response', { key: masked, count: enterpriseModels.length, diamond: diamondCount, forced_msg: forcedModeMsg })
|
|
356
360
|
};
|
|
357
361
|
}
|
|
358
362
|
else {
|
|
@@ -364,17 +368,27 @@ async function handleConnectCommand(args) {
|
|
|
364
368
|
// Wait, generate-config falls back to providing a list containing "[Enter] GPT-4o (Fallback)" if fetch failed.
|
|
365
369
|
// So we need to detect if it's a "REAL" fetch or a "FALLBACK" fetch.
|
|
366
370
|
// The fallback models have `variants: {}` usually, but real ones might too.
|
|
367
|
-
//
|
|
368
|
-
|
|
371
|
+
// A better check: The fallback list is hardcoded in generate-config.ts catch block.
|
|
372
|
+
// Let's modify generate-config to return EMPTY list on error?
|
|
373
|
+
// Or just check if the returned models work?
|
|
374
|
+
// Simplest: If `generatePollinationsConfig` returns any model starting with `enter/` that includes "(Fallback)" in name, we assume failure?
|
|
375
|
+
// "GPT-4o (Fallback)" is the name.
|
|
376
|
+
const isFallback = models.some(m => m.name.includes('(Fallback)') && m.id.startsWith('enter/'));
|
|
377
|
+
if (isFallback) {
|
|
378
|
+
throw new Error(t('proxy.errors.key_rejected'));
|
|
379
|
+
}
|
|
380
|
+
// If we are here, we got no enter models, or empty list?
|
|
381
|
+
// If key is valid but has no access?
|
|
382
|
+
throw new Error(t('proxy.errors.no_enter_models'));
|
|
369
383
|
}
|
|
370
384
|
}
|
|
371
385
|
catch (e) {
|
|
372
386
|
// 3. FAILURE HANDLING - Revert to FREE
|
|
373
387
|
saveConfig({ apiKey: undefined, mode: 'manual' }); // Clear Key, Set Manual
|
|
374
|
-
emitStatusToast('error',
|
|
388
|
+
emitStatusToast('error', t('toasts.invalid_key_revert'), 'Pollinations Config');
|
|
375
389
|
return {
|
|
376
390
|
handled: true,
|
|
377
|
-
error:
|
|
391
|
+
error: t('proxy.errors.invalid_key_free_mode', { error: e.message || String(e) })
|
|
378
392
|
};
|
|
379
393
|
}
|
|
380
394
|
}
|
|
@@ -382,11 +396,40 @@ function handleConfigCommand(args) {
|
|
|
382
396
|
const [key, value] = args;
|
|
383
397
|
if (!key) {
|
|
384
398
|
const config = loadConfig();
|
|
399
|
+
const k = config.apiKey ? (config.apiKey.length > 8 ? `${config.apiKey.substring(0, 5)}****${config.apiKey.substring(config.apiKey.length - 4)}` : '****') : t('commands.config.not_configured');
|
|
400
|
+
const markdownResponse = `${t('commands.config.title', { version: config.version || 'inconnue' })}
|
|
401
|
+
${t('commands.config.alias_note')}
|
|
402
|
+
${t('commands.config.intro')}
|
|
403
|
+
|
|
404
|
+
${t('commands.config.table_headers')}
|
|
405
|
+
${t('commands.config.table_divider')}
|
|
406
|
+
| **apiKey** | \`${k}\` | ${t('commands.config.api_key_role')} | \`/poll connect <key>\` |
|
|
407
|
+
| **mode** | \`${config.mode}\` | ${t('commands.config.mode_role')} | \`/poll mode <manual/pro/alwaysfree>\` |
|
|
408
|
+
| **enablePaidTools**| \`${config.enablePaidTools ?? true}\` | ${t('commands.config.enablePaidTools_role')} | \`/poll config enablePaidTools <true/false>\` |
|
|
409
|
+
| **costConfirmationRequired**| \`${config.costConfirmationRequired ?? true}\` | ${t('commands.config.costConfirmationRequired_role')} | \`/poll config costConfirmationRequired <true/false>\` |
|
|
410
|
+
| **costThreshold**| \`${config.costThreshold ?? 0.15} 🌻\` | ${t('commands.config.costThreshold_role')} | \`/poll config costThreshold <X>\` |
|
|
411
|
+
| **cost_estimator**| \`${config.costEstimator ?? true}\` | ${t('commands.config.cost_estimator_role')} | \`/poll config cost_estimator <true/false>\` |
|
|
412
|
+
| **fallbacks.free.main** | \`${config.fallbacks?.free?.main || 'free/mistral'}\` | ${t('commands.config.fallback_main_role')} | \`/poll fallback <main> <agent>\` |
|
|
413
|
+
| **fallbacks.free.agent** | \`${config.fallbacks?.free?.agent || 'free/openai-fast'}\`| ${t('commands.config.fallback_agent_role')} | \`/poll fallback <main> <agent>\` |
|
|
414
|
+
| **fallbacks.enter.agent** | \`${config.fallbacks?.enter?.agent || 'free/openai-fast'}\`| ${t('commands.config.fallback_enter_role')} | *${t('commands.config.managed_auto')}* |
|
|
415
|
+
| **status_gui** | \`${config.gui?.status || 'all'}\` | ${t('commands.config.status_gui_role')} | \`/poll config status_gui <all/alert/none>\` |
|
|
416
|
+
| **logs_gui** | \`${config.gui?.logs || 'error'}\` | ${t('commands.config.logs_gui_role')} | \`/poll config logs_gui <verbose/error/none>\` |
|
|
417
|
+
| **threshold_tier** | \`${config.thresholds?.tier || 80}%\` | ${t('commands.config.threshold_tier_role')} | \`/poll config threshold_tier <1-100>\` |
|
|
418
|
+
| **threshold_wallet** | \`${config.thresholds?.wallet || 80}%\` | ${t('commands.config.threshold_wallet_role')} | \`/poll config threshold_wallet <1-100>\` |
|
|
419
|
+
| **status_bar** | \`${config.statusBar ?? true}\` | ${t('commands.config.status_bar_role')} | \`/poll config status_bar <true/false>\` |
|
|
420
|
+
| **lang** | \`${config.lang || 'en'}\` | ${t('commands.config.lang_role')} | \`/poll config lang <en/fr/es/de/it>\` |`;
|
|
385
421
|
return {
|
|
386
422
|
handled: true,
|
|
387
|
-
response:
|
|
423
|
+
response: markdownResponse
|
|
388
424
|
};
|
|
389
425
|
}
|
|
426
|
+
if (key === 'lang' && value) {
|
|
427
|
+
if (!['en', 'fr', 'es', 'de', 'it'].includes(value)) {
|
|
428
|
+
return { handled: true, error: "Valeurs supportées: en, fr, es, de, it" };
|
|
429
|
+
}
|
|
430
|
+
saveConfig({ lang: value });
|
|
431
|
+
return { handled: true, response: `✅ lang = ${value} (redémarrage recommandé)` };
|
|
432
|
+
}
|
|
390
433
|
if (key === 'toast_verbosity' && value) {
|
|
391
434
|
// BACKWARD COMPAT (Maps to Status GUI)
|
|
392
435
|
if (!['none', 'alert', 'all'].includes(value)) {
|
|
@@ -424,65 +467,291 @@ function handleConfigCommand(args) {
|
|
|
424
467
|
saveConfig({ thresholds: { ...config.thresholds, tier: threshold } });
|
|
425
468
|
return { handled: true, response: `✅ threshold_tier = ${threshold}%` };
|
|
426
469
|
}
|
|
427
|
-
if (key === '
|
|
470
|
+
if (key === 'threshold_wallet' && value) {
|
|
428
471
|
const threshold = parseInt(value);
|
|
429
472
|
if (isNaN(threshold) || threshold < 0 || threshold > 100) {
|
|
430
473
|
return { handled: true, error: 'Valeur entre 0 et 100 requise' };
|
|
431
474
|
}
|
|
432
475
|
const config = loadConfig();
|
|
433
|
-
saveConfig({ thresholds: { ...config.thresholds,
|
|
434
|
-
return { handled: true, response: `✅
|
|
435
|
-
}
|
|
436
|
-
if (key === 'threshold_wallet_stop' && value) {
|
|
437
|
-
const stopValue = parseFloat(value);
|
|
438
|
-
if (isNaN(stopValue) || stopValue < 0) {
|
|
439
|
-
return { handled: true, error: 'Valeur $ positive requise' };
|
|
440
|
-
}
|
|
441
|
-
const config = loadConfig();
|
|
442
|
-
saveConfig({ thresholds: { ...config.thresholds, wallet_stop: stopValue } });
|
|
443
|
-
return { handled: true, response: `✅ threshold_wallet_stop = $${stopValue}` };
|
|
476
|
+
saveConfig({ thresholds: { ...config.thresholds, wallet: threshold } });
|
|
477
|
+
return { handled: true, response: `✅ threshold_wallet = ${threshold}%` };
|
|
444
478
|
}
|
|
445
479
|
if (key === 'status_bar' && value) {
|
|
446
480
|
const enabled = value === 'true';
|
|
447
481
|
saveConfig({ statusBar: enabled });
|
|
448
482
|
return { handled: true, response: `✅ status_bar = ${enabled}` };
|
|
449
483
|
}
|
|
484
|
+
if (key === 'cost_estimator' && value) {
|
|
485
|
+
const enabled = value === 'true';
|
|
486
|
+
const config = loadConfig();
|
|
487
|
+
saveConfig({ ...config, costEstimator: enabled });
|
|
488
|
+
return { handled: true, response: `✅ cost_estimator = ${enabled}` };
|
|
489
|
+
}
|
|
490
|
+
if (key === 'enablePaidTools' && value) {
|
|
491
|
+
const enabled = value === 'true';
|
|
492
|
+
saveConfig({ enablePaidTools: enabled });
|
|
493
|
+
return { handled: true, response: `✅ enablePaidTools = ${enabled}${!enabled ? ' (wallet protection active)' : ''}` };
|
|
494
|
+
}
|
|
495
|
+
if (key === 'costThreshold' && value) {
|
|
496
|
+
const threshold = parseFloat(value);
|
|
497
|
+
if (isNaN(threshold) || threshold < 0) {
|
|
498
|
+
return { handled: true, error: 'Valeur numérique positive requise (en pollen). Ex: 0.15' };
|
|
499
|
+
}
|
|
500
|
+
saveConfig({ costThreshold: threshold });
|
|
501
|
+
return { handled: true, response: `✅ costThreshold = ${threshold} 🌻` };
|
|
502
|
+
}
|
|
503
|
+
if (key === 'costConfirmationRequired' && value) {
|
|
504
|
+
const enabled = value === 'true';
|
|
505
|
+
saveConfig({ costConfirmationRequired: enabled });
|
|
506
|
+
return { handled: true, response: `✅ costConfirmationRequired = ${enabled}` };
|
|
507
|
+
}
|
|
450
508
|
return {
|
|
451
509
|
handled: true,
|
|
452
|
-
error: `Clé inconnue: ${key}. Clés: status_gui, logs_gui, threshold_tier,
|
|
510
|
+
error: `Clé inconnue: ${key}. Clés: status_gui, logs_gui, threshold_tier, threshold_wallet, status_bar, cost_estimator, enablePaidTools, costThreshold, costConfirmationRequired, lang`
|
|
453
511
|
};
|
|
454
512
|
}
|
|
455
513
|
function handleHelpCommand() {
|
|
456
514
|
const help = `
|
|
457
|
-
|
|
515
|
+
${t('commands.help.title')}
|
|
516
|
+
${t('commands.help.alias_note')}
|
|
517
|
+
|
|
518
|
+
${t('commands.help.mode_usage')}
|
|
519
|
+
|
|
520
|
+
${t('commands.help.configuration')}
|
|
458
521
|
|
|
459
|
-
|
|
460
|
-
- **\`/pollinations usage [full]\`**: Affiche le dashboard (full = détail).
|
|
461
|
-
- **\`/pollinations fallback [mode] [model]\`**: Configure le Safety Net pour economy/pro.
|
|
462
|
-
- **\`/pollinations config [key] [value]\`**:
|
|
463
|
-
- \`threshold_tier\`: 0-100 (% Alerte Tier).
|
|
464
|
-
- \`threshold_wallet_warn\`: 0-100 (% Alerte Wallet Session).
|
|
465
|
-
- \`threshold_wallet_stop\`: Montage absolu $ (Stop Wallet).
|
|
466
|
-
- \`status_gui\`: none, alert, all.
|
|
467
|
-
- \`logs_gui\`: none, error, verbose.
|
|
468
|
-
- \`status_bar\`: true/false (Widget).
|
|
522
|
+
${t('commands.help.models_pricing')}
|
|
469
523
|
`.trim();
|
|
470
524
|
return { handled: true, response: help };
|
|
471
525
|
}
|
|
526
|
+
// === MODELS & PRICING COMMANDS ===
|
|
527
|
+
function parseNameDesc(m) {
|
|
528
|
+
const fullDesc = m.description || m.name;
|
|
529
|
+
const parts = fullDesc.split(" - ");
|
|
530
|
+
if (parts.length > 1) {
|
|
531
|
+
return { nom: parts[0].trim(), desc: parts.slice(1).join(" - ").trim() };
|
|
532
|
+
}
|
|
533
|
+
return { nom: fullDesc, desc: "" };
|
|
534
|
+
}
|
|
535
|
+
export async function handleModelsCommand(args) {
|
|
536
|
+
const filter = args[0]; // optional: image, video, audio, text
|
|
537
|
+
if (!ModelRegistry.isReady()) {
|
|
538
|
+
return {
|
|
539
|
+
handled: true,
|
|
540
|
+
response: t('commands.models.loading')
|
|
541
|
+
};
|
|
542
|
+
}
|
|
543
|
+
const sections = [];
|
|
544
|
+
// --- FETCH FREE UNIVERSE GITHUB/LEGACY MODELS ---
|
|
545
|
+
if (!filter || filter === 'text') {
|
|
546
|
+
try {
|
|
547
|
+
const freeRes = await fetch('https://text.pollinations.ai/models', { signal: AbortSignal.timeout(4000) });
|
|
548
|
+
if (freeRes.ok) {
|
|
549
|
+
const freeData = await freeRes.json();
|
|
550
|
+
sections.push(t('commands.models.free_title'));
|
|
551
|
+
sections.push(t('commands.models.free_desc'));
|
|
552
|
+
sections.push(t('commands.models.free_headers1'));
|
|
553
|
+
sections.push(t('commands.models.free_headers2'));
|
|
554
|
+
for (const m of freeData) {
|
|
555
|
+
const desc = m.description || m.name;
|
|
556
|
+
const aliases = m.aliases ? m.aliases.join(', ') : m.name;
|
|
557
|
+
sections.push(`| \`${m.name}\` | ${aliases} | ${desc.substring(0, 40)} | ${m.vision ? '👁️' : '❌'} | ${m.tools ? '🛠️' : '❌'} |`);
|
|
558
|
+
}
|
|
559
|
+
sections.push('');
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
catch (e) {
|
|
563
|
+
sections.push(t('commands.models.free_error'));
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
sections.push(t('commands.models.enter_title'));
|
|
567
|
+
const categories = [
|
|
568
|
+
{ cat: 'image', emoji: '🎨', label: t('commands.models.cats.image') },
|
|
569
|
+
{ cat: 'video', emoji: '🎬', label: t('commands.models.cats.video') },
|
|
570
|
+
{ cat: 'audio', emoji: '🔊', label: t('commands.models.cats.audio') },
|
|
571
|
+
{ cat: 'text', emoji: '📝', label: t('commands.models.cats.text') },
|
|
572
|
+
];
|
|
573
|
+
for (const { cat, emoji, label } of categories) {
|
|
574
|
+
if (filter && filter !== cat)
|
|
575
|
+
continue;
|
|
576
|
+
const models = ModelRegistry.list(cat);
|
|
577
|
+
if (models.length === 0)
|
|
578
|
+
continue;
|
|
579
|
+
const sorted = [...models].sort((a, b) => a.name.localeCompare(b.name));
|
|
580
|
+
sections.push(t('commands.models.cat_title', { emoji, label, count: models.length }));
|
|
581
|
+
sections.push(t('commands.models.enter_headers1'));
|
|
582
|
+
sections.push(t('commands.models.enter_headers2'));
|
|
583
|
+
for (const m of sorted) {
|
|
584
|
+
const { nom, desc } = parseNameDesc(m);
|
|
585
|
+
const badges = buildBadges(m);
|
|
586
|
+
const input = buildInputIcons(m);
|
|
587
|
+
const output = buildOutputCost(m);
|
|
588
|
+
sections.push(`| ${nom} | \`${m.name}\` | ${desc.substring(0, 40)} | ${badges} | ${input} | ${output} |`);
|
|
589
|
+
}
|
|
590
|
+
sections.push('');
|
|
591
|
+
}
|
|
592
|
+
sections.push(t('commands.models.capabilities'));
|
|
593
|
+
sections.push(t('commands.models.other'));
|
|
594
|
+
return { handled: true, response: sections.join('\n') };
|
|
595
|
+
}
|
|
596
|
+
export async function handlePricingCommand() {
|
|
597
|
+
try {
|
|
598
|
+
const cp = require('child_process');
|
|
599
|
+
const path = require('path');
|
|
600
|
+
// Pointeur __dirname -> dist/server. Donc on cible scripts/pollinations_pricing.js
|
|
601
|
+
const scriptPath = path.join(__dirname, 'scripts', 'pollinations_pricing.js');
|
|
602
|
+
// Exécution locale via Node (sécurisé pour le bundle prod, pas de npx tsx)
|
|
603
|
+
const output = cp.execSync(`node "${scriptPath}"`, { encoding: 'utf-8', stdio: 'pipe' });
|
|
604
|
+
return { handled: true, response: output };
|
|
605
|
+
}
|
|
606
|
+
catch (e) {
|
|
607
|
+
return { handled: true, error: `Erreur lors de la récupération des prix: ${e.message}` };
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
// ─── Formatting Helpers for Models/Pricing ────────────────────────────────
|
|
611
|
+
function buildBadges(m) {
|
|
612
|
+
const f = [];
|
|
613
|
+
if (m.paid_only)
|
|
614
|
+
f.push('💎');
|
|
615
|
+
const allFlags = [...(m.input_modalities || []), ...(m.output_modalities || []), m.name];
|
|
616
|
+
if (m.supportsI2X)
|
|
617
|
+
allFlags.push("👁️");
|
|
618
|
+
const str = allFlags.join(" ").toLowerCase();
|
|
619
|
+
if (str.includes("image") || str.includes("👁️"))
|
|
620
|
+
f.push("👁️");
|
|
621
|
+
if (m.reasoning || str.includes("reasoning"))
|
|
622
|
+
f.push("🧠");
|
|
623
|
+
if (str.includes("audio") || str.includes("whisper") || str.includes("scribe") || str.includes("🎙️"))
|
|
624
|
+
f.push("🎙️");
|
|
625
|
+
if (str.includes("search") || str.includes("sonar") || str.includes("gemini"))
|
|
626
|
+
f.push("🔍");
|
|
627
|
+
if (m.output_modalities.includes("audio") || (m.voices && m.voices.length > 0) || str.includes("tts") || str.includes("music"))
|
|
628
|
+
f.push("🔊");
|
|
629
|
+
if (str.includes("coder") || str.includes("code") || str.includes("gemini"))
|
|
630
|
+
f.push("💻");
|
|
631
|
+
return f.filter((v, i, a) => a.indexOf(v) === i).join(" ");
|
|
632
|
+
}
|
|
633
|
+
function buildInputIcons(m) {
|
|
634
|
+
const icons = [];
|
|
635
|
+
if (m.input_modalities.includes('text'))
|
|
636
|
+
icons.push('📝');
|
|
637
|
+
if (m.input_modalities.includes('image'))
|
|
638
|
+
icons.push('🖼️');
|
|
639
|
+
if (m.input_modalities.includes('audio'))
|
|
640
|
+
icons.push('🎤');
|
|
641
|
+
return icons.join('') || '📝';
|
|
642
|
+
}
|
|
643
|
+
function buildOutputCost(m) {
|
|
644
|
+
const p = m.pricing;
|
|
645
|
+
const tokens = t('commands.pricing_units.tokens');
|
|
646
|
+
const s = t('commands.pricing_units.s');
|
|
647
|
+
const img = t('commands.pricing_units.img');
|
|
648
|
+
const tok = t('commands.pricing_units.tok');
|
|
649
|
+
if (p.completionImageTokens) {
|
|
650
|
+
return p.completionImageTokens < 0.0001
|
|
651
|
+
? tokens
|
|
652
|
+
: `${p.completionImageTokens} ${img}`;
|
|
653
|
+
}
|
|
654
|
+
if (p.completionVideoSeconds)
|
|
655
|
+
return `${p.completionVideoSeconds} ${s}`;
|
|
656
|
+
if (p.completionVideoTokens)
|
|
657
|
+
return `${tokens}/s`;
|
|
658
|
+
if (p.completionAudioTokens)
|
|
659
|
+
return `${p.completionAudioTokens} ${tok}`;
|
|
660
|
+
if (p.completionAudioSeconds)
|
|
661
|
+
return `${p.completionAudioSeconds} ${s}`;
|
|
662
|
+
if (p.promptAudioSeconds)
|
|
663
|
+
return `${p.promptAudioSeconds} ${s}`;
|
|
664
|
+
if (p.completionTextTokens)
|
|
665
|
+
return `${p.completionTextTokens} ${tok}`;
|
|
666
|
+
return tokens;
|
|
667
|
+
}
|
|
668
|
+
export async function handleInfosCommand() {
|
|
669
|
+
const config = loadConfig();
|
|
670
|
+
let name = "Developer";
|
|
671
|
+
let tier = "anonymous";
|
|
672
|
+
if (config.apiKey) {
|
|
673
|
+
try {
|
|
674
|
+
const res = await fetch('https://gen.pollinations.ai/account/profile', {
|
|
675
|
+
headers: { 'Authorization': `Bearer ${config.apiKey}` }
|
|
676
|
+
});
|
|
677
|
+
if (res.ok) {
|
|
678
|
+
const data = await res.json();
|
|
679
|
+
if (data.name)
|
|
680
|
+
name = data.name;
|
|
681
|
+
tier = data.tier || "anonymous";
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
catch (e) {
|
|
685
|
+
// Ignorer l'erreur réseau et garder les valeurs par défaut
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
const emojis = {
|
|
689
|
+
microbe: '🦠', spore: '🍄', seed: '🌱', flower: '🌸', nectar: '🍯', anonymous: '👤'
|
|
690
|
+
};
|
|
691
|
+
const tierEmoji = emojis[tier] || '❓';
|
|
692
|
+
const response = `${t('commands.infos.title', { name })}
|
|
693
|
+
${t('commands.infos.features_title')}
|
|
694
|
+
${t('commands.infos.features_free')}
|
|
695
|
+
|
|
696
|
+
${t('commands.infos.features_pro')}
|
|
697
|
+
|
|
698
|
+
${t('commands.infos.features_config')}
|
|
699
|
+
|
|
700
|
+
${t('commands.infos.tiers_title', { emoji: tierEmoji, tier: tier.toUpperCase() })}
|
|
701
|
+
${t('commands.infos.about')}
|
|
702
|
+
|
|
703
|
+
${t('commands.infos.levels_title')}
|
|
704
|
+
${t('commands.infos.levels_list')}
|
|
705
|
+
|
|
706
|
+
${t('commands.infos.beta_note')}
|
|
707
|
+
|
|
708
|
+
${t('commands.infos.pollen_title')}
|
|
709
|
+
|
|
710
|
+
${t('commands.infos.pollen_get')}
|
|
711
|
+
|
|
712
|
+
${t('commands.infos.pollen_spend')}`;
|
|
713
|
+
return { handled: true, response };
|
|
714
|
+
}
|
|
472
715
|
// === INTEGRATION OPENCODE ===
|
|
473
716
|
export function createCommandHooks() {
|
|
474
717
|
return {
|
|
475
718
|
'tui.command.execute': async (input, output) => {
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
719
|
+
if (!input.command.startsWith('/pollinations')) {
|
|
720
|
+
return;
|
|
721
|
+
}
|
|
722
|
+
try {
|
|
723
|
+
// Parse command
|
|
724
|
+
const rawArgs = input.command.replace('/pollinations', '').trim();
|
|
725
|
+
const result = await handleCommand(rawArgs);
|
|
726
|
+
if (result.handled) {
|
|
727
|
+
if (result.error) {
|
|
728
|
+
output.error = t('commands.generic.tui_error', { error: result.error });
|
|
729
|
+
}
|
|
730
|
+
else if (result.response) {
|
|
731
|
+
output.response = result.response;
|
|
732
|
+
}
|
|
733
|
+
// If no response and no error, assume handled silently (like appendPrompt)
|
|
484
734
|
}
|
|
485
735
|
}
|
|
736
|
+
catch (err) {
|
|
737
|
+
output.error = t('commands.generic.tui_critical', { error: err.message });
|
|
738
|
+
}
|
|
739
|
+
},
|
|
740
|
+
// Hook for UI Commands (Palette / Buttons)
|
|
741
|
+
'command.execute.before': async (input, output) => {
|
|
742
|
+
const cmd = input.command;
|
|
743
|
+
if (cmd === 'pollinations.addKey') {
|
|
744
|
+
handleCommand('addKey'); // Return help message
|
|
745
|
+
}
|
|
746
|
+
else if (cmd === 'pollinations.usage') {
|
|
747
|
+
const res = await handleCommand('usage');
|
|
748
|
+
if (res.response)
|
|
749
|
+
globalClient?.tui.showToast({ title: "Pollinations Usage", metadata: { type: 'info', message: t('commands.generic.tui_usage_msg') } });
|
|
750
|
+
}
|
|
751
|
+
else if (cmd === 'pollinations.mode') {
|
|
752
|
+
// UI Pollution Fix: SILENCE.
|
|
753
|
+
// User explicitly requested NO messages.
|
|
754
|
+
}
|
|
486
755
|
}
|
|
487
756
|
};
|
|
488
757
|
}
|