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.
@@ -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
- // DEBUG: Trace Config State for Hot Reload verification
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 paidListPath = path.join(config.gui ? path.dirname(path.join(process.env.HOME || '/tmp', '.config/opencode/pollinations-signature.json')) : '/tmp', 'pollinations-paid-models.json');
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 homedir = process.env.HOME || '/tmp';
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') {
@@ -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 {};
@@ -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
- function tierMetaForAllowance(allowance) {
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
- function calculateResetInfo() {
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
- const fullDesc = m.description || m.name;
113
- const parts = fullDesc.split(" - ");
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: fullDesc, desc: "" };
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
- "name": "opencode-pollinations-plugin",
3
- "displayName": "Pollinations",
4
- "version": "6.4.8",
5
- "description": "Native Pollinations.ai Provider Plugin for OpenCode",
6
- "publisher": "pollinations",
7
- "repository": {
8
- "type": "git",
9
- "url": "git+https://github.com/fkom13/opencode-pollinations-plugin.git"
10
- },
11
- "type": "module",
12
- "exports": {
13
- ".": {
14
- "types": "./dist/index.d.ts",
15
- "default": "./dist/index.js"
16
- }
17
- },
18
- "bin": {
19
- "opencode-pollinations-plugin": "./bin/setup.js"
20
- },
21
- "main": "./dist/index.js",
22
- "types": "./dist/index.d.ts",
23
- "engines": {
24
- "vscode": "^1.80.0"
25
- },
26
- "activationEvents": [
27
- "onStartupFinished"
28
- ],
29
- "scripts": {
30
- "build": "tsc",
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
+ }