opencode-pollinations-plugin 6.4.0 → 6.4.3
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 +2 -2
- package/README.es.md +2 -2
- package/README.fr.md +2 -2
- package/README.it.md +2 -2
- package/README.md +2 -2
- package/README.zh.md +2 -2
- package/dist/server/commands.js +20 -1
- package/dist/server/config.d.ts +4 -0
- package/dist/server/config.js +1 -0
- package/dist/server/generate-config.js +4 -4
- package/dist/server/models/worker.js +8 -7
- package/dist/server/proxy.js +7 -1
- package/dist/server/quota.d.ts +6 -0
- package/dist/server/quota.js +168 -73
- package/dist/server/tier-info.js +6 -6
- package/dist/tools/pollinations/polli_config.js +8 -1
- package/package.json +1 -1
package/README.de.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# 🌸 Pollinations AI Plugin für OpenCode (v6.4
|
|
1
|
+
# 🌸 Pollinations AI Plugin für OpenCode (v6.4)
|
|
2
2
|
|
|
3
3
|
## ✨ Neu in v6.4.0
|
|
4
4
|
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
|
|
36
36
|
---
|
|
37
37
|
|
|
38
|
-
## ✨ Was ist neu in V6.
|
|
38
|
+
## ✨ Was ist neu in V6.4?
|
|
39
39
|
|
|
40
40
|
- ⏱️ **Stündliche Quoten**: Verabschieden Sie sich von täglichen Limits! Developer Tiers werden jetzt **jede Stunde** pünktlich um `:00` zurückgesetzt. So haben Sie bei langen Code-Sitzungen immer frische Credits.
|
|
41
41
|
- ⚡ **100% Dynamische Engine**: Keine hartcodierten Modelllisten, festen Standardkonfigurationen und Preise mehr! In V6.2 ruft der KI-Agent von OpenCode dynamisch die neuesten LLMs, Parameter, Tags (`[💎 Paid]`, `[🌿 Free]`) und Kostenabschätzungen über die Pollinations-APIs ab.
|
package/README.es.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# 🌸 Pollinations AI Plugin para OpenCode (v6.4
|
|
1
|
+
# 🌸 Pollinations AI Plugin para OpenCode (v6.4)
|
|
2
2
|
|
|
3
3
|
## ✨ Novedades v6.4.0
|
|
4
4
|
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
|
|
36
36
|
---
|
|
37
37
|
|
|
38
|
-
## ✨ Novedades de la V6.
|
|
38
|
+
## ✨ Novedades de la V6.4
|
|
39
39
|
|
|
40
40
|
- ⏱️ **Cuotas por Hora**: ¡Dile adiós a los límites diarios! Los Tiers de Desarrollador se recargan **cada hora** exacta (a la hora en punto `:00`), asegurando que siempre tengas créditos frescos para tus largas jornadas de código.
|
|
41
41
|
- ⚡ **Motor 100% Dinámico**: ¡Se acabaron las listas de modelos quemadas en el código y los precios estáticos! En V6.2, el agente obtiene dinámicamente los últimos LLMs, parámetros, etiquetas (`[💎 Paid]`, `[🌿 Free]`) y costes aproximados conectándose continuamente a la API de Pollinations.
|
package/README.fr.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# 🌸 Plugin Pollinations AI pour OpenCode (v6.4
|
|
1
|
+
# 🌸 Plugin Pollinations AI pour OpenCode (v6.4)
|
|
2
2
|
|
|
3
3
|
## ✨ Nouveautés v6.4.0
|
|
4
4
|
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
|
|
36
36
|
---
|
|
37
37
|
|
|
38
|
-
## ✨ Nouveautés de la V6.
|
|
38
|
+
## ✨ Nouveautés de la V6.4
|
|
39
39
|
|
|
40
40
|
- ⏱️ **Quotas Horaires** : Dites adieu aux limites journalières ! Les Tiers Développeurs se réinitialisent désormais **toutes les heures** pile (à `:00`), vous assurant d'avoir toujours des crédits frais disponibles pendant vos longues sessions de code.
|
|
41
41
|
- ⚡ **Moteur 100% Dynamique** : Fini les listes de modèles codées en dur, les configurations par défaut et les prix fixes ! L'agent IA récupère dynamiquement les derniers LLMs, paramètres, tags (`[💎 Paid]`, `[🌿 Free]`) et coûts approximatifs depuis les API Pollinations.
|
package/README.it.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# 🌸 Pollinations AI Plugin per OpenCode (v6.4
|
|
1
|
+
# 🌸 Pollinations AI Plugin per OpenCode (v6.4)
|
|
2
2
|
|
|
3
3
|
## ✨ Novità v6.4.0
|
|
4
4
|
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
|
|
36
36
|
---
|
|
37
37
|
|
|
38
|
-
## ✨ Novità della V6.
|
|
38
|
+
## ✨ Novità della V6.4
|
|
39
39
|
|
|
40
40
|
- ⏱️ **Quote Orarie**: Addio ai limiti giornalieri! I Tier Sviluppatore ora si ricaricano **ogni singola ora** in punto (`:00`), garantendoti sempre crediti freschi durante le tue sessioni di programmazione.
|
|
41
41
|
- ⚡ **Motore 100% Dinamico**: Fine delle liste di modelli hard-coded, delle configurazioni fisse e dei prezzi statici! Nella V6.2, l'agente AI di OpenCode recupera dinamicamente gli ultimi LLM, parametri, tag (`[💎 Paid]`, `[🌿 Free]`) e stime dei costi dalle API di Pollinations.
|
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# 🌸 Pollinations AI Plugin for OpenCode (v6.4
|
|
1
|
+
# 🌸 Pollinations AI Plugin for OpenCode (v6.4)
|
|
2
2
|
|
|
3
3
|
<div align="center">
|
|
4
4
|
<img src="https://avatars.githubusercontent.com/u/88394740?s=400&v=4" alt="Pollinations.ai Logo" width="180">
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
|
|
28
28
|
---
|
|
29
29
|
|
|
30
|
-
## ✨ What's new in
|
|
30
|
+
## ✨ What's new in v6.4?
|
|
31
31
|
|
|
32
32
|
- 🎯 **Quests & Gamification**: New `polli_quests` tool + `/poll quests` command. See your Pollinations quests by category and the **free Pollen waiting to be claimed**. Just using this plugin (text, image & audio models) completes several quests **retroactively** — you may already have free Pollen waiting!
|
|
33
33
|
- 🆓 **Free Image & Video Tools (no key required!)**: Two brand-new bonus tools usable by **any** OpenCode model, even without a Pollinations account:
|
package/README.zh.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# 🌸 Pollinations AI 插件 - OpenCode版 (v6.4
|
|
1
|
+
# 🌸 Pollinations AI 插件 - OpenCode版 (v6.4)
|
|
2
2
|
|
|
3
3
|
## ✨ v6.4.0 新功能
|
|
4
4
|
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
|
|
36
36
|
---
|
|
37
37
|
|
|
38
|
-
## ✨ V6.
|
|
38
|
+
## ✨ V6.4 新篇章
|
|
39
39
|
|
|
40
40
|
- ⏱️ **小时级额度刷新**: 告别漫长的每日限额等待!现有的开发者等级现在将在 **每个整点** (`:00`) 清零并重新下放属于你的花粉(Pollen)。这确保您的代码生成和创意测试不被中断!
|
|
41
41
|
- ⚡ **100% 动态云端引擎**: 在 V6.2 中,代理助手如今能够自主向 Pollinations API 前台发送查询请求,自动读取并注入全网最新的 AI 模型表 (`[💎 Paid]`, `[🌿 Free]`) 、动态最高限额与实时期望消费数据,硬编码的时代宣告结束。
|
package/dist/server/commands.js
CHANGED
|
@@ -655,6 +655,8 @@ ${t('commands.config.table_divider')}
|
|
|
655
655
|
| **costConfirmationRequired**| \`${config.costConfirmationRequired ?? true}\` | ${t('commands.config.costConfirmationRequired_role')} | \`/poll config costConfirmationRequired <true/false>\` |
|
|
656
656
|
| **costThreshold**| \`${config.costThreshold ?? 0.15} 🌻\` | ${t('commands.config.costThreshold_role')} | \`/poll config costThreshold <X>\` |
|
|
657
657
|
| **cost_estimator**| \`${config.costEstimator ?? true}\` | ${t('commands.config.cost_estimator_role')} | \`/poll config cost_estimator <true/false>\` |
|
|
658
|
+
| **refillOverride**| \`${config.refillOverride ?? 'auto (déduit)'}\` | Quest Pollen refill horaire | \`/poll config refillOverride <0.01/0.15/0.4/0.8/10>\` |
|
|
659
|
+
| **questStashInFreeMode**| \`${config.questStashInFreeMode ?? true}\` | Compte le stash dans alwaysfree | \`/poll config questStashInFreeMode <true/false>\` |
|
|
658
660
|
| **fallbacks.free.main** | \`${config.fallbacks?.free?.main || 'free/mistral'}\` | ${t('commands.config.fallback_main_role')} | \`/poll fallback <main> <agent>\` |
|
|
659
661
|
| **fallbacks.free.agent** | \`${config.fallbacks?.free?.agent || 'free/openai-fast'}\`| ${t('commands.config.fallback_agent_role')} | \`/poll fallback <main> <agent>\` |
|
|
660
662
|
| **fallbacks.enter.agent** | \`${config.fallbacks?.enter?.agent || 'free/openai-fast'}\`| ${t('commands.config.fallback_enter_role')} | *${t('commands.config.managed_auto')}* |
|
|
@@ -751,9 +753,26 @@ ${t('commands.config.table_divider')}
|
|
|
751
753
|
saveConfig({ costConfirmationRequired: enabled });
|
|
752
754
|
return { handled: true, response: `✅ costConfirmationRequired = ${enabled}` };
|
|
753
755
|
}
|
|
756
|
+
if (key === 'refillOverride' && value) {
|
|
757
|
+
if (value === 'auto') {
|
|
758
|
+
saveConfig({ refillOverride: undefined });
|
|
759
|
+
return { handled: true, response: '✅ refillOverride = auto (déduction automatique)' };
|
|
760
|
+
}
|
|
761
|
+
const override = parseFloat(value);
|
|
762
|
+
if (isNaN(override) || ![0.01, 0.15, 0.4, 0.8, 10].includes(override)) {
|
|
763
|
+
return { handled: true, error: 'Valeurs supportées: auto, 0.01, 0.15, 0.4, 0.8, 10' };
|
|
764
|
+
}
|
|
765
|
+
saveConfig({ refillOverride: override });
|
|
766
|
+
return { handled: true, response: `✅ refillOverride = ${override} 🌻/h` };
|
|
767
|
+
}
|
|
768
|
+
if (key === 'questStashInFreeMode' && value) {
|
|
769
|
+
const enabled = value === 'true';
|
|
770
|
+
saveConfig({ questStashInFreeMode: enabled });
|
|
771
|
+
return { handled: true, response: `✅ questStashInFreeMode = ${enabled}${enabled ? ' (le stash Quest compte comme free)' : ' (seul le refill horaire compte)'}` };
|
|
772
|
+
}
|
|
754
773
|
return {
|
|
755
774
|
handled: true,
|
|
756
|
-
error: `Clé inconnue: ${key}. Clés: status_gui, logs_gui, threshold_tier, threshold_wallet, status_bar, cost_estimator, enablePaidTools, costThreshold, costConfirmationRequired, lang`
|
|
775
|
+
error: `Clé inconnue: ${key}. Clés: status_gui, logs_gui, threshold_tier, threshold_wallet, status_bar, cost_estimator, enablePaidTools, costThreshold, costConfirmationRequired, refillOverride, questStashInFreeMode, lang`
|
|
757
776
|
};
|
|
758
777
|
}
|
|
759
778
|
function handleHelpCommand() {
|
package/dist/server/config.d.ts
CHANGED
|
@@ -28,6 +28,8 @@ export interface PollinationsConfigV5 {
|
|
|
28
28
|
costConfirmationRequired: boolean;
|
|
29
29
|
statusBar: boolean;
|
|
30
30
|
costEstimator: boolean;
|
|
31
|
+
refillOverride?: number;
|
|
32
|
+
questStashInFreeMode?: boolean;
|
|
31
33
|
lang?: string;
|
|
32
34
|
}
|
|
33
35
|
export declare function loadConfig(): PollinationsConfigV5;
|
|
@@ -58,6 +60,8 @@ export declare function saveConfig(updates: Partial<PollinationsConfigV5>): {
|
|
|
58
60
|
costConfirmationRequired: boolean;
|
|
59
61
|
statusBar: boolean;
|
|
60
62
|
costEstimator: boolean;
|
|
63
|
+
refillOverride?: number;
|
|
64
|
+
questStashInFreeMode?: boolean;
|
|
61
65
|
lang?: string;
|
|
62
66
|
};
|
|
63
67
|
export declare function saveKeyToAuthJson(key: string): boolean;
|
package/dist/server/config.js
CHANGED
|
@@ -82,6 +82,7 @@ const DEFAULT_CONFIG_V5 = {
|
|
|
82
82
|
keyHasAccessToProfile: true, // Default true for legacy keys
|
|
83
83
|
statusBar: true,
|
|
84
84
|
costEstimator: true, // Show cost estimates by default
|
|
85
|
+
questStashInFreeMode: true, // Count quest stash as free in alwaysfree
|
|
85
86
|
lang: 'en', // Default language is English
|
|
86
87
|
};
|
|
87
88
|
import { log as logSystem } from './logger.js';
|
|
@@ -87,12 +87,12 @@ export async function generatePollinationsConfig(forceApiKey, forceStrict = fals
|
|
|
87
87
|
const enterList = ModelRegistry.list('text');
|
|
88
88
|
const paidModels = [];
|
|
89
89
|
enterList.forEach((m) => {
|
|
90
|
-
if (m.tools ===
|
|
91
|
-
return; //
|
|
90
|
+
if (m.tools !== true || m.community === true)
|
|
91
|
+
return; // Exclude non-tool and community models
|
|
92
92
|
const mapped = mapModel(m, 'enter/', '');
|
|
93
93
|
modelsOutput.push(mapped);
|
|
94
94
|
if (m.paid_only) {
|
|
95
|
-
paidModels.push(mapped.id.replace('enter/', ''));
|
|
95
|
+
paidModels.push(mapped.id.replace('enter/', ''));
|
|
96
96
|
}
|
|
97
97
|
});
|
|
98
98
|
log(`Total models (Free+Pro): ${modelsOutput.length}`);
|
|
@@ -207,7 +207,7 @@ function mapModel(raw, prefix, namePrefix) {
|
|
|
207
207
|
}
|
|
208
208
|
}
|
|
209
209
|
// 2. REASONING VARIANTS — format @ai-sdk/openai-compatible: { reasoningEffort: "level" }
|
|
210
|
-
if (raw.reasoning === true || rawId.includes('thinking') || rawId.includes('reasoning')) {
|
|
210
|
+
if (raw.reasoning === true || raw.capabilities?.includes('reasoning') || rawId.includes('thinking') || rawId.includes('reasoning')) {
|
|
211
211
|
modelObj.variants = {
|
|
212
212
|
...modelObj.variants,
|
|
213
213
|
low: { reasoningEffort: 'low' },
|
|
@@ -58,12 +58,11 @@ export class ToolRegistryWorker {
|
|
|
58
58
|
videoTable += `| Modèle | Source I/O | Audio | 1 pollen ≈ | Specs |\n`;
|
|
59
59
|
videoTable += `|--------|------------|-------|------------|-------|\n`;
|
|
60
60
|
for (const m of videoModels) {
|
|
61
|
-
// Estimation pour une vidéo moyenne de 6 secondes
|
|
62
61
|
const cost = estimateVideoCost(m.name, 6);
|
|
63
62
|
const price = cost ? `${per1pollen(cost)} vidéos` : 'inconnu';
|
|
64
|
-
// durationRange et aspectRatios arrivent du fetcher (ou fallback)
|
|
65
63
|
const specs = `${m.durationRange ? m.durationRange.join('-') + 's' : '?s'} / ${m.aspectRatios ? m.aspectRatios.length : '?'} ratios`;
|
|
66
|
-
const
|
|
64
|
+
const isCommunity = m.community === true || m.name.includes('/');
|
|
65
|
+
const badge = isCommunity ? '[👥]' : (m.paid_only ? '[💎 Paid]' : '[🌿 Free]');
|
|
67
66
|
videoTable += `| \`${m.name}\` ${badge} | ${m.supportsI2X ? 'T2V/I2V' : 'T2V'} | ${m.output_modalities?.includes('audio') || m.name === 'grok-video' ? '✅' : '❌'} | ${price} | ${specs} |\n`;
|
|
68
67
|
}
|
|
69
68
|
if (!polliGenVideoTool.description.includes('**🎬 Modèles Vidéo Détectés')) {
|
|
@@ -84,7 +83,8 @@ export class ToolRegistryWorker {
|
|
|
84
83
|
for (const m of imageModels.slice(0, 20)) {
|
|
85
84
|
const cost = estimateImageCost(m.name);
|
|
86
85
|
const price = cost ? `${per1pollen(cost)} images` : 'inconnu';
|
|
87
|
-
const
|
|
86
|
+
const isCommunity = m.community === true || m.name.includes('/');
|
|
87
|
+
const badge = isCommunity ? '[👥]' : (m.paid_only ? '[💎 Paid]' : '[🌿 Free]');
|
|
88
88
|
imageTable += `| \`${m.name}\` ${badge} | ${m.supportsI2X ? '✅' : '❌'} | Standard | ${price} |\n`;
|
|
89
89
|
}
|
|
90
90
|
if (imageModels.length > 20) {
|
|
@@ -105,9 +105,9 @@ export class ToolRegistryWorker {
|
|
|
105
105
|
audioTable += `| Modèle | Durée max | Qualité |\n`;
|
|
106
106
|
audioTable += `|--------|-----------|---------|\n`;
|
|
107
107
|
for (const m of audioModels) {
|
|
108
|
-
const
|
|
108
|
+
const isCommunity = m.community === true || m.name.includes('/');
|
|
109
|
+
const badge = isCommunity ? '[👥]' : (m.paid_only ? '[💎 Paid]' : '[🌿 Free]');
|
|
109
110
|
const duration = m.durationRange ? `${m.durationRange.join('-')}s` : 'Standard';
|
|
110
|
-
// STT Whispers or TTS or Music
|
|
111
111
|
audioTable += `| \`${m.name}\` ${badge} | ${duration} | Standard |\n`;
|
|
112
112
|
}
|
|
113
113
|
if (!polliGenAudioTool.description.includes('**🎵 Modèles Audio/Music Détectés')) {
|
|
@@ -138,7 +138,8 @@ export class ToolRegistryWorker {
|
|
|
138
138
|
searchTable += `| Modèle | Description / Specs |\n`;
|
|
139
139
|
searchTable += `|--------|---------------------|\n`;
|
|
140
140
|
for (const m of searchModels) {
|
|
141
|
-
const
|
|
141
|
+
const isCommunity = m.community === true || m.name.includes('/');
|
|
142
|
+
const badge = isCommunity ? '[👥]' : (m.paid_only ? '[💎 Paid]' : '[🌿 Free]');
|
|
142
143
|
// Clean markdown piping conflicts
|
|
143
144
|
let cleanDesc = m.description.replace(/\|/g, '-');
|
|
144
145
|
// Adding "Specialized" hint
|
package/dist/server/proxy.js
CHANGED
|
@@ -446,7 +446,13 @@ export async function handleChatCompletion(req, res, bodyRaw) {
|
|
|
446
446
|
fallbackReason = t('proxy.warnings.quota_unreachable_msg');
|
|
447
447
|
}
|
|
448
448
|
else {
|
|
449
|
-
const
|
|
449
|
+
const effectiveFree = config.questStashInFreeMode !== false
|
|
450
|
+
? quota.tierRemaining + (quota.questStash || 0)
|
|
451
|
+
: quota.tierRemaining;
|
|
452
|
+
const effectiveLimit = config.questStashInFreeMode !== false
|
|
453
|
+
? quota.tierLimit + (quota.questStash || 0)
|
|
454
|
+
: quota.tierLimit;
|
|
455
|
+
const tierRatio = effectiveLimit > 0 ? (effectiveFree / effectiveLimit) : 0;
|
|
450
456
|
if (tierRatio <= (config.thresholds.tier / 100)) {
|
|
451
457
|
log(`[SafetyNet] AlwaysFree Mode: Tier (${(tierRatio * 100).toFixed(1)}%) <= Threshold (${config.thresholds.tier}%). Switching.`);
|
|
452
458
|
emitStatusToast('warning', t('proxy.warnings.tier_limit_title', { threshold: config.thresholds.tier }), 'AlwaysFree Mode');
|
package/dist/server/quota.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export interface QuotaStatus {
|
|
|
3
3
|
tierRemaining: number;
|
|
4
4
|
tierUsed: number;
|
|
5
5
|
tierLimit: number;
|
|
6
|
+
questStash: number;
|
|
6
7
|
walletBalance: number;
|
|
7
8
|
nextResetAt: Date;
|
|
8
9
|
timeUntilReset: number;
|
|
@@ -16,3 +17,8 @@ export interface QuotaStatus {
|
|
|
16
17
|
export declare function fetchUsageForPeriod(apiKey: string, lastReset: Date): Promise<DetailedUsageEntry[]>;
|
|
17
18
|
export declare function getQuotaStatus(forceRefresh?: boolean): Promise<QuotaStatus>;
|
|
18
19
|
export declare function formatQuotaForToast(quota: QuotaStatus): string;
|
|
20
|
+
export declare function fetchQuestStash(apiKey: string): Promise<{
|
|
21
|
+
questStash: number;
|
|
22
|
+
claimedQuestTier: number;
|
|
23
|
+
tierConsumedSinceClaim: number;
|
|
24
|
+
}>;
|
package/dist/server/quota.js
CHANGED
|
@@ -1,65 +1,147 @@
|
|
|
1
|
-
import * as https from 'https';
|
|
1
|
+
import * as https from 'https';
|
|
2
2
|
import { loadConfig } from './config.js';
|
|
3
3
|
// === CACHE & CONSTANTS ===
|
|
4
|
-
const CACHE_TTL = 30000;
|
|
4
|
+
const CACHE_TTL = 30000;
|
|
5
5
|
let cachedQuota = null;
|
|
6
6
|
let lastQuotaFetch = 0;
|
|
7
7
|
const ONE_HOUR_MS = 60 * 60 * 1000;
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
spore: { pollen: 0.01, emoji: '🍄', cadence: 'hourly' },
|
|
17
|
-
seed: { pollen: 0.15, emoji: '🌱', cadence: 'hourly' },
|
|
18
|
-
flower: { pollen: 0.4, emoji: '🌸', cadence: 'hourly' },
|
|
19
|
-
nectar: { pollen: 0.8, emoji: '🍯', cadence: 'hourly' },
|
|
20
|
-
router: { pollen: 10, emoji: '🐝', cadence: 'hourly' },
|
|
21
|
-
};
|
|
8
|
+
const KNOWN_REFILLS = [
|
|
9
|
+
{ pollen: 0, emoji: '👤', label: 'anonymous' },
|
|
10
|
+
{ pollen: 0.01, emoji: '🍄', label: 'spore' },
|
|
11
|
+
{ pollen: 0.15, emoji: '🌱', label: 'seed' },
|
|
12
|
+
{ pollen: 0.4, emoji: '🌸', label: 'flower' },
|
|
13
|
+
{ pollen: 0.8, emoji: '🍯', label: 'nectar' },
|
|
14
|
+
{ pollen: 10, emoji: '🐝', label: 'router' },
|
|
15
|
+
];
|
|
22
16
|
// === LOGGING ===
|
|
23
17
|
import { logApi } from './logger.js';
|
|
24
18
|
function logQuota(msg) {
|
|
25
19
|
logApi(`[QUOTA] ${msg}`);
|
|
26
20
|
}
|
|
27
|
-
// === SMART FETCH API ===
|
|
21
|
+
// === SMART FETCH API (cursor-based) ===
|
|
28
22
|
export async function fetchUsageForPeriod(apiKey, lastReset) {
|
|
29
23
|
let allUsage = [];
|
|
30
|
-
|
|
31
|
-
|
|
24
|
+
const limit = 100;
|
|
25
|
+
let cursorEventId = null;
|
|
32
26
|
while (true) {
|
|
27
|
+
let queryPath = `/account/usage?limit=${limit}`;
|
|
28
|
+
if (cursorEventId) {
|
|
29
|
+
queryPath += `&before_event_id=${encodeURIComponent(cursorEventId)}`;
|
|
30
|
+
}
|
|
33
31
|
let usageRes;
|
|
34
32
|
try {
|
|
35
|
-
usageRes = await fetchAPI(
|
|
33
|
+
usageRes = await fetchAPI(queryPath, apiKey);
|
|
36
34
|
}
|
|
37
35
|
catch (e) {
|
|
38
|
-
logQuota(`SmartFetch failed
|
|
36
|
+
logQuota(`SmartFetch failed: ${e}`);
|
|
39
37
|
break;
|
|
40
38
|
}
|
|
41
39
|
if (!usageRes.usage || usageRes.usage.length === 0) {
|
|
42
|
-
break;
|
|
40
|
+
break;
|
|
43
41
|
}
|
|
44
42
|
let reachedCutoff = false;
|
|
45
43
|
for (const entry of usageRes.usage) {
|
|
46
|
-
const timestampStr = entry.timestamp.includes('Z')
|
|
44
|
+
const timestampStr = entry.timestamp.includes('Z')
|
|
45
|
+
? entry.timestamp
|
|
46
|
+
: entry.timestamp.replace(' ', 'T') + 'Z';
|
|
47
47
|
const entryTime = new Date(timestampStr);
|
|
48
48
|
if (entryTime < lastReset) {
|
|
49
49
|
reachedCutoff = true;
|
|
50
|
-
break;
|
|
50
|
+
break;
|
|
51
51
|
}
|
|
52
52
|
allUsage.push(entry);
|
|
53
53
|
}
|
|
54
|
-
// If we found an entry older than lastReset, or if the page was not full, we reached the end.
|
|
55
54
|
if (reachedCutoff || usageRes.usage.length < limit) {
|
|
56
55
|
break;
|
|
57
56
|
}
|
|
58
|
-
|
|
57
|
+
const lastEntry = usageRes.usage[usageRes.usage.length - 1];
|
|
58
|
+
cursorEventId = lastEntry.cursor_event_id || null;
|
|
59
|
+
if (!cursorEventId)
|
|
60
|
+
break;
|
|
59
61
|
}
|
|
60
62
|
logQuota(`SmartFetch: Retrieved ${allUsage.length} transactions for current period.`);
|
|
61
63
|
return allUsage;
|
|
62
64
|
}
|
|
65
|
+
// === ALLOWANCE DEDUCTION ===
|
|
66
|
+
async function fetchUsageForAllowance(apiKey) {
|
|
67
|
+
const sevenDaysAgo = new Date(Date.now() - 7 * 24 * ONE_HOUR_MS);
|
|
68
|
+
const allUsage = [];
|
|
69
|
+
let cursorEventId = null;
|
|
70
|
+
let pageCount = 0;
|
|
71
|
+
const maxPages = 20;
|
|
72
|
+
while (pageCount < maxPages) {
|
|
73
|
+
let path = `/account/usage?days=7&limit=500`;
|
|
74
|
+
if (cursorEventId) {
|
|
75
|
+
path += `&before_event_id=${encodeURIComponent(cursorEventId)}`;
|
|
76
|
+
}
|
|
77
|
+
const res = await fetchAPI(path, apiKey);
|
|
78
|
+
if (!res.usage || res.usage.length === 0)
|
|
79
|
+
break;
|
|
80
|
+
let reachedCutoff = false;
|
|
81
|
+
for (const entry of res.usage) {
|
|
82
|
+
const ts = (entry.timestamp.includes('Z') ? entry.timestamp : entry.timestamp.replace(' ', 'T') + 'Z');
|
|
83
|
+
if (new Date(ts) < sevenDaysAgo) {
|
|
84
|
+
reachedCutoff = true;
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
allUsage.push(entry);
|
|
88
|
+
}
|
|
89
|
+
if (reachedCutoff || res.usage.length < 500)
|
|
90
|
+
break;
|
|
91
|
+
const last = res.usage[res.usage.length - 1];
|
|
92
|
+
cursorEventId = last?.cursor_event_id || null;
|
|
93
|
+
if (!cursorEventId)
|
|
94
|
+
break;
|
|
95
|
+
pageCount++;
|
|
96
|
+
}
|
|
97
|
+
logQuota(`deduceAllowance: fetched ${allUsage.length} records over ${pageCount + 1} pages`);
|
|
98
|
+
return allUsage;
|
|
99
|
+
}
|
|
100
|
+
async function deduceAllowanceFromApi(apiKey) {
|
|
101
|
+
try {
|
|
102
|
+
const allUsage = await fetchUsageForAllowance(apiKey);
|
|
103
|
+
if (allUsage.length === 0)
|
|
104
|
+
return 0;
|
|
105
|
+
const sevenDaysAgo = new Date(Date.now() - 7 * 24 * ONE_HOUR_MS);
|
|
106
|
+
const hourlyBuckets = new Map();
|
|
107
|
+
for (const entry of allUsage) {
|
|
108
|
+
if (entry.meter_source !== 'tier')
|
|
109
|
+
continue;
|
|
110
|
+
const ts = entry.timestamp.includes('Z') ? entry.timestamp : entry.timestamp.replace(' ', 'T') + 'Z';
|
|
111
|
+
const entryTime = new Date(ts);
|
|
112
|
+
if (entryTime < sevenDaysAgo)
|
|
113
|
+
continue;
|
|
114
|
+
const hourKey = entryTime.getUTCFullYear() * 1000000
|
|
115
|
+
+ entryTime.getUTCMonth() * 10000
|
|
116
|
+
+ entryTime.getUTCDate() * 100
|
|
117
|
+
+ entryTime.getUTCHours();
|
|
118
|
+
hourlyBuckets.set(hourKey, (hourlyBuckets.get(hourKey) || 0) + entry.cost_usd);
|
|
119
|
+
}
|
|
120
|
+
const maxHourlyTier = Math.max(0, ...hourlyBuckets.values());
|
|
121
|
+
const match = KNOWN_REFILLS.slice().reverse().find(r => r.pollen <= maxHourlyTier + 0.02);
|
|
122
|
+
logQuota(`deduceAllowance: ${allUsage.length} records, ${hourlyBuckets.size} hourly buckets, max=${maxHourlyTier.toFixed(4)}, deduced=${match?.pollen ?? 0} (${match?.label})`);
|
|
123
|
+
return match ? match.pollen : 0;
|
|
124
|
+
}
|
|
125
|
+
catch (e) {
|
|
126
|
+
logQuota(`deduceAllowance failed: ${e}`);
|
|
127
|
+
return 0;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
function deduceAllowanceFromUsage(usage) {
|
|
131
|
+
const tierCosts = usage
|
|
132
|
+
.filter(u => u.meter_source === 'tier')
|
|
133
|
+
.map(u => u.cost_usd);
|
|
134
|
+
const maxHourlyTier = Math.max(0, ...tierCosts);
|
|
135
|
+
const match = KNOWN_REFILLS.slice().reverse().find(r => r.pollen <= maxHourlyTier + 0.02);
|
|
136
|
+
return match ? match.pollen : 0;
|
|
137
|
+
}
|
|
138
|
+
function tierMetaForAllowance(allowance) {
|
|
139
|
+
const match = KNOWN_REFILLS.find(r => r.pollen === allowance)
|
|
140
|
+
|| KNOWN_REFILLS.findLast(r => r.pollen <= allowance);
|
|
141
|
+
return match
|
|
142
|
+
? { label: match.label, emoji: match.emoji }
|
|
143
|
+
: { label: 'unknown', emoji: '❓' };
|
|
144
|
+
}
|
|
63
145
|
// === MAIN QUOTA FUNCTION ===
|
|
64
146
|
export async function getQuotaStatus(forceRefresh = false) {
|
|
65
147
|
const config = loadConfig();
|
|
@@ -72,45 +154,42 @@ export async function getQuotaStatus(forceRefresh = false) {
|
|
|
72
154
|
}
|
|
73
155
|
try {
|
|
74
156
|
logQuota("Fetching Quota Data...");
|
|
75
|
-
// 1. Fetch API
|
|
76
|
-
// SEQUENTIAL FETCH (Avoid Rate Limits)
|
|
77
|
-
const profileRes = await fetchAPI('/account/profile', config.apiKey);
|
|
78
157
|
const balanceRes = await fetchAPI('/account/balance', config.apiKey);
|
|
79
|
-
const
|
|
80
|
-
const balance = balanceRes.balance;
|
|
81
|
-
// 2. Convertir Timezone : Obtenir instant exact du Reset
|
|
82
|
-
const resetInfo = calculateResetInfo(profile.nextResetAt);
|
|
83
|
-
logQuota(`Fetch Success. Tier: ${profile.tier}, Balance: ${balance}, Next Reset: ${profile.nextResetAt}`);
|
|
84
|
-
// 3. Smart Fetch : Récupérer uniquement les dépenses du jour (depuis lastReset)
|
|
158
|
+
const resetInfo = calculateResetInfo();
|
|
85
159
|
const periodUsage = await fetchUsageForPeriod(config.apiKey, resetInfo.lastReset);
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
160
|
+
const allowance = balanceRes.allowance
|
|
161
|
+
?? config.refillOverride
|
|
162
|
+
?? await deduceAllowanceFromApi(config.apiKey);
|
|
163
|
+
const tierMeta = tierMetaForAllowance(allowance);
|
|
164
|
+
const tierLimit = allowance;
|
|
89
165
|
const { tierUsed } = calculateCurrentPeriodUsage(periodUsage, resetInfo);
|
|
90
|
-
// 4. Calculate Balances
|
|
91
166
|
const tierRemaining = Math.max(0, tierLimit - tierUsed);
|
|
92
|
-
// Fix rounding errors
|
|
93
167
|
const cleanTierRemaining = Math.max(0, parseFloat(tierRemaining.toFixed(4)));
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
const
|
|
98
|
-
|
|
168
|
+
const totalBalance = balanceRes.total
|
|
169
|
+
?? balanceRes.balance
|
|
170
|
+
?? 0;
|
|
171
|
+
const paidPollenNative = balanceRes.pack;
|
|
172
|
+
const paidPollen = paidPollenNative !== undefined
|
|
173
|
+
? paidPollenNative
|
|
174
|
+
: Math.max(0, totalBalance - cleanTierRemaining);
|
|
175
|
+
const cleanWalletBalance = Math.max(0, parseFloat(paidPollen.toFixed(4)));
|
|
99
176
|
const tierAlertPercent = tierLimit > 0 ? (cleanTierRemaining / tierLimit * 100) : 0;
|
|
100
177
|
const tierNeedsAlert = tierLimit > 0 && tierAlertPercent <= config.thresholds.tier;
|
|
101
178
|
const walletNeedsAlert = cleanWalletBalance > 0 && cleanWalletBalance < (config.thresholds.wallet || 0.5);
|
|
179
|
+
logQuota(`Fetch Success. Allowance: ${allowance}, Paid: ${cleanWalletBalance}, Total: ${totalBalance}`);
|
|
102
180
|
cachedQuota = {
|
|
103
181
|
tierRemaining: cleanTierRemaining,
|
|
104
182
|
tierUsed,
|
|
105
183
|
tierLimit,
|
|
184
|
+
questStash: 0, // will be computed by fetchQuestStash if needed
|
|
106
185
|
walletBalance: cleanWalletBalance,
|
|
107
186
|
nextResetAt: resetInfo.nextReset,
|
|
108
187
|
timeUntilReset: resetInfo.timeUntilReset,
|
|
109
188
|
canUseEnterprise: cleanTierRemaining > 0.05 || cleanWalletBalance > 0.05,
|
|
110
189
|
isUsingWallet: cleanTierRemaining <= 0.05 && cleanWalletBalance > 0.05,
|
|
111
190
|
needsAlert: tierNeedsAlert || walletNeedsAlert,
|
|
112
|
-
tier:
|
|
113
|
-
tierEmoji:
|
|
191
|
+
tier: tierMeta.label,
|
|
192
|
+
tierEmoji: tierMeta.emoji
|
|
114
193
|
};
|
|
115
194
|
lastQuotaFetch = now;
|
|
116
195
|
return cachedQuota;
|
|
@@ -126,18 +205,20 @@ export async function getQuotaStatus(forceRefresh = false) {
|
|
|
126
205
|
}
|
|
127
206
|
}
|
|
128
207
|
function createDefaultQuota(tierName, limit) {
|
|
208
|
+
const meta = tierMetaForAllowance(limit);
|
|
129
209
|
return {
|
|
130
210
|
tierRemaining: 0,
|
|
131
211
|
tierUsed: 0,
|
|
132
212
|
tierLimit: limit,
|
|
213
|
+
questStash: 0,
|
|
133
214
|
walletBalance: 0,
|
|
134
215
|
nextResetAt: new Date(),
|
|
135
216
|
timeUntilReset: 0,
|
|
136
217
|
canUseEnterprise: false,
|
|
137
218
|
isUsingWallet: false,
|
|
138
219
|
needsAlert: false,
|
|
139
|
-
tier: tierName,
|
|
140
|
-
tierEmoji:
|
|
220
|
+
tier: tierName !== 'none' ? meta.label : 'none',
|
|
221
|
+
tierEmoji: meta.emoji
|
|
141
222
|
};
|
|
142
223
|
}
|
|
143
224
|
// === HELPERS ===
|
|
@@ -150,7 +231,7 @@ function fetchAPI(endpoint, apiKey) {
|
|
|
150
231
|
method: 'GET',
|
|
151
232
|
headers: {
|
|
152
233
|
'Authorization': `Bearer ${apiKey}`,
|
|
153
|
-
'User-Agent': 'opencode-pollinations-plugin/
|
|
234
|
+
'User-Agent': 'opencode-pollinations-plugin/6.4.1'
|
|
154
235
|
}
|
|
155
236
|
};
|
|
156
237
|
const req = https.request(options, (res) => {
|
|
@@ -176,26 +257,9 @@ function fetchAPI(endpoint, apiKey) {
|
|
|
176
257
|
req.end();
|
|
177
258
|
});
|
|
178
259
|
}
|
|
179
|
-
function calculateResetInfo(
|
|
260
|
+
function calculateResetInfo() {
|
|
180
261
|
const now = new Date();
|
|
181
|
-
|
|
182
|
-
// Guard also against malformed timestamps that would yield Invalid Date (NaN).
|
|
183
|
-
let nextReset = nextResetAt ? new Date(nextResetAt) : null;
|
|
184
|
-
if (!nextReset || isNaN(nextReset.getTime())) {
|
|
185
|
-
// No refill: there is no upcoming reset. Use a stable current-hour window
|
|
186
|
-
// so usage SmartFetch still has a sane lower bound, and surface 0 countdown.
|
|
187
|
-
const lastReset = new Date(now.getTime() - ONE_HOUR_MS);
|
|
188
|
-
return {
|
|
189
|
-
nextReset: now,
|
|
190
|
-
lastReset,
|
|
191
|
-
timeUntilReset: 0,
|
|
192
|
-
timeSinceReset: ONE_HOUR_MS,
|
|
193
|
-
resetHour: now.getUTCHours(),
|
|
194
|
-
resetMinute: now.getUTCMinutes(),
|
|
195
|
-
resetSecond: now.getUTCSeconds(),
|
|
196
|
-
progressPercent: 100
|
|
197
|
-
};
|
|
198
|
-
}
|
|
262
|
+
const nextReset = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(), now.getUTCHours() + 1, 0, 0, 0));
|
|
199
263
|
const lastReset = new Date(nextReset.getTime() - ONE_HOUR_MS);
|
|
200
264
|
const timeUntilReset = Math.max(0, nextReset.getTime() - now.getTime());
|
|
201
265
|
const timeSinceReset = Math.max(0, now.getTime() - lastReset.getTime());
|
|
@@ -206,8 +270,8 @@ function calculateResetInfo(nextResetAt) {
|
|
|
206
270
|
timeUntilReset,
|
|
207
271
|
timeSinceReset,
|
|
208
272
|
resetHour: nextReset.getUTCHours(),
|
|
209
|
-
resetMinute:
|
|
210
|
-
resetSecond:
|
|
273
|
+
resetMinute: 0,
|
|
274
|
+
resetSecond: 0,
|
|
211
275
|
progressPercent
|
|
212
276
|
};
|
|
213
277
|
}
|
|
@@ -215,7 +279,6 @@ function calculateCurrentPeriodUsage(usage, resetInfo) {
|
|
|
215
279
|
let tierUsed = 0;
|
|
216
280
|
let packUsed = 0;
|
|
217
281
|
const entriesAfterReset = usage.filter(entry => {
|
|
218
|
-
// Safe Parse
|
|
219
282
|
const timestamp = entry.timestamp.replace(' ', 'T') + 'Z';
|
|
220
283
|
const entryTime = new Date(timestamp);
|
|
221
284
|
return entryTime >= resetInfo.lastReset;
|
|
@@ -241,5 +304,37 @@ export function formatQuotaForToast(quota) {
|
|
|
241
304
|
const hours = Math.floor(ms / (1000 * 60 * 60));
|
|
242
305
|
const minutes = Math.floor((ms % (1000 * 60 * 60)) / (1000 * 60));
|
|
243
306
|
const resetIn = `${hours}h${minutes}m`;
|
|
244
|
-
|
|
307
|
+
const stashStr = quota.questStash > 0
|
|
308
|
+
? ` | 🎁 ~${quota.questStash.toFixed(2)} (stash)`
|
|
309
|
+
: '';
|
|
310
|
+
return `${quota.tierEmoji} ${quota.tierRemaining.toFixed(2)}/${quota.tierLimit} (${tierPercent}%)${stashStr} | 💎 $${quota.walletBalance.toFixed(2)} | ⏰ ${resetIn}`;
|
|
311
|
+
}
|
|
312
|
+
export async function fetchQuestStash(apiKey) {
|
|
313
|
+
let claimedQuestTier = 0;
|
|
314
|
+
let firstClaimMs = Infinity;
|
|
315
|
+
let tierConsumedSinceClaim = 0;
|
|
316
|
+
try {
|
|
317
|
+
const qres = await fetchAPI('/account/quests', apiKey);
|
|
318
|
+
for (const q of (qres?.quests || [])) {
|
|
319
|
+
const r = q.reward;
|
|
320
|
+
if (r && r.claimedAt && r.balanceBucket === 'tier') {
|
|
321
|
+
claimedQuestTier += (r.pollenAmount || 0);
|
|
322
|
+
const cms = new Date(r.claimedAt).getTime();
|
|
323
|
+
if (!isNaN(cms) && cms < firstClaimMs)
|
|
324
|
+
firstClaimMs = cms;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
if (claimedQuestTier > 0 && isFinite(firstClaimMs)) {
|
|
328
|
+
const ures = await fetchAPI('/account/usage?limit=500', apiKey);
|
|
329
|
+
for (const e of (ures?.usage || [])) {
|
|
330
|
+
const ts = new Date(String(e.timestamp).replace(' ', 'T') + (String(e.timestamp).includes('Z') ? '' : 'Z')).getTime();
|
|
331
|
+
if (e.meter_source === 'tier' && !isNaN(ts) && ts >= firstClaimMs) {
|
|
332
|
+
tierConsumedSinceClaim += (e.cost_usd || 0);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
catch { /* fallback gracefully */ }
|
|
338
|
+
const questStash = Math.max(0, claimedQuestTier - tierConsumedSinceClaim);
|
|
339
|
+
return { questStash, claimedQuestTier, tierConsumedSinceClaim };
|
|
245
340
|
}
|
package/dist/server/tier-info.js
CHANGED
|
@@ -16,16 +16,16 @@ export const TIER_INFO = {
|
|
|
16
16
|
emoji: '🦠',
|
|
17
17
|
hourlyPollen: 0,
|
|
18
18
|
dailyEstimate: 0,
|
|
19
|
-
condition: '
|
|
20
|
-
conditionKey: 'tier.condition.
|
|
19
|
+
condition: 'Account under review',
|
|
20
|
+
conditionKey: 'tier.condition.under_review',
|
|
21
21
|
},
|
|
22
22
|
spore: {
|
|
23
23
|
name: 'Spore',
|
|
24
24
|
emoji: '🍄',
|
|
25
25
|
hourlyPollen: 0.01,
|
|
26
26
|
dailyEstimate: 0.24,
|
|
27
|
-
condition: '
|
|
28
|
-
conditionKey: 'tier.condition.
|
|
27
|
+
condition: 'New account (default)',
|
|
28
|
+
conditionKey: 'tier.condition.default',
|
|
29
29
|
},
|
|
30
30
|
seed: {
|
|
31
31
|
name: 'Seed',
|
|
@@ -40,8 +40,8 @@ export const TIER_INFO = {
|
|
|
40
40
|
emoji: '🌸',
|
|
41
41
|
hourlyPollen: 0.4,
|
|
42
42
|
dailyEstimate: 9.6,
|
|
43
|
-
condition: '
|
|
44
|
-
conditionKey: 'tier.condition.
|
|
43
|
+
condition: 'Complete Quests & contribute',
|
|
44
|
+
conditionKey: 'tier.condition.quests',
|
|
45
45
|
},
|
|
46
46
|
nectar: {
|
|
47
47
|
name: 'Nectar',
|
|
@@ -36,7 +36,9 @@ Use 'action=update' to change these. NEVER confuse Chat Mode with Tools Protecti
|
|
|
36
36
|
costThreshold: tool.schema.number().optional().describe('Cost threshold in USD/🌼 above which confirmation is required'),
|
|
37
37
|
thresholdsTier: tool.schema.number().optional().describe('Warning threshold PERCENTAGE (e.g. 10 for 10%) for Free Tier.'),
|
|
38
38
|
thresholdsWallet: tool.schema.number().optional().describe('Warning threshold PERCENTAGE (e.g. 50 for 50%) for Wallet balance.'),
|
|
39
|
-
lang: tool.schema.enum(['en', 'fr', 'es', 'de', 'it', 'zh']).optional().describe('Plugin language for commands and toasts (en, fr, es, de, it, zh).')
|
|
39
|
+
lang: tool.schema.enum(['en', 'fr', 'es', 'de', 'it', 'zh']).optional().describe('Plugin language for commands and toasts (en, fr, es, de, it, zh).'),
|
|
40
|
+
refillOverride: tool.schema.number().optional().describe('Manual Quest Pollen hourly refill override (0.01, 0.15, 0.4, 0.8, or 10). Set to 0 for auto-deduction.'),
|
|
41
|
+
questStashInFreeMode: tool.schema.boolean().optional().describe('Count accumulated quest stash as free pollen in alwaysfree Safety Net (default: true).')
|
|
40
42
|
},
|
|
41
43
|
async execute(args, context) {
|
|
42
44
|
if (args.action === 'view') {
|
|
@@ -63,6 +65,11 @@ Use 'action=update' to change these. NEVER confuse Chat Mode with Tools Protecti
|
|
|
63
65
|
updates.enablePaidTools = args.enablePaidTools;
|
|
64
66
|
if (args.costThreshold !== undefined)
|
|
65
67
|
updates.costThreshold = args.costThreshold;
|
|
68
|
+
if (args.refillOverride !== undefined) {
|
|
69
|
+
updates.refillOverride = args.refillOverride === 0 ? undefined : args.refillOverride;
|
|
70
|
+
}
|
|
71
|
+
if (args.questStashInFreeMode !== undefined)
|
|
72
|
+
updates.questStashInFreeMode = args.questStashInFreeMode;
|
|
66
73
|
if (args.lang !== undefined)
|
|
67
74
|
updates.lang = args.lang;
|
|
68
75
|
if (args.thresholdsTier !== undefined || args.thresholdsWallet !== undefined) {
|
package/package.json
CHANGED