opencode-pollinations-plugin 5.4.8-debug.1 → 5.5.0-debug.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/dist/server/commands.js +9 -12
- package/dist/server/config.d.ts +6 -6
- package/dist/server/config.js +23 -9
- package/dist/server/proxy.js +23 -73
- package/dist/server/quota.js +9 -3
- package/package.json +1 -1
package/dist/server/commands.js
CHANGED
|
@@ -174,32 +174,29 @@ async function handleUsageCommand(args) {
|
|
|
174
174
|
}
|
|
175
175
|
}
|
|
176
176
|
function handleFallbackCommand(args) {
|
|
177
|
-
const [
|
|
178
|
-
if (!
|
|
177
|
+
const [primary, subagent] = args;
|
|
178
|
+
if (!primary) {
|
|
179
179
|
const config = loadConfig();
|
|
180
|
-
const freeConfig = `Free:
|
|
181
|
-
const enterConfig = `Enter:
|
|
180
|
+
const freeConfig = `Free Fallbacks: primary=${config.fallbacks.free.primary}, subagent=${config.fallbacks.free.subagent}`;
|
|
181
|
+
const enterConfig = `Enter Targets: subagent=${config.fallbacks.enter.subagent}`;
|
|
182
182
|
return {
|
|
183
183
|
handled: true,
|
|
184
|
-
response: `
|
|
184
|
+
response: `Configuration des Modèles:\n${freeConfig}\n${enterConfig}`
|
|
185
185
|
};
|
|
186
186
|
}
|
|
187
|
-
// Default behavior for "/poll fallback <model> <agent>" is setting FREE fallbacks
|
|
188
|
-
// User needs to use commands (maybe add /poll fallback enter ...) later
|
|
189
|
-
// For now, map to Free Fallback as it's the primary Safety Net
|
|
190
187
|
const config = loadConfig();
|
|
191
188
|
saveConfig({
|
|
192
189
|
fallbacks: {
|
|
193
190
|
...config.fallbacks,
|
|
194
191
|
free: {
|
|
195
|
-
|
|
196
|
-
|
|
192
|
+
primary: primary,
|
|
193
|
+
subagent: subagent || config.fallbacks.free.subagent
|
|
197
194
|
}
|
|
198
195
|
}
|
|
199
196
|
});
|
|
200
197
|
return {
|
|
201
198
|
handled: true,
|
|
202
|
-
response: `✅
|
|
199
|
+
response: `✅ Fallbacks configurés: primary=${primary}, subagent=${subagent || config.fallbacks.free.subagent}`
|
|
203
200
|
};
|
|
204
201
|
}
|
|
205
202
|
function handleConfigCommand(args) {
|
|
@@ -273,7 +270,7 @@ function handleHelpCommand() {
|
|
|
273
270
|
|
|
274
271
|
- **\`/pollinations mode [mode]\`**: Change le mode (manual, alwaysfree, pro).
|
|
275
272
|
- **\`/pollinations usage [full]\`**: Affiche le dashboard (full = détail).
|
|
276
|
-
- **\`/pollinations fallback <
|
|
273
|
+
- **\`/pollinations fallback <primary> [subagent]\`**: Configure le Safety Net (Free).
|
|
277
274
|
- **\`/pollinations config [key] [value]\`**:
|
|
278
275
|
- \`status_gui\`: none, alert, all (Status Dashboard).
|
|
279
276
|
- \`logs_gui\`: none, error, verbose (Logs Techniques).
|
package/dist/server/config.d.ts
CHANGED
|
@@ -12,11 +12,11 @@ export interface PollinationsConfigV5 {
|
|
|
12
12
|
};
|
|
13
13
|
fallbacks: {
|
|
14
14
|
free: {
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
primary: string;
|
|
16
|
+
subagent: string;
|
|
17
17
|
};
|
|
18
18
|
enter: {
|
|
19
|
-
|
|
19
|
+
subagent: string;
|
|
20
20
|
};
|
|
21
21
|
};
|
|
22
22
|
enablePaidTools: boolean;
|
|
@@ -37,11 +37,11 @@ export declare function saveConfig(updates: Partial<PollinationsConfigV5>): {
|
|
|
37
37
|
};
|
|
38
38
|
fallbacks: {
|
|
39
39
|
free: {
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
primary: string;
|
|
41
|
+
subagent: string;
|
|
42
42
|
};
|
|
43
43
|
enter: {
|
|
44
|
-
|
|
44
|
+
subagent: string;
|
|
45
45
|
};
|
|
46
46
|
};
|
|
47
47
|
enablePaidTools: boolean;
|
package/dist/server/config.js
CHANGED
|
@@ -9,13 +9,11 @@ const CONFIG_DIR_OPENCODE = path.join(HOMEDIR, '.config', 'opencode');
|
|
|
9
9
|
const OPENCODE_CONFIG_FILE = path.join(CONFIG_DIR_OPENCODE, 'opencode.json');
|
|
10
10
|
const AUTH_FILE = path.join(HOMEDIR, '.local', 'share', 'opencode', 'auth.json');
|
|
11
11
|
// LOAD PACKAGE VERSION
|
|
12
|
-
let PKG_VERSION = '5.
|
|
12
|
+
let PKG_VERSION = '5.5.0';
|
|
13
13
|
try {
|
|
14
|
-
const pkgPath =
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
PKG_VERSION = pkg.version;
|
|
18
|
-
}
|
|
14
|
+
const pkgPath = new URL('../../package.json', import.meta.url);
|
|
15
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
16
|
+
PKG_VERSION = pkg.version;
|
|
19
17
|
}
|
|
20
18
|
catch (e) { }
|
|
21
19
|
const DEFAULT_CONFIG_V5 = {
|
|
@@ -24,8 +22,8 @@ const DEFAULT_CONFIG_V5 = {
|
|
|
24
22
|
gui: { status: 'alert', logs: 'none' },
|
|
25
23
|
thresholds: { tier: 10, wallet: 5 },
|
|
26
24
|
fallbacks: {
|
|
27
|
-
free: {
|
|
28
|
-
enter: {
|
|
25
|
+
free: { primary: 'free/mistral', subagent: 'free/openai-fast' },
|
|
26
|
+
enter: { subagent: 'default' }
|
|
29
27
|
},
|
|
30
28
|
enablePaidTools: false,
|
|
31
29
|
statusBar: true
|
|
@@ -40,7 +38,6 @@ function logConfig(msg) {
|
|
|
40
38
|
catch (e) { }
|
|
41
39
|
}
|
|
42
40
|
// SIMPLE LOAD (Direct Disk Read - No Caching, No Watchers)
|
|
43
|
-
// This ensures the Proxy ALWAYS sees the latest state from auth.json
|
|
44
41
|
export function loadConfig() {
|
|
45
42
|
return readConfigFromDisk();
|
|
46
43
|
}
|
|
@@ -52,6 +49,23 @@ function readConfigFromDisk() {
|
|
|
52
49
|
if (fs.existsSync(CONFIG_FILE)) {
|
|
53
50
|
const raw = fs.readFileSync(CONFIG_FILE, 'utf-8');
|
|
54
51
|
const custom = JSON.parse(raw);
|
|
52
|
+
// MIGRATION LOGIC (v5.5.0)
|
|
53
|
+
if (custom.fallbacks) {
|
|
54
|
+
if (custom.fallbacks.free) {
|
|
55
|
+
if (custom.fallbacks.free.main) {
|
|
56
|
+
custom.fallbacks.free.primary = custom.fallbacks.free.main;
|
|
57
|
+
delete custom.fallbacks.free.main;
|
|
58
|
+
}
|
|
59
|
+
if (custom.fallbacks.free.agent) {
|
|
60
|
+
custom.fallbacks.free.subagent = custom.fallbacks.free.agent;
|
|
61
|
+
delete custom.fallbacks.free.agent;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (custom.fallbacks.enter && custom.fallbacks.enter.agent) {
|
|
65
|
+
custom.fallbacks.enter.subagent = custom.fallbacks.enter.agent;
|
|
66
|
+
delete custom.fallbacks.enter.agent;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
55
69
|
config = { ...config, ...custom };
|
|
56
70
|
if (config.apiKey)
|
|
57
71
|
keyFound = true;
|
package/dist/server/proxy.js
CHANGED
|
@@ -33,37 +33,6 @@ function saveSignatureMap() {
|
|
|
33
33
|
log(`ERROR: Error mapping signature: ${String(e)}`);
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
|
-
/**
|
|
37
|
-
* DEEP INSPECTION LOGGING (Temporary for v5.5.0 Analysis)
|
|
38
|
-
* Dumps the full payload to a local log file to identify OpenCode agent signatures.
|
|
39
|
-
*/
|
|
40
|
-
function dumpPayload(body) {
|
|
41
|
-
try {
|
|
42
|
-
const logPath = '/home/fkomp/Bureau/oracle/opencode-pollinations-plugin/logs/payload_dump.log';
|
|
43
|
-
if (!fs.existsSync(path.dirname(logPath)))
|
|
44
|
-
fs.mkdirSync(path.dirname(logPath), { recursive: true });
|
|
45
|
-
// Extract interesting bits for quick reading
|
|
46
|
-
const systemPrompt = body.messages?.find((m) => m.role === 'system')?.content || "NONE";
|
|
47
|
-
const inspection = {
|
|
48
|
-
timestamp: new Date().toISOString(),
|
|
49
|
-
captured_fields: {
|
|
50
|
-
model: body.model,
|
|
51
|
-
user: body.user,
|
|
52
|
-
metadata: body.metadata,
|
|
53
|
-
agent: body.agent,
|
|
54
|
-
hasTools: !!(body.tools && body.tools.length > 0),
|
|
55
|
-
toolsCount: body.tools?.length || 0,
|
|
56
|
-
messageCount: body.messages?.length || 0,
|
|
57
|
-
systemPromptSnippet: systemPrompt.substring(0, 300) + "..."
|
|
58
|
-
},
|
|
59
|
-
full_body: body
|
|
60
|
-
};
|
|
61
|
-
fs.appendFileSync(logPath, JSON.stringify(inspection, null, 2) + "\n\n--- END OF REQUEST ---\n\n");
|
|
62
|
-
}
|
|
63
|
-
catch (e) {
|
|
64
|
-
console.error("[DebugLog] Failed to dump payload:", e);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
36
|
// RECURSIVE NORMALIZER for Stable Hashing
|
|
68
37
|
function normalizeContent(c) {
|
|
69
38
|
if (!c)
|
|
@@ -181,10 +150,15 @@ export async function handleChatCompletion(req, res, bodyRaw) {
|
|
|
181
150
|
let authHeader = undefined;
|
|
182
151
|
try {
|
|
183
152
|
const body = JSON.parse(bodyRaw);
|
|
184
|
-
dumpPayload(body); // <--- DEEP INSPECTION
|
|
185
153
|
const config = loadConfig();
|
|
186
|
-
//
|
|
187
|
-
|
|
154
|
+
// --- AGENT DETECTION (v5.5.0 Simplified) ---
|
|
155
|
+
const systemMsg = body.messages?.find((m) => m.role === 'system')?.content || "";
|
|
156
|
+
const isSubagent = systemMsg.includes("exploring codebases") ||
|
|
157
|
+
systemMsg.includes("title generator") ||
|
|
158
|
+
systemMsg.includes("researching complex questions") ||
|
|
159
|
+
systemMsg.includes("session summary");
|
|
160
|
+
const agentLabel = isSubagent ? "Subagent" : "Primary";
|
|
161
|
+
log(`[Proxy] Agent Detected: ${agentLabel} (Tools: ${body.tools?.length || 0})`);
|
|
188
162
|
// 0. COMMAND HANDLING
|
|
189
163
|
if (body.messages && body.messages.length > 0) {
|
|
190
164
|
const lastMsg = body.messages[body.messages.length - 1];
|
|
@@ -253,50 +227,26 @@ export async function handleChatCompletion(req, res, bodyRaw) {
|
|
|
253
227
|
actualModel = actualModel.replace('free/', '');
|
|
254
228
|
}
|
|
255
229
|
// B. SAFETY NETS (The Core V5 Logic)
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
230
|
+
// B. SPECIALIZED TARGETS (Subagents in Enter Universe)
|
|
231
|
+
if (isEnterprise && isSubagent) {
|
|
232
|
+
const specialized = config.fallbacks.enter.subagent;
|
|
233
|
+
if (specialized && specialized !== 'default') {
|
|
234
|
+
actualModel = specialized.replace('enter/', '').replace('free/', '');
|
|
235
|
+
if (specialized.startsWith('free/'))
|
|
261
236
|
isEnterprise = false;
|
|
262
|
-
|
|
263
|
-
fallbackReason = "Quota Unreachable (Safety)";
|
|
264
|
-
}
|
|
265
|
-
else {
|
|
266
|
-
const tierRatio = quota.tierLimit > 0 ? (quota.tierRemaining / quota.tierLimit) : 0;
|
|
267
|
-
if (tierRatio <= (config.thresholds.tier / 100)) {
|
|
268
|
-
log(`[SafetyNet] AlwaysFree Mode: Tier (${(tierRatio * 100).toFixed(1)}%) <= Threshold (${config.thresholds.tier}%). Switching.`);
|
|
269
|
-
actualModel = config.fallbacks.free.main.replace('free/', '');
|
|
270
|
-
isEnterprise = false;
|
|
271
|
-
isFallbackActive = true;
|
|
272
|
-
fallbackReason = `Daily Tier < ${config.thresholds.tier}% (Wallet Protected)`;
|
|
273
|
-
}
|
|
274
|
-
}
|
|
237
|
+
log(`[Route] Subagent specialized to: ${actualModel}`);
|
|
275
238
|
}
|
|
276
239
|
}
|
|
277
|
-
|
|
240
|
+
// C. SAFETY NETS (The Core V5 Logic)
|
|
241
|
+
if (config.mode === 'alwaysfree' || config.mode === 'pro') {
|
|
278
242
|
if (isEnterprise) {
|
|
279
|
-
if (quota.tier === 'error') {
|
|
280
|
-
|
|
281
|
-
|
|
243
|
+
if (!quota.canUseEnterprise || quota.tier === 'error') {
|
|
244
|
+
const fallbackModel = isSubagent ? config.fallbacks.free.subagent : config.fallbacks.free.primary;
|
|
245
|
+
log(`[SafetyNet] ${config.mode.toUpperCase()} Limit reached. Switching to: ${fallbackModel}`);
|
|
246
|
+
actualModel = fallbackModel.replace('free/', '');
|
|
282
247
|
isEnterprise = false;
|
|
283
248
|
isFallbackActive = true;
|
|
284
|
-
fallbackReason =
|
|
285
|
-
}
|
|
286
|
-
else {
|
|
287
|
-
const tierRatio = quota.tierLimit > 0 ? (quota.tierRemaining / quota.tierLimit) : 0;
|
|
288
|
-
// Logic: Fallback if Wallet is Low (< Threshold) AND Tier is Exhausted (< Threshold %)
|
|
289
|
-
// Wait, user wants priority to Free Tier.
|
|
290
|
-
// If Free Tier is available (Ratio > Threshold), we usage it (don't fallback).
|
|
291
|
-
// If Free Tier is exhausted (Ratio <= Threshold), THEN check Wallet.
|
|
292
|
-
// If Wallet also Low, THEN Fallback.
|
|
293
|
-
if (quota.walletBalance < config.thresholds.wallet && tierRatio <= (config.thresholds.tier / 100)) {
|
|
294
|
-
log(`[SafetyNet] Pro Mode: Wallet < $${config.thresholds.wallet} AND Tier < ${config.thresholds.tier}%. Switching.`);
|
|
295
|
-
actualModel = config.fallbacks.free.main.replace('free/', '');
|
|
296
|
-
isEnterprise = false;
|
|
297
|
-
isFallbackActive = true;
|
|
298
|
-
fallbackReason = `Wallet & Tier Critical`;
|
|
299
|
-
}
|
|
249
|
+
fallbackReason = `${config.mode.toUpperCase()} Limit Reached`;
|
|
300
250
|
}
|
|
301
251
|
}
|
|
302
252
|
}
|
|
@@ -502,7 +452,7 @@ export async function handleChatCompletion(req, res, bodyRaw) {
|
|
|
502
452
|
log(`[SafetyNet] Upstream Rejection (${fetchRes.status}). Triggering Transparent Fallback.`);
|
|
503
453
|
if (isEnterpriseFallback) {
|
|
504
454
|
// 1a. Enterprise -> Free Fallback
|
|
505
|
-
actualModel = config.fallbacks.free.
|
|
455
|
+
actualModel = config.fallbacks.free.primary.replace('free/', '');
|
|
506
456
|
isEnterprise = false;
|
|
507
457
|
isFallbackActive = true;
|
|
508
458
|
if (fetchRes.status === 402)
|
package/dist/server/quota.js
CHANGED
|
@@ -66,6 +66,12 @@ export async function getQuotaStatus(forceRefresh = false) {
|
|
|
66
66
|
// Le wallet c'est le reste (balance totale - ce qu'il reste du tier gratuit non consommé)
|
|
67
67
|
const walletBalance = Math.max(0, balance - cleanTierRemaining);
|
|
68
68
|
const cleanWalletBalance = Math.max(0, parseFloat(walletBalance.toFixed(4)));
|
|
69
|
+
// Calculer les marges de sécurité basées sur les seuils configurés
|
|
70
|
+
const tierThresholdPollen = (config.thresholds.tier / 100) * tierLimit;
|
|
71
|
+
const walletThresholdUSD = config.thresholds.wallet; // Wallet threshold is already in $ in config
|
|
72
|
+
// canUseEnterprise: Vrai si on a encore du budget au-dessus du seuil de fallback
|
|
73
|
+
const hasAvailableTier = cleanTierRemaining > tierThresholdPollen;
|
|
74
|
+
const hasAvailableWallet = cleanWalletBalance > walletThresholdUSD;
|
|
69
75
|
cachedQuota = {
|
|
70
76
|
tierRemaining: cleanTierRemaining,
|
|
71
77
|
tierUsed,
|
|
@@ -73,9 +79,9 @@ export async function getQuotaStatus(forceRefresh = false) {
|
|
|
73
79
|
walletBalance: cleanWalletBalance,
|
|
74
80
|
nextResetAt: resetInfo.nextReset,
|
|
75
81
|
timeUntilReset: resetInfo.timeUntilReset,
|
|
76
|
-
canUseEnterprise:
|
|
77
|
-
isUsingWallet:
|
|
78
|
-
needsAlert: tierLimit > 0 ? (cleanTierRemaining
|
|
82
|
+
canUseEnterprise: hasAvailableTier || hasAvailableWallet,
|
|
83
|
+
isUsingWallet: !hasAvailableTier && hasAvailableWallet,
|
|
84
|
+
needsAlert: tierLimit > 0 ? (cleanTierRemaining <= tierThresholdPollen) : false,
|
|
79
85
|
tier: profile.tier,
|
|
80
86
|
tierEmoji: tierInfo.emoji
|
|
81
87
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-pollinations-plugin",
|
|
3
3
|
"displayName": "Pollinations AI (V5.1)",
|
|
4
|
-
"version": "5.
|
|
4
|
+
"version": "5.5.0-debug.3",
|
|
5
5
|
"description": "Native Pollinations.ai Provider Plugin for OpenCode",
|
|
6
6
|
"publisher": "pollinations",
|
|
7
7
|
"repository": {
|