opencode-pollinations-plugin 6.1.0-beta.2 → 6.1.0-beta.23
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.md +242 -62
- package/dist/index.js +68 -159
- package/dist/server/commands.d.ts +6 -0
- package/dist/server/commands.js +400 -71
- package/dist/server/config.d.ts +32 -23
- package/dist/server/config.js +183 -99
- package/dist/server/connect-response.d.ts +2 -0
- package/dist/server/connect-response.js +141 -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 +36 -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 +150 -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 +21 -0
- package/dist/server/models/worker.js +97 -0
- package/dist/server/pollinations-api.d.ts +11 -0
- package/dist/server/pollinations-api.js +21 -8
- package/dist/server/proxy.js +223 -160
- 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 +24 -0
- package/dist/tools/index.js +83 -0
- package/dist/tools/pollinations/beta_discovery.d.ts +9 -0
- package/dist/tools/pollinations/beta_discovery.js +197 -0
- package/dist/tools/pollinations/cost-guard.d.ts +38 -0
- package/dist/tools/pollinations/cost-guard.js +141 -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 +246 -0
- package/dist/tools/pollinations/gen_image.d.ts +11 -0
- package/dist/tools/pollinations/gen_image.js +225 -0
- package/dist/tools/pollinations/gen_music.d.ts +14 -0
- package/dist/tools/pollinations/gen_music.js +180 -0
- package/dist/tools/pollinations/gen_video.d.ts +16 -0
- package/dist/tools/pollinations/gen_video.js +256 -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 +164 -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 +165 -0
- package/dist/tools/pollinations/shared.js +665 -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 +194 -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 +392 -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 +10 -4
- package/dist/server/models-seed.d.ts +0 -18
- package/dist/server/models-seed.js +0 -55
package/README.md
CHANGED
|
@@ -1,117 +1,297 @@
|
|
|
1
1
|
# 🌸 Pollinations AI Plugin for OpenCode
|
|
2
2
|
|
|
3
3
|
<div align="center">
|
|
4
|
-
<img src="https://avatars.githubusercontent.com/u/88394740?s=400&v=4" alt="Pollinations.ai Logo" width="
|
|
5
|
-
<br>
|
|
6
|
-
<b>The
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-

|
|
4
|
+
<img src="https://avatars.githubusercontent.com/u/88394740?s=400&v=4" alt="Pollinations.ai Logo" width="180">
|
|
5
|
+
<br><br>
|
|
6
|
+
<b>The most complete bridge between OpenCode and the Pollinations.ai ecosystem.</b><br>
|
|
7
|
+
Free AI models, multimodal generation, and smart quota management — directly in your editor.
|
|
8
|
+
<br><br>
|
|
9
|
+
|
|
10
|
+

|
|
11
|
+

|
|
14
12
|

|
|
15
|
-
 | [🛣️ Roadmap](./ROADMAP.md)
|
|
13
|
+

|
|
14
|
+

|
|
18
15
|
|
|
19
16
|
</div>
|
|
20
17
|
|
|
21
18
|
---
|
|
22
19
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
-
|
|
26
|
-
- **🧠 Reasoning Models**: Access advanced thinking models (DeepSeek, Kimi, Perplexity)
|
|
27
|
-
- **🛠️ Native Tools**: Full function calling support for compatible models
|
|
28
|
-
- **🔐 Secure Auth**: Native OpenCode `/connect` integration
|
|
29
|
-
- **🔄 Hot-Reload**: Change API keys without restarting OpenCode
|
|
20
|
+
> **"No closed doors, no corporate hoops — just good tools and good people."**
|
|
21
|
+
>
|
|
22
|
+
> Pollinations.ai is an open-source platform built by and for the creative community. This plugin brings it entirely inside OpenCode: text, image, audio, video, music, diagrams, and more — with a transparent fallback system that means you're *never* blocked.
|
|
30
23
|
|
|
31
24
|
---
|
|
32
25
|
|
|
33
|
-
##
|
|
34
|
-
|
|
35
|
-
**Pollen** is Pollinations' unified credit system. $1 ≈ 1 Pollen.
|
|
26
|
+
## ✨ What's New in v6.1-beta.22
|
|
36
27
|
|
|
37
|
-
|
|
38
|
-
|:-----|:------------|:------------|
|
|
39
|
-
| 🦠 **Microbe** | 0.1 Pollen | Flagged accounts |
|
|
40
|
-
| 🌱 **Spore** | 1 Pollen | Sign up |
|
|
41
|
-
| 🌿 **Seed** | 3 Pollen | Active GitHub dev (8+ points) |
|
|
42
|
-
| 🌸 **Flower** | 10 Pollen | Published app |
|
|
43
|
-
| 🍯 **Nectar** | 20 Pollen | Major contributor |
|
|
28
|
+
The jump from v5.9 to v6.1 is not incremental. The plugin has grown from a smart proxy into a **full multimodal agent toolkit**:
|
|
44
29
|
|
|
45
|
-
|
|
30
|
+
- **15+ native tools** for generation, design, and media processing — usable directly in OpenCode's agent mode
|
|
31
|
+
- **Smart Fetch Quota**: 100% exact math fetching directly from Pollinations API for quota verification (zero local cache)
|
|
32
|
+
- **Dynamic Pricing**: Tool costs are estimated live based on Tinybird API model stats. Token models have a built-in max theoretical threshold limit (Cost Guard).
|
|
33
|
+
- **Stealth notifications**: status toasts now only fire in relevant contexts, no more noise
|
|
34
|
+
- **Background removal** with multi-key rotation and automatic fallback to free provider
|
|
35
|
+
- **Video, music, audio generation** via the Pollinations API
|
|
36
|
+
- **Web search + scraping** integrated as agent tools
|
|
46
37
|
|
|
47
38
|
---
|
|
48
39
|
|
|
49
40
|
## 🚀 Quick Start
|
|
50
41
|
|
|
51
|
-
### 1. Install Plugin
|
|
52
42
|
```bash
|
|
43
|
+
# Install globally
|
|
53
44
|
npm install -g opencode-pollinations-plugin
|
|
54
|
-
npx opencode-pollinations-plugin
|
|
55
45
|
```
|
|
56
46
|
|
|
57
|
-
|
|
47
|
+
Then in OpenCode:
|
|
58
48
|
```
|
|
59
49
|
/connect
|
|
60
50
|
```
|
|
61
|
-
Select **pollinations** → Enter your API key from [enter.pollinations.ai](https://enter.pollinations.ai)
|
|
51
|
+
Select **pollinations** → Enter your API key from [enter.pollinations.ai](https://enter.pollinations.ai), or leave blank to use the free tier.
|
|
52
|
+
|
|
53
|
+
Select any `pollinations/*` text model and start chatting. **No key required for text.**
|
|
62
54
|
|
|
63
|
-
|
|
64
|
-
|
|
55
|
+
> ⚠️ **Note:** Image generation (`gen_image`) requires a Pollinations API Key. Text models remain free.
|
|
56
|
+
|
|
57
|
+
> ⚠️ After connecting a new key, restart OpenCode once for the model list to update.
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## 🌐 Free Universe — No Key Required
|
|
62
|
+
|
|
63
|
+
Access a wide range of models with **zero setup**, supported by Pollinations' ad model:
|
|
64
|
+
|
|
65
|
+
| Model | Speed | Context | Notes |
|
|
66
|
+
|-------|:-----:|:-------:|-------|
|
|
67
|
+
| `openai` / `openai-large` | Fast | 128k | GPT-4o class |
|
|
68
|
+
| `gemini` / `gemini-search` | Fast | 1M | Google Gemini |
|
|
69
|
+
| `mistral` / `qwen-coder` | Fast | 32k | Code-focused |
|
|
70
|
+
| `nova-fast` / `grok` | Very Fast | 32k | Quick tasks |
|
|
71
|
+
| `deepseek` / `kimi` | Medium | 128k | Reasoning |
|
|
72
|
+
|
|
73
|
+
> 💡 Using Gemini with tool calls? The plugin auto-detects incompatibilities and transparently falls back to OpenAI — your workflow never breaks.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## 💎 Pro Mode — Unlock Premium Models
|
|
78
|
+
|
|
79
|
+
Connect your [Pollinations API key](https://enter.pollinations.ai) to access enterprise-grade models charged in **Pollen** (the unified credit, ~$1 = 1 Pollen):
|
|
80
|
+
|
|
81
|
+
| Model | Provider | Notes |
|
|
82
|
+
|-------|----------|-------|
|
|
83
|
+
| `claude` / `claude-large` | Anthropic | Requires wallet credits |
|
|
84
|
+
| `gemini-large` | Google | Requires wallet credits |
|
|
85
|
+
| `gpt-4o` / `openai-large` | OpenAI | Daily grant available |
|
|
86
|
+
| `deepseek-coder` | DeepSeek | Daily grant available |
|
|
87
|
+
|
|
88
|
+
### 🛡️ Safety Net — You're Never Blocked
|
|
89
|
+
|
|
90
|
+
If your quota or wallet runs low mid-session, the plugin switches automatically to a free model, injects a visible warning in the response stream, and continues. No errors. No broken sessions.
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## 🔧 Agent Tools (v6.1 — New)
|
|
95
|
+
|
|
96
|
+
When OpenCode uses the plugin in agent mode, it gets access to a rich toolbox organized in three categories:
|
|
97
|
+
|
|
98
|
+
### 🎨 Pollinations Generation Tools
|
|
99
|
+
|
|
100
|
+
These tools call the Pollinations APIs directly, enabling the AI to generate media as part of its reasoning:
|
|
101
|
+
|
|
102
|
+
| Tool | Description |
|
|
103
|
+
|------|-------------|
|
|
104
|
+
| `gen_image` | Generate images from a text prompt (Flux, SDXL, etc.) **[Requires Key]** |
|
|
105
|
+
| `gen_audio` | Generate speech or sound effects |
|
|
106
|
+
| `gen_music` | Generate music from a description |
|
|
107
|
+
| `gen_video` | Generate short video clips |
|
|
108
|
+
| `transcribe_audio` | Transcribe an audio file to text |
|
|
109
|
+
| `deepsearch` | Multi-step AI-powered research |
|
|
110
|
+
| `search_crawl_scrape` | Web search with full page scraping |
|
|
111
|
+
| `beta_discovery` | Probe undocumented model parameters (400 validation extraction) |
|
|
112
|
+
|
|
113
|
+
### 🖌️ Design Tools
|
|
114
|
+
|
|
115
|
+
| Tool | Description |
|
|
116
|
+
|------|-------------|
|
|
117
|
+
| `gen_diagram` | Generate diagrams (flowcharts, architecture, etc.) |
|
|
118
|
+
| `gen_palette` | Create color palettes from a description or image |
|
|
119
|
+
| `gen_qrcode` | Generate styled QR codes |
|
|
120
|
+
|
|
121
|
+
### ⚡ Power Tools
|
|
122
|
+
|
|
123
|
+
| Tool | Description |
|
|
124
|
+
|------|-------------|
|
|
125
|
+
| `remove_background` | Remove image backgrounds (free or BackgroundCut HD) |
|
|
126
|
+
| `rmbg_keys` | Manage BackgroundCut API keys with rotation |
|
|
127
|
+
| `extract_audio` | Extract the audio track from a video file |
|
|
128
|
+
| `extract_frames` | Extract frames from a video at a given interval |
|
|
129
|
+
| `file_to_url` | Upload a local file and return a public URL |
|
|
130
|
+
|
|
131
|
+
> **Background Removal** supports multi-key rotation: if one BackgroundCut key is exhausted or rate-limited, the plugin automatically rotates to the next available key, then falls back to the free provider if all keys fail.
|
|
65
132
|
|
|
66
133
|
---
|
|
67
134
|
|
|
68
135
|
## 👁️ Vision Support
|
|
69
136
|
|
|
70
|
-
|
|
137
|
+
Paste images directly into the chat or use an image URL. The plugin handles encoding automatically.
|
|
71
138
|
|
|
72
139
|
| Model | Vision | Reasoning | Tools |
|
|
73
140
|
|-------|:------:|:---------:|:-----:|
|
|
74
|
-
| `openai` / `openai-large` | ✅ |
|
|
75
|
-
| `gemini` / `gemini-search` | ✅ |
|
|
76
|
-
| `claude` / `claude-large` | ✅ |
|
|
77
|
-
| `kimi` | ✅ | ✅ |
|
|
78
|
-
| `openai-audio` | ✅ |
|
|
141
|
+
| `openai` / `openai-large` | ✅ | — | ✅ |
|
|
142
|
+
| `gemini` / `gemini-search` | ✅ | — | ✅ |
|
|
143
|
+
| `claude` / `claude-large` | ✅ | — | ✅ |
|
|
144
|
+
| `kimi` | ✅ | ✅ | — |
|
|
145
|
+
| `openai-audio` | ✅ | — | ✅ |
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## 💰 Understanding Pollen & Tiers
|
|
150
|
+
|
|
151
|
+
**Pollen** is Pollinations' unified credit system. **$1 ≈ 1 Pollen.**
|
|
152
|
+
|
|
153
|
+
Every registered user receives a **daily Pollen grant** based on their tier:
|
|
154
|
+
|
|
155
|
+
| Tier | Daily Grant | Requirement |
|
|
156
|
+
|:-----|:------------|:------------|
|
|
157
|
+
| 🦠 **Microbe** | 0.1 Pollen | Flagged accounts |
|
|
158
|
+
| 🌱 **Spore** | 1 Pollen | Sign up |
|
|
159
|
+
| 🌿 **Seed** | 3 Pollen | Active GitHub dev (8+ points) |
|
|
160
|
+
| 🌸 **Flower** | 10 Pollen | Published app |
|
|
161
|
+
| 🍯 **Nectar** | 20 Pollen | Major contributor |
|
|
162
|
+
|
|
163
|
+
> 🌸 Publishing this plugin to the OpenCode ecosystem earned its author **Flower** tier (10 Pollen/day). [Register here to claim yours.](https://enter.pollinations.ai)
|
|
164
|
+
|
|
165
|
+
Certain models (`claude-large`, `gemini-large`, `veo`, `seedream-pro`) are **Paid-Only**: they require wallet credits on top of (or instead of) daily grants.
|
|
166
|
+
|
|
167
|
+
---
|
|
79
168
|
|
|
80
|
-
|
|
169
|
+
## 📊 Commands
|
|
170
|
+
|
|
171
|
+
```
|
|
172
|
+
/pollinations usage # View Pollen balance + tier status
|
|
173
|
+
/pollinations usage full # Detailed per-model breakdown
|
|
174
|
+
/pollinations mode [manual|alwaysfree|pro] # Change routing mode
|
|
175
|
+
/pollinations status # Plugin health check
|
|
176
|
+
/pollinations config [key] [value] # Read or write config values
|
|
177
|
+
/pollinations fallback <main> [agent] # Configure fallback models
|
|
178
|
+
/pollinations help # Full command reference
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Aliases: `/poll` works as a shorthand for all commands.
|
|
182
|
+
|
|
183
|
+
### Config Keys
|
|
184
|
+
|
|
185
|
+
| Key | Values | Description |
|
|
186
|
+
|-----|--------|-------------|
|
|
187
|
+
| `status_gui` | `none` / `alert` / `all` | Toast verbosity |
|
|
188
|
+
| `logs_gui` | `none` / `error` / `verbose` | Technical log verbosity |
|
|
189
|
+
| `threshold_tier` | `0-100` | Alert threshold for tier (%) |
|
|
190
|
+
| `threshold_wallet` | `0-100` | Safety Net trigger ($) |
|
|
191
|
+
| `status_bar` | `true` / `false` | Status bar widget |
|
|
81
192
|
|
|
82
193
|
---
|
|
83
194
|
|
|
84
|
-
##
|
|
195
|
+
## 🔑 API Key Types
|
|
196
|
+
|
|
197
|
+
| Type | Access |
|
|
198
|
+
|------|--------|
|
|
199
|
+
| **Standard (`pk_...`)** | Full access: models, usage dashboard, quota |
|
|
200
|
+
| **Limited** | Generation only. Dashboard shows a restriction alert. The plugin auto-switches to Manual mode to avoid quota errors. |
|
|
201
|
+
| **Legacy (`sk_...`)** | Accepted for backward compatibility |
|
|
85
202
|
|
|
86
|
-
|
|
87
|
-
- **OpenAI**: `openai`, `openai-fast`, `openai-large`
|
|
88
|
-
- **Gemini**: `gemini`, `gemini-fast`, `gemini-search`, `gemini-large` 💎
|
|
89
|
-
- **Claude**: `claude-fast`, `claude`, `claude-large` 💎
|
|
90
|
-
- **Reasoning**: `deepseek`, `kimi`, `perplexity-reasoning`
|
|
91
|
-
- **Code**: `qwen-coder`, `mistral`
|
|
92
|
-
- **Fast**: `nova-fast`, `grok`
|
|
203
|
+
---
|
|
93
204
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
205
|
+
## 🛠️ Routing Modes
|
|
206
|
+
|
|
207
|
+
| Mode | Behavior |
|
|
208
|
+
|------|----------|
|
|
209
|
+
| `manual` | You choose the model, no automatic switching |
|
|
210
|
+
| `alwaysfree` | Only free models, never charges Pollen |
|
|
211
|
+
| `pro` | Enterprise models with automatic free fallback when quota/wallet is low |
|
|
97
212
|
|
|
98
213
|
---
|
|
99
214
|
|
|
100
|
-
##
|
|
215
|
+
## 🏗️ Architecture (Summary)
|
|
216
|
+
|
|
217
|
+
The plugin runs a **local HTTP proxy** on a dynamically assigned port (system port 0 — no conflicts, cross-platform). OpenCode points its `pollinations` provider at this proxy. The proxy reads your config, checks quota, applies model-specific sanitizations, and forwards requests to the correct Pollinations endpoint.
|
|
101
218
|
|
|
102
219
|
```
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
220
|
+
OpenCode TUI
|
|
221
|
+
│ POST /v1/chat/completions
|
|
222
|
+
▼
|
|
223
|
+
Local Proxy (Dynamic Port)
|
|
224
|
+
├── Safety Net Logic (quota check, fallback)
|
|
225
|
+
├── Model Sanitization (Azure/Vertex/Gemini/Kimi)
|
|
226
|
+
└── Route to:
|
|
227
|
+
├── text.pollinations.ai (Free Universe)
|
|
228
|
+
└── gen.pollinations.ai (Enterprise Universe)
|
|
106
229
|
```
|
|
107
230
|
|
|
231
|
+
Config is read from (highest priority first):
|
|
232
|
+
1. `~/.pollinations/config.json`
|
|
233
|
+
2. `~/.local/share/opencode/auth.json`
|
|
234
|
+
3. `~/.config/opencode/opencode.json`
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
## 🗺️ Roadmap
|
|
239
|
+
|
|
240
|
+
### ✅ Shipped (v5.x → v6.1-beta)
|
|
241
|
+
- Free + Enterprise proxy with transparent fallback (Safety Net)
|
|
242
|
+
- Dynamic port allocation — cross-platform (no more `fuser`, no port conflicts)
|
|
243
|
+
- Pollen/tier quota tracking with Smart Fetch API (no local logging)
|
|
244
|
+
- Dynamic Pricing (Tinybird stats) and Cost Guard max thresholds
|
|
245
|
+
- Agent tools: image, audio, music, video generation
|
|
246
|
+
- Agent tools: web search, scraping, deep research
|
|
247
|
+
- Design tools: diagrams, palettes, QR codes
|
|
248
|
+
- Power tools: background removal with key rotation, frame/audio extraction
|
|
249
|
+
- Stealth notification mode (toasts only in relevant sessions)
|
|
250
|
+
- Limited-key support with automatic mode switching
|
|
251
|
+
- Gemini tools auto-fallback to OpenAI
|
|
252
|
+
- Enterprise schema sanitization (Azure, Vertex, Bedrock, Kimi)
|
|
253
|
+
|
|
254
|
+
### 🔜 Next (v6.2 — v6.5, 2026)
|
|
255
|
+
- **Config file watcher** — hot-reload without restarting OpenCode
|
|
256
|
+
- **Signature map rotation** — LRU eviction to prevent unbounded memory growth
|
|
257
|
+
- **Unit tests** — coverage for `proxy.ts`, `quota.ts`, `tools/`
|
|
258
|
+
- **`/poll status` one-liner** — faster than opening the dashboard
|
|
259
|
+
- **Structured logging** — JSON format, configurable log levels, log rotation
|
|
260
|
+
- **Model search** — `/poll models <query>` to filter the model list
|
|
261
|
+
|
|
262
|
+
### 🔭 Longer Term (v7.0+, 2027)
|
|
263
|
+
- **Smart Router** — cost-aware and latency-aware model selection
|
|
264
|
+
- **Multi-provider failover** — fallback to OpenRouter if Pollinations is unreachable
|
|
265
|
+
- **Web Dashboard** — browser UI for monitoring, config, and analytics
|
|
266
|
+
- **Team features** — shared quotas and API keys
|
|
267
|
+
- **Persistent memory** — vector DB integration for long-running agents
|
|
268
|
+
|
|
269
|
+
> Community ideas with the most votes: API usage alerts, conversation export (Markdown/JSON), model comparison mode. Open an issue to vote!
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
## 🤝 Contributing
|
|
274
|
+
|
|
275
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for setup instructions, code guidelines, and priority areas (testing, docs, i18n, DevOps).
|
|
276
|
+
|
|
277
|
+
### Areas Where Help Is Needed
|
|
278
|
+
- 🧪 **Testing** — Unit + integration tests for the proxy and tools
|
|
279
|
+
- 📚 **Docs** — User guides, tool examples
|
|
280
|
+
- 🌍 **i18n** — French/English consistency, German and Spanish translations
|
|
281
|
+
- 🎨 **UX** — Command output formatting improvements
|
|
282
|
+
|
|
108
283
|
---
|
|
109
284
|
|
|
110
285
|
## 🔗 Links
|
|
111
286
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
287
|
+
| Resource | Link |
|
|
288
|
+
|----------|------|
|
|
289
|
+
| Sign up for Pollinations (free tiers + paid models) | [enter.pollinations.ai](https://enter.pollinations.ai) |
|
|
290
|
+
| Pollinations website | [pollinations.ai](https://pollinations.ai) |
|
|
291
|
+
| Pollinations GitHub | [github.com/pollinations/pollinations](https://github.com/pollinations/pollinations) |
|
|
292
|
+
| Discord community | [Join us!](https://discord.gg/pollinations-ai-885844321461485618) |
|
|
293
|
+
| OpenCode ecosystem | [opencode.ai/docs/ecosystem](https://opencode.ai/docs/ecosystem#plugins) |
|
|
294
|
+
| Plugin author | [@fkom13](https://github.com/fkom13) |
|
|
115
295
|
|
|
116
296
|
---
|
|
117
297
|
|
package/dist/index.js
CHANGED
|
@@ -1,31 +1,21 @@
|
|
|
1
1
|
import * as http from 'http';
|
|
2
|
-
import * as fs from 'fs';
|
|
3
2
|
import { generatePollinationsConfig } from './server/generate-config.js';
|
|
4
|
-
import { loadConfig,
|
|
3
|
+
import { loadConfig, migrateLegacyConfig } from './server/config.js';
|
|
5
4
|
import { handleChatCompletion } from './server/proxy.js';
|
|
6
|
-
import { createToastHooks, setGlobalClient } from './server/toast.js';
|
|
5
|
+
import { createToastHooks, createToolHooks, setGlobalClient } from './server/toast.js';
|
|
7
6
|
import { createStatusHooks } from './server/status.js';
|
|
8
|
-
import { createCommandHooks } from './server/commands.js';
|
|
7
|
+
import { createCommandHooks, setClientForCommands } from './server/commands.js';
|
|
8
|
+
import { createToolRegistry } from './tools/index.js';
|
|
9
9
|
import { createRequire } from 'module';
|
|
10
10
|
const require = createRequire(import.meta.url);
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
fs.appendFileSync(LOG_FILE, `[${new Date().toISOString()}] ${msg}\n`);
|
|
15
|
-
}
|
|
16
|
-
catch (e) { }
|
|
17
|
-
}
|
|
18
|
-
// === PROXY SERVER (Singleton with Fixed Port) ===
|
|
19
|
-
const DEFAULT_PORT = 18888;
|
|
20
|
-
const GLOBAL_SERVER_KEY = '__POLLINATIONS_PROXY_SERVER__';
|
|
11
|
+
import { log } from './server/logger.js';
|
|
12
|
+
import { ModelRegistry } from './server/models/index.js';
|
|
13
|
+
const sessionModels = new Map();
|
|
21
14
|
const startProxy = () => {
|
|
22
|
-
// Check if server exists in global scope (survives module reloads)
|
|
23
|
-
if (global[GLOBAL_SERVER_KEY]) {
|
|
24
|
-
log(`[Proxy] Reusing existing global server on port ${DEFAULT_PORT}`);
|
|
25
|
-
return Promise.resolve(DEFAULT_PORT);
|
|
26
|
-
}
|
|
27
15
|
return new Promise((resolve) => {
|
|
28
16
|
const server = http.createServer(async (req, res) => {
|
|
17
|
+
// ... (Request Handling) ...
|
|
18
|
+
// We reuse the existing logic structure but simplified startup
|
|
29
19
|
log(`[Proxy] Request: ${req.method} ${req.url}`);
|
|
30
20
|
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
31
21
|
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
|
@@ -67,156 +57,70 @@ const startProxy = () => {
|
|
|
67
57
|
res.writeHead(404);
|
|
68
58
|
res.end("Not Found");
|
|
69
59
|
});
|
|
70
|
-
//
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
log(`[Proxy] Port ${port} in use, falling back to dynamic port`);
|
|
82
|
-
server.removeAllListeners('error');
|
|
83
|
-
tryListen(0, false); // Try dynamic port
|
|
84
|
-
}
|
|
85
|
-
else {
|
|
86
|
-
log(`[Proxy] Fatal Error: ${e}`);
|
|
87
|
-
resolve(0);
|
|
88
|
-
}
|
|
89
|
-
});
|
|
90
|
-
};
|
|
91
|
-
tryListen(DEFAULT_PORT, true);
|
|
60
|
+
// Listen on random port (0) to avoid conflicts (CLI/IDE)
|
|
61
|
+
server.listen(0, '127.0.0.1', () => {
|
|
62
|
+
// @ts-ignore
|
|
63
|
+
const assignedPort = server.address().port;
|
|
64
|
+
log(`[Proxy] Started v${require('../package.json').version} (Dynamic Port) on port ${assignedPort}`);
|
|
65
|
+
resolve(assignedPort);
|
|
66
|
+
});
|
|
67
|
+
server.on('error', (e) => {
|
|
68
|
+
log(`[Proxy] Fatal Error: ${e}`);
|
|
69
|
+
resolve(0);
|
|
70
|
+
});
|
|
92
71
|
});
|
|
93
72
|
};
|
|
94
|
-
// === AUTH HOOK: Native /connect Integration ===
|
|
95
|
-
// Auth Hook moved inside plugin to access context
|
|
96
73
|
// === PLUGIN EXPORT ===
|
|
97
74
|
export const PollinationsPlugin = async (ctx) => {
|
|
98
|
-
|
|
99
|
-
|
|
75
|
+
const v = require('../package.json').version;
|
|
76
|
+
log(`Plugin Initializing v${v}...`);
|
|
77
|
+
log(`[ENV] Keys: ${Object.keys(process.env).filter(k => k.includes('OPENCODE') || k.includes('APP') || k.includes('DATA')).join(', ')}`);
|
|
78
|
+
console.log(`🚀 POLLINATIONS PLUGIN v${v} LOADED 🚀`);
|
|
79
|
+
// MIGRATE CONFIG
|
|
80
|
+
migrateLegacyConfig();
|
|
81
|
+
// START PROXY
|
|
100
82
|
const port = await startProxy();
|
|
101
83
|
const localBaseUrl = `http://127.0.0.1:${port}/v1`;
|
|
84
|
+
// INIT MODEL REGISTRY (non-blocking, fire-and-forget)
|
|
85
|
+
ModelRegistry.refresh().then(() => {
|
|
86
|
+
const stats = ModelRegistry.stats();
|
|
87
|
+
log(`[ModelRegistry] Ready: ${stats.image} image, ${stats.video} video, ${stats.audio} audio, ${stats.text} text`);
|
|
88
|
+
// Démarrage du patcher asynchrone des descriptions des Outils (Phase 1.5)
|
|
89
|
+
import('./server/models/worker.js').then(module => {
|
|
90
|
+
module.ToolRegistryWorker.start();
|
|
91
|
+
}).catch(e => log(`[ToolWorker] Failed to load worker: ${e}`));
|
|
92
|
+
}).catch(e => log(`[ModelRegistry] Init failed (will use fallback): ${e}`));
|
|
102
93
|
setGlobalClient(ctx.client);
|
|
94
|
+
setClientForCommands(ctx.client);
|
|
103
95
|
const toastHooks = createToastHooks(ctx.client);
|
|
104
96
|
const commandHooks = createCommandHooks();
|
|
105
|
-
//
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
if (isRefreshing)
|
|
109
|
-
return;
|
|
110
|
-
isRefreshing = true;
|
|
111
|
-
try {
|
|
112
|
-
log('[Event] Refreshing provider config after auth update...');
|
|
113
|
-
const modelsArray = await generatePollinationsConfig();
|
|
114
|
-
const modelsObj = {};
|
|
115
|
-
for (const m of modelsArray) {
|
|
116
|
-
modelsObj[m.id] = m;
|
|
117
|
-
}
|
|
118
|
-
const version = require('../package.json').version;
|
|
119
|
-
// CRITICAL: Fetch current config first to avoid overwriting other providers
|
|
120
|
-
let currentConfig = {};
|
|
121
|
-
try {
|
|
122
|
-
// Try to fetch existing config to preserve other providers
|
|
123
|
-
const response = await ctx.client.fetch('/config');
|
|
124
|
-
if (response.ok) {
|
|
125
|
-
currentConfig = await response.json();
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
catch (err) {
|
|
129
|
-
log(`[Event] Warning: Could not fetch current config: ${err}`);
|
|
130
|
-
}
|
|
131
|
-
// Safe Merge
|
|
132
|
-
if (!currentConfig.provider)
|
|
133
|
-
currentConfig.provider = {};
|
|
134
|
-
currentConfig.provider.pollinations = {
|
|
135
|
-
id: 'openai',
|
|
136
|
-
name: `Pollinations AI (v${version})`,
|
|
137
|
-
options: {
|
|
138
|
-
baseURL: localBaseUrl,
|
|
139
|
-
apiKey: 'plugin-managed',
|
|
140
|
-
},
|
|
141
|
-
models: modelsObj
|
|
142
|
-
};
|
|
143
|
-
// Use Server API to update config with the MERGED object
|
|
144
|
-
await ctx.client.fetch('/config', {
|
|
145
|
-
method: 'PATCH',
|
|
146
|
-
headers: { 'Content-Type': 'application/json' },
|
|
147
|
-
body: JSON.stringify({
|
|
148
|
-
provider: currentConfig.provider
|
|
149
|
-
})
|
|
150
|
-
});
|
|
151
|
-
log(`[Event] Provider config refreshed with ${Object.keys(modelsObj).length} models.`);
|
|
152
|
-
}
|
|
153
|
-
catch (e) {
|
|
154
|
-
log(`[Event] Failed to refresh provider config: ${e}`);
|
|
155
|
-
}
|
|
156
|
-
finally {
|
|
157
|
-
// Debounce: prevent another refresh for 5 seconds
|
|
158
|
-
setTimeout(() => { isRefreshing = false; }, 5000);
|
|
159
|
-
}
|
|
160
|
-
};
|
|
97
|
+
// Build tool registry (conditional on API key presence)
|
|
98
|
+
const toolRegistry = createToolRegistry();
|
|
99
|
+
log(`[Tools] ${Object.keys(toolRegistry).length} tools registered`);
|
|
161
100
|
return {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
const authData = await auth();
|
|
169
|
-
if (authData && 'key' in authData) {
|
|
170
|
-
const k = authData.key;
|
|
171
|
-
saveConfig({ apiKey: k });
|
|
172
|
-
return { apiKey: k };
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
catch (e) {
|
|
176
|
-
log(`[AuthHook] loader error: ${e}`);
|
|
101
|
+
"chat.message": async (input) => {
|
|
102
|
+
const m = input.model;
|
|
103
|
+
if (m) {
|
|
104
|
+
if (m.modelID && !m.modelID.includes('pollimock-handler')) {
|
|
105
|
+
sessionModels.set(input.sessionID, `${m.providerID}/${m.modelID}`);
|
|
106
|
+
log(`[Hook] Saved active model ${m.providerID}/${m.modelID} for session ${input.sessionID}`);
|
|
177
107
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
label: 'Pollinations API Key',
|
|
186
|
-
prompts: [{
|
|
187
|
-
type: 'text',
|
|
188
|
-
key: 'apiKey',
|
|
189
|
-
message: 'Enter Pollinations API Key (starts with sk_)',
|
|
190
|
-
validate: (v) => (!v || v.length < 10) ? 'Invalid key' : undefined
|
|
191
|
-
}],
|
|
192
|
-
authorize: async (inputs) => {
|
|
193
|
-
log(`[AuthHook] authorize() called`);
|
|
194
|
-
if (!inputs?.apiKey)
|
|
195
|
-
return { type: 'failed' };
|
|
196
|
-
try {
|
|
197
|
-
const r = await fetch('https://gen.pollinations.ai/text/models', {
|
|
198
|
-
headers: { 'Authorization': `Bearer ${inputs.apiKey}` }
|
|
199
|
-
});
|
|
200
|
-
if (r.ok) {
|
|
201
|
-
log('[AuthHook] Success. Saving & Refreshing Config...');
|
|
202
|
-
saveConfig({ apiKey: inputs.apiKey });
|
|
203
|
-
// CRITICAL: Refresh config IMMEDIATELY after successful auth
|
|
204
|
-
await refreshProviderConfig();
|
|
205
|
-
return { type: 'success', key: inputs.apiKey };
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
catch (e) {
|
|
209
|
-
log(`[AuthHook] Auth error: ${e}`);
|
|
210
|
-
}
|
|
211
|
-
return { type: 'failed' };
|
|
108
|
+
else if (m.modelID && m.modelID.includes('pollimock-handler')) {
|
|
109
|
+
const prev = sessionModels.get(input.sessionID);
|
|
110
|
+
if (prev) {
|
|
111
|
+
log(`[Hook] Virtual model triggered. Reverting to ${prev} in 500ms...`);
|
|
112
|
+
setTimeout(() => {
|
|
113
|
+
ctx.client.tui.executeCommand({ body: { command: `/model ${prev}` } }).catch(console.error);
|
|
114
|
+
}, 500);
|
|
212
115
|
}
|
|
213
|
-
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
214
118
|
},
|
|
215
|
-
|
|
216
|
-
event: async ({ event }) => { },
|
|
119
|
+
tool: toolRegistry,
|
|
217
120
|
async config(config) {
|
|
218
121
|
log("[Hook] config() called");
|
|
219
|
-
//
|
|
122
|
+
// STARTUP only - No complex hot reload logic
|
|
123
|
+
// The user must restart OpenCode to refresh this list if they change keys.
|
|
220
124
|
const modelsArray = await generatePollinationsConfig();
|
|
221
125
|
const modelsObj = {};
|
|
222
126
|
for (const m of modelsArray) {
|
|
@@ -224,19 +128,24 @@ export const PollinationsPlugin = async (ctx) => {
|
|
|
224
128
|
}
|
|
225
129
|
if (!config.provider)
|
|
226
130
|
config.provider = {};
|
|
131
|
+
// Dynamic Provider Name
|
|
227
132
|
const version = require('../package.json').version;
|
|
133
|
+
// Inject Virtual Handler Model
|
|
134
|
+
modelsObj['pollimock-handler'] = {
|
|
135
|
+
id: 'pollimock-handler',
|
|
136
|
+
name: 'Command Handler (Virtual)',
|
|
137
|
+
options: { hidden: true } // Try to hide from UI if OpenCode supports it
|
|
138
|
+
};
|
|
228
139
|
config.provider['pollinations'] = {
|
|
229
|
-
id: '
|
|
140
|
+
id: 'pollinations',
|
|
230
141
|
name: `Pollinations AI (v${version})`,
|
|
231
|
-
options: {
|
|
232
|
-
baseURL: localBaseUrl,
|
|
233
|
-
apiKey: 'plugin-managed', // Key is managed by auth hook
|
|
234
|
-
},
|
|
142
|
+
options: { baseURL: localBaseUrl },
|
|
235
143
|
models: modelsObj
|
|
236
144
|
};
|
|
237
145
|
log(`[Hook] Registered ${Object.keys(modelsObj).length} models.`);
|
|
238
146
|
},
|
|
239
147
|
...toastHooks,
|
|
148
|
+
...createToolHooks(ctx.client),
|
|
240
149
|
...createStatusHooks(ctx.client),
|
|
241
150
|
...commandHooks
|
|
242
151
|
};
|
|
@@ -9,8 +9,14 @@ interface CommandResult {
|
|
|
9
9
|
response?: string;
|
|
10
10
|
error?: string;
|
|
11
11
|
}
|
|
12
|
+
export declare function setClientForCommands(client: any): void;
|
|
12
13
|
export declare function handleCommand(command: string): Promise<CommandResult>;
|
|
14
|
+
export declare function handleUsageCommand(args: string[]): Promise<CommandResult>;
|
|
15
|
+
export declare function handleModelsCommand(args: string[]): Promise<CommandResult>;
|
|
16
|
+
export declare function handlePricingCommand(): Promise<CommandResult>;
|
|
17
|
+
export declare function handleInfosCommand(): Promise<CommandResult>;
|
|
13
18
|
export declare function createCommandHooks(): {
|
|
14
19
|
'tui.command.execute': (input: any, output: any) => Promise<void>;
|
|
20
|
+
'command.execute.before': (input: any, output: any) => Promise<void>;
|
|
15
21
|
};
|
|
16
22
|
export {};
|