opencode-pollinations-plugin 6.4.8 → 6.4.10
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 +17 -14
- package/README.es.md +17 -14
- package/README.fr.md +17 -14
- package/README.it.md +17 -14
- package/README.md +33 -34
- package/README.zh.md +17 -14
- package/bin/setup.js +139 -0
- package/dist/index.js +4 -6
- package/dist/locales/de.json +10 -4
- package/dist/locales/en.json +11 -4
- package/dist/locales/es.json +10 -4
- package/dist/locales/fr.json +11 -4
- package/dist/locales/it.json +10 -4
- package/dist/locales/zh.json +11 -4
- package/dist/server/commands.js +12 -71
- package/dist/server/config.js +1 -1
- package/dist/server/generate-config.js +2 -2
- package/dist/server/models/cache.js +3 -0
- package/dist/server/models/fetcher.d.ts +3 -2
- package/dist/server/models/fetcher.js +26 -4
- package/dist/server/models/types.d.ts +3 -1
- package/dist/server/proxy.js +8 -27
- package/dist/server/quota.d.ts +24 -0
- package/dist/server/quota.js +8 -2
- package/dist/server/scripts/pollinations_pricing.js +6 -3
- package/package.json +63 -62
package/dist/server/proxy.js
CHANGED
|
@@ -241,23 +241,7 @@ export async function handleChatCompletion(req, res, bodyRaw) {
|
|
|
241
241
|
try {
|
|
242
242
|
const body = JSON.parse(bodyRaw);
|
|
243
243
|
const config = loadConfig();
|
|
244
|
-
|
|
245
|
-
log(`[Proxy Request] Config Loaded. Mode: ${config.mode}, HasKey: ${!!config.apiKey}, KeyLength: ${config.apiKey ? config.apiKey.length : 0}`);
|
|
246
|
-
// TEMPORARY DIAGNOSTIC: Capture multimodal content format from OpenCode
|
|
247
|
-
if (body.messages && body.messages.length > 0) {
|
|
248
|
-
const lastUserMsg = [...body.messages].reverse().find((m) => m.role === 'user');
|
|
249
|
-
if (lastUserMsg) {
|
|
250
|
-
const contentType = typeof lastUserMsg.content;
|
|
251
|
-
const isArray = Array.isArray(lastUserMsg.content);
|
|
252
|
-
log(`[VISION DEBUG] Last user msg content type: ${contentType}, isArray: ${isArray}`);
|
|
253
|
-
if (isArray) {
|
|
254
|
-
log(`[VISION DEBUG] Content parts: ${JSON.stringify(lastUserMsg.content.map((c) => ({ type: c.type, hasText: !!c.text, hasImageUrl: !!c.image_url, hasImage: !!c.image })))}`);
|
|
255
|
-
}
|
|
256
|
-
else if (contentType === 'string') {
|
|
257
|
-
log(`[VISION DEBUG] String content (first 200 chars): ${lastUserMsg.content.substring(0, 200)}`);
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
}
|
|
244
|
+
log(`[Proxy Request] Mode: ${config.mode}, HasKey: ${!!config.apiKey}`);
|
|
261
245
|
// 0. COMMAND HANDLING
|
|
262
246
|
if (body.messages && body.messages.length > 0) {
|
|
263
247
|
const lastMsg = body.messages[body.messages.length - 1];
|
|
@@ -354,13 +338,7 @@ export async function handleChatCompletion(req, res, bodyRaw) {
|
|
|
354
338
|
// Check dynamic list saved by generate-config.ts
|
|
355
339
|
if (isEnterprise) {
|
|
356
340
|
try {
|
|
357
|
-
const
|
|
358
|
-
// Wait, logic above for config path is messy. Let's use standard path logic:
|
|
359
|
-
// config.ts uses ~/.pollinations/config.json usually.
|
|
360
|
-
// generate-config uses path.join(config.gui ? path.dirname(CONFIG_FILE) : '/tmp')
|
|
361
|
-
// Let's rely on standard ~/.pollinations location if possible, or try both.
|
|
362
|
-
const homedir = process.env.HOME || '/tmp';
|
|
363
|
-
const standardPaidPath = path.join(homedir, '.pollinations', 'pollinations-paid-models.json');
|
|
341
|
+
const standardPaidPath = path.join(getConfigDir(), 'pollinations-paid-models.json');
|
|
364
342
|
if (fs.existsSync(standardPaidPath)) {
|
|
365
343
|
const paidModels = JSON.parse(fs.readFileSync(standardPaidPath, 'utf-8'));
|
|
366
344
|
if (paidModels.includes(actualModel)) {
|
|
@@ -405,8 +383,7 @@ export async function handleChatCompletion(req, res, bodyRaw) {
|
|
|
405
383
|
if (isEnterprise) {
|
|
406
384
|
// Paid Only Check: BLOCK (not fallback) in AlwaysFree mode
|
|
407
385
|
try {
|
|
408
|
-
const
|
|
409
|
-
const standardPaidPath = path.join(homedir, '.pollinations', 'pollinations-paid-models.json');
|
|
386
|
+
const standardPaidPath = path.join(getConfigDir(), 'pollinations-paid-models.json');
|
|
410
387
|
if (fs.existsSync(standardPaidPath)) {
|
|
411
388
|
const paidModels = JSON.parse(fs.readFileSync(standardPaidPath, 'utf-8'));
|
|
412
389
|
if (paidModels.includes(actualModel)) {
|
|
@@ -577,6 +554,10 @@ export async function handleChatCompletion(req, res, bodyRaw) {
|
|
|
577
554
|
if (actualModel.includes("gpt") || actualModel.includes("openai") || actualModel.includes("azure") || actualModel.includes("midijourney") || actualModel.includes("grok")) {
|
|
578
555
|
const limit = (actualModel.includes("midijourney") || actualModel.includes("grok")) ? 128 : 120;
|
|
579
556
|
proxyBody.tools = truncateTools(proxyBody.tools, limit);
|
|
557
|
+
if (proxyBody.reasoning_effort)
|
|
558
|
+
delete proxyBody.reasoning_effort;
|
|
559
|
+
if (proxyBody.reasoningEffort)
|
|
560
|
+
delete proxyBody.reasoningEffort;
|
|
580
561
|
if (proxyBody.messages) {
|
|
581
562
|
proxyBody.messages.forEach((m) => {
|
|
582
563
|
if (m.tool_calls) {
|
|
@@ -718,7 +699,7 @@ export async function handleChatCompletion(req, res, bodyRaw) {
|
|
|
718
699
|
// TRANSPARENT FALLBACK LOGIC
|
|
719
700
|
// 1. Enterprise Safety Net (Quota/Auth/RateLimit)
|
|
720
701
|
// 2. Gemini Tools Fix (Gemini + Tools -> 401 -> Fallback to OpenAI)
|
|
721
|
-
const isEnterpriseFallback = (fetchRes.status === 402 || fetchRes.status === 429 || fetchRes.status === 401 || fetchRes.status === 403) && isEnterprise;
|
|
702
|
+
const isEnterpriseFallback = (fetchRes.status === 402 || fetchRes.status === 429 || fetchRes.status === 502 || fetchRes.status === 401 || fetchRes.status === 403) && isEnterprise;
|
|
722
703
|
const isGeminiToolsFallback = fetchRes.status === 401 && actualModel.includes('gemini') && !isEnterprise && proxyBody.tools && proxyBody.tools.length > 0;
|
|
723
704
|
// STRICT MANUAL MODE: Disable "Magic" Fallbacks
|
|
724
705
|
if ((isEnterpriseFallback || isGeminiToolsFallback) && config.mode !== 'manual') {
|
package/dist/server/quota.d.ts
CHANGED
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
import { DetailedUsageEntry } from './pollinations-api.js';
|
|
2
|
+
interface ResetInfo {
|
|
3
|
+
nextReset: Date;
|
|
4
|
+
lastReset: Date;
|
|
5
|
+
timeUntilReset: number;
|
|
6
|
+
timeSinceReset: number;
|
|
7
|
+
resetHour: number;
|
|
8
|
+
resetMinute: number;
|
|
9
|
+
resetSecond: number;
|
|
10
|
+
progressPercent: number;
|
|
11
|
+
}
|
|
2
12
|
export interface QuotaStatus {
|
|
3
13
|
tierRemaining: number;
|
|
4
14
|
tierUsed: number;
|
|
@@ -15,10 +25,24 @@ export interface QuotaStatus {
|
|
|
15
25
|
errorType?: 'auth_limited' | 'network' | 'unknown';
|
|
16
26
|
}
|
|
17
27
|
export declare function fetchUsageForPeriod(apiKey: string, lastReset: Date): Promise<DetailedUsageEntry[]>;
|
|
28
|
+
/** Map hourly refill amount → display meta (exported for unit tests). */
|
|
29
|
+
export declare function tierMetaForAllowance(allowance: number): {
|
|
30
|
+
label: string;
|
|
31
|
+
emoji: string;
|
|
32
|
+
};
|
|
33
|
+
/** Known hourly refill ladder (read-only, for tests / UI). */
|
|
34
|
+
export declare function getKnownRefills(): ReadonlyArray<{
|
|
35
|
+
pollen: number;
|
|
36
|
+
emoji: string;
|
|
37
|
+
label: string;
|
|
38
|
+
}>;
|
|
18
39
|
export declare function getQuotaStatus(forceRefresh?: boolean): Promise<QuotaStatus>;
|
|
40
|
+
/** Next top-of-hour UTC reset window (exported for unit tests). */
|
|
41
|
+
export declare function calculateResetInfo(): ResetInfo;
|
|
19
42
|
export declare function formatQuotaForToast(quota: QuotaStatus): string;
|
|
20
43
|
export declare function fetchQuestStash(apiKey: string): Promise<{
|
|
21
44
|
questStash: number;
|
|
22
45
|
claimedQuestTier: number;
|
|
23
46
|
tierConsumedSinceClaim: number;
|
|
24
47
|
}>;
|
|
48
|
+
export {};
|
package/dist/server/quota.js
CHANGED
|
@@ -138,13 +138,18 @@ function deduceAllowanceFromUsage(usage) {
|
|
|
138
138
|
const match = KNOWN_REFILLS.slice().reverse().find(r => r.pollen <= maxHourlyTier + 0.02);
|
|
139
139
|
return match ? match.pollen : 0;
|
|
140
140
|
}
|
|
141
|
-
|
|
141
|
+
/** Map hourly refill amount → display meta (exported for unit tests). */
|
|
142
|
+
export function tierMetaForAllowance(allowance) {
|
|
142
143
|
const match = KNOWN_REFILLS.find(r => r.pollen === allowance)
|
|
143
144
|
|| KNOWN_REFILLS.findLast(r => r.pollen <= allowance);
|
|
144
145
|
return match
|
|
145
146
|
? { label: match.label, emoji: match.emoji }
|
|
146
147
|
: { label: 'unknown', emoji: '❓' };
|
|
147
148
|
}
|
|
149
|
+
/** Known hourly refill ladder (read-only, for tests / UI). */
|
|
150
|
+
export function getKnownRefills() {
|
|
151
|
+
return KNOWN_REFILLS;
|
|
152
|
+
}
|
|
148
153
|
// === MAIN QUOTA FUNCTION ===
|
|
149
154
|
export async function getQuotaStatus(forceRefresh = false) {
|
|
150
155
|
const config = loadConfig();
|
|
@@ -270,7 +275,8 @@ function fetchAPI(endpoint, apiKey) {
|
|
|
270
275
|
req.end();
|
|
271
276
|
});
|
|
272
277
|
}
|
|
273
|
-
|
|
278
|
+
/** Next top-of-hour UTC reset window (exported for unit tests). */
|
|
279
|
+
export function calculateResetInfo() {
|
|
274
280
|
const now = new Date();
|
|
275
281
|
const nextReset = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(), now.getUTCHours() + 1, 0, 0, 0));
|
|
276
282
|
const lastReset = new Date(nextReset.getTime() - ONE_HOUR_MS);
|
|
@@ -109,12 +109,15 @@ function getCost(m, type) {
|
|
|
109
109
|
}
|
|
110
110
|
// ─── RENDU VISUEL ────────────────────────────────────────────────────────────
|
|
111
111
|
function parseNameDesc(m) {
|
|
112
|
-
|
|
113
|
-
|
|
112
|
+
if (m.title && m.description) {
|
|
113
|
+
return { nom: m.title, desc: m.description };
|
|
114
|
+
}
|
|
115
|
+
const displayName = m.title || m.description || m.name;
|
|
116
|
+
const parts = displayName.split(" - ");
|
|
114
117
|
if (parts.length > 1) {
|
|
115
118
|
return { nom: parts[0].trim(), desc: parts.slice(1).join(" - ").trim() };
|
|
116
119
|
}
|
|
117
|
-
return { nom:
|
|
120
|
+
return { nom: displayName, desc: "" };
|
|
118
121
|
}
|
|
119
122
|
function flags(m, overrides = []) {
|
|
120
123
|
const f = [];
|
package/package.json
CHANGED
|
@@ -1,64 +1,65 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
"
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
"test": "node scripts/tests/test-suite.cjs",
|
|
32
|
-
"prepare": "npm run build",
|
|
33
|
-
"package": "npx vsce package"
|
|
34
|
-
},
|
|
35
|
-
"contributes": {
|
|
36
|
-
"commands": [
|
|
37
|
-
{
|
|
38
|
-
"command": "pollinations.mode",
|
|
39
|
-
"title": "Pollinations: Change Mode"
|
|
40
|
-
},
|
|
41
|
-
{
|
|
42
|
-
"command": "pollinations.usage",
|
|
43
|
-
"title": "Pollinations: Show Usage"
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
"command": "pollinations.addKey",
|
|
47
|
-
"title": "Pollinations: Add BackgroundCut Key"
|
|
48
|
-
}
|
|
49
|
-
]
|
|
50
|
-
},
|
|
51
|
-
"files": [
|
|
52
|
-
"dist"
|
|
53
|
-
],
|
|
54
|
-
"dependencies": {
|
|
55
|
-
"@opencode-ai/plugin": "^1.0.85",
|
|
56
|
-
"qrcode": "^1.5.4",
|
|
57
|
-
"zod": "^3.25.76"
|
|
58
|
-
},
|
|
59
|
-
"devDependencies": {
|
|
60
|
-
"@types/node": "^20.0.0",
|
|
61
|
-
"@types/qrcode": "^1.5.6",
|
|
62
|
-
"typescript": "^5.0.0"
|
|
2
|
+
"name": "opencode-pollinations-plugin",
|
|
3
|
+
"displayName": "Pollinations",
|
|
4
|
+
"version": "6.4.10",
|
|
5
|
+
"description": "Native Pollinations.ai Provider Plugin for OpenCode — free & premium AI models, media tools, quests, cost guards",
|
|
6
|
+
"author": "fkom13",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"keywords": [
|
|
9
|
+
"opencode",
|
|
10
|
+
"pollinations",
|
|
11
|
+
"ai",
|
|
12
|
+
"plugin",
|
|
13
|
+
"image-generation",
|
|
14
|
+
"video-generation",
|
|
15
|
+
"llm",
|
|
16
|
+
"pollen"
|
|
17
|
+
],
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git+https://github.com/fkom13/opencode-pollinations-plugin.git"
|
|
21
|
+
},
|
|
22
|
+
"bugs": {
|
|
23
|
+
"url": "https://github.com/fkom13/opencode-pollinations-plugin/issues"
|
|
24
|
+
},
|
|
25
|
+
"homepage": "https://github.com/fkom13/opencode-pollinations-plugin#readme",
|
|
26
|
+
"type": "module",
|
|
27
|
+
"exports": {
|
|
28
|
+
".": {
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"default": "./dist/index.js"
|
|
63
31
|
}
|
|
64
|
-
}
|
|
32
|
+
},
|
|
33
|
+
"bin": {
|
|
34
|
+
"opencode-pollinations-plugin": "./bin/setup.js"
|
|
35
|
+
},
|
|
36
|
+
"main": "./dist/index.js",
|
|
37
|
+
"types": "./dist/index.d.ts",
|
|
38
|
+
"engines": {
|
|
39
|
+
"node": ">=18"
|
|
40
|
+
},
|
|
41
|
+
"scripts": {
|
|
42
|
+
"build": "tsc",
|
|
43
|
+
"test": "npm run build && node scripts/tests/test-suite.cjs",
|
|
44
|
+
"test:unit": "node scripts/tests/test-suite.cjs",
|
|
45
|
+
"test:i18n": "node scripts/tests/test-i18n.cjs",
|
|
46
|
+
"prepare": "npm run build",
|
|
47
|
+
"prepublishOnly": "npm run build && npm run test:unit && npm run test:i18n"
|
|
48
|
+
},
|
|
49
|
+
"files": [
|
|
50
|
+
"dist",
|
|
51
|
+
"bin",
|
|
52
|
+
"README.md",
|
|
53
|
+
"LICENSE.md"
|
|
54
|
+
],
|
|
55
|
+
"dependencies": {
|
|
56
|
+
"@opencode-ai/plugin": "^1.0.85",
|
|
57
|
+
"qrcode": "^1.5.4",
|
|
58
|
+
"zod": "^3.25.76"
|
|
59
|
+
},
|
|
60
|
+
"devDependencies": {
|
|
61
|
+
"@types/node": "^20.0.0",
|
|
62
|
+
"@types/qrcode": "^1.5.6",
|
|
63
|
+
"typescript": "^5.0.0"
|
|
64
|
+
}
|
|
65
|
+
}
|