opencode-pollinations-plugin 6.1.0-beta.3 → 6.1.0-beta.30

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.
Files changed (98) hide show
  1. package/README.md +257 -61
  2. package/dist/index.js +53 -161
  3. package/dist/server/commands.d.ts +6 -0
  4. package/dist/server/commands.js +404 -73
  5. package/dist/server/config.d.ts +32 -23
  6. package/dist/server/config.js +183 -104
  7. package/dist/server/connect-response.d.ts +2 -0
  8. package/dist/server/connect-response.js +141 -0
  9. package/dist/server/generate-config.d.ts +3 -30
  10. package/dist/server/generate-config.js +164 -106
  11. package/dist/server/index.d.ts +2 -1
  12. package/dist/server/index.js +124 -149
  13. package/dist/server/logger.d.ts +8 -0
  14. package/dist/server/logger.js +36 -0
  15. package/dist/server/models/cache.d.ts +35 -0
  16. package/dist/server/models/cache.js +160 -0
  17. package/dist/server/models/fetcher.d.ts +18 -0
  18. package/dist/server/models/fetcher.js +150 -0
  19. package/dist/server/models/index.d.ts +6 -0
  20. package/dist/server/models/index.js +5 -0
  21. package/dist/server/models/manual.d.ts +15 -0
  22. package/dist/server/models/manual.js +92 -0
  23. package/dist/server/models/types.d.ts +55 -0
  24. package/dist/server/models/types.js +7 -0
  25. package/dist/server/models/worker.d.ts +21 -0
  26. package/dist/server/models/worker.js +97 -0
  27. package/dist/server/pollinations-api.d.ts +11 -0
  28. package/dist/server/pollinations-api.js +21 -8
  29. package/dist/server/proxy.js +195 -160
  30. package/dist/server/quota.d.ts +2 -0
  31. package/dist/server/quota.js +89 -86
  32. package/dist/server/scripts/pollinations_pricing.d.ts +8 -0
  33. package/dist/server/scripts/pollinations_pricing.js +246 -0
  34. package/dist/server/scripts/test_cost_endpoints.d.ts +1 -0
  35. package/dist/server/scripts/test_cost_endpoints.js +61 -0
  36. package/dist/server/scripts/test_dynamic_pricing.d.ts +1 -0
  37. package/dist/server/scripts/test_dynamic_pricing.js +39 -0
  38. package/dist/server/scripts/test_freetier_audit.d.ts +11 -0
  39. package/dist/server/scripts/test_freetier_audit.js +215 -0
  40. package/dist/server/scripts/test_parallel_cost.d.ts +1 -0
  41. package/dist/server/scripts/test_parallel_cost.js +104 -0
  42. package/dist/server/toast.d.ts +7 -1
  43. package/dist/server/toast.js +43 -10
  44. package/dist/tools/design/gen_diagram.d.ts +2 -0
  45. package/dist/tools/design/gen_diagram.js +94 -0
  46. package/dist/tools/design/gen_palette.d.ts +2 -0
  47. package/dist/tools/design/gen_palette.js +182 -0
  48. package/dist/tools/design/gen_qrcode.d.ts +2 -0
  49. package/dist/tools/design/gen_qrcode.js +50 -0
  50. package/dist/tools/ffmpeg.d.ts +24 -0
  51. package/dist/tools/ffmpeg.js +54 -0
  52. package/dist/tools/index.d.ts +25 -0
  53. package/dist/tools/index.js +86 -0
  54. package/dist/tools/pollinations/beta_discovery.d.ts +9 -0
  55. package/dist/tools/pollinations/beta_discovery.js +197 -0
  56. package/dist/tools/pollinations/cost-guard.d.ts +38 -0
  57. package/dist/tools/pollinations/cost-guard.js +141 -0
  58. package/dist/tools/pollinations/deepsearch.d.ts +7 -0
  59. package/dist/tools/pollinations/deepsearch.js +80 -0
  60. package/dist/tools/pollinations/gen_audio.d.ts +18 -0
  61. package/dist/tools/pollinations/gen_audio.js +246 -0
  62. package/dist/tools/pollinations/gen_image.d.ts +11 -0
  63. package/dist/tools/pollinations/gen_image.js +225 -0
  64. package/dist/tools/pollinations/gen_music.d.ts +14 -0
  65. package/dist/tools/pollinations/gen_music.js +180 -0
  66. package/dist/tools/pollinations/gen_video.d.ts +16 -0
  67. package/dist/tools/pollinations/gen_video.js +256 -0
  68. package/dist/tools/pollinations/polli_config.d.ts +2 -0
  69. package/dist/tools/pollinations/polli_config.js +88 -0
  70. package/dist/tools/pollinations/polli_gen_confirm.d.ts +2 -0
  71. package/dist/tools/pollinations/polli_gen_confirm.js +48 -0
  72. package/dist/tools/pollinations/polli_status.d.ts +2 -0
  73. package/dist/tools/pollinations/polli_status.js +31 -0
  74. package/dist/tools/pollinations/polli_web_search.d.ts +15 -0
  75. package/dist/tools/pollinations/polli_web_search.js +164 -0
  76. package/dist/tools/pollinations/search_crawl_scrape.d.ts +7 -0
  77. package/dist/tools/pollinations/search_crawl_scrape.js +85 -0
  78. package/dist/tools/pollinations/shared.d.ts +165 -0
  79. package/dist/tools/pollinations/shared.js +665 -0
  80. package/dist/tools/pollinations/test_estimators.d.ts +1 -0
  81. package/dist/tools/pollinations/test_estimators.js +22 -0
  82. package/dist/tools/pollinations/transcribe_audio.d.ts +13 -0
  83. package/dist/tools/pollinations/transcribe_audio.js +194 -0
  84. package/dist/tools/power/extract_audio.d.ts +2 -0
  85. package/dist/tools/power/extract_audio.js +179 -0
  86. package/dist/tools/power/extract_frames.d.ts +2 -0
  87. package/dist/tools/power/extract_frames.js +237 -0
  88. package/dist/tools/power/file_to_url.d.ts +2 -0
  89. package/dist/tools/power/file_to_url.js +217 -0
  90. package/dist/tools/power/remove_background.d.ts +2 -0
  91. package/dist/tools/power/remove_background.js +392 -0
  92. package/dist/tools/power/rmbg_keys.d.ts +2 -0
  93. package/dist/tools/power/rmbg_keys.js +79 -0
  94. package/dist/tools/shared.d.ts +30 -0
  95. package/dist/tools/shared.js +80 -0
  96. package/package.json +10 -4
  97. package/dist/server/models-seed.d.ts +0 -18
  98. package/dist/server/models-seed.js +0 -55
package/README.md CHANGED
@@ -1,38 +1,156 @@
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="150">
5
- <br>
6
- <b>The Bridge between OpenCode and the Pollinations.ai Ecosystem</b>
7
- <br>
8
- Access 25+ AI models with Vision, Reasoning, and Tools support.
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
+ ![Version](https://img.shields.io/badge/version-6.1.0--beta.22-blue.svg)
11
+ ![Downloads](https://img.shields.io/npm/dt/opencode-pollinations-plugin?label=downloads&color=brightgreen)
12
+ ![License](https://img.shields.io/badge/license-MIT-green.svg)
13
+ ![Status](https://img.shields.io/badge/status-Beta-orange.svg)
14
+ ![OpenCode](https://img.shields.io/badge/OpenCode-Plugin-purple.svg)
15
+
9
16
  </div>
10
17
 
11
- <div align="center">
18
+ ---
12
19
 
13
- ![Version](https://img.shields.io/badge/version-6.0.0--beta.25-blue.svg)
14
- ![License](https://img.shields.io/badge/license-MIT-green.svg)
15
- ![Status](https://img.shields.io/badge/status-Stable-brightgreen.svg)
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.
16
23
 
17
- [📜 Changelog](./CHANGELOG.md) | [🛣️ Roadmap](./ROADMAP.md)
24
+ ---
18
25
 
19
- </div>
26
+ ## ✨ What's New in v6.1-beta.22
27
+
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**:
29
+
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
37
+
38
+ ---
39
+
40
+ ## 🚀 Quick Start
41
+
42
+ ```bash
43
+ # Install globally
44
+ npm install -g opencode-pollinations-plugin
45
+ ```
46
+
47
+ Then in OpenCode:
48
+ ```
49
+ /connect
50
+ ```
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.**
54
+
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.
20
132
 
21
133
  ---
22
134
 
23
- ## Features
135
+ ## 👁️ Vision Support
24
136
 
25
- - **👁️ Vision Support**: Send images to AI models (OpenAI, Claude, Gemini, Kimi)
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
137
+ Paste images directly into the chat or use an image URL. The plugin handles encoding automatically.
138
+
139
+ | Model | Vision | Reasoning | Tools |
140
+ |-------|:------:|:---------:|:-----:|
141
+ | `openai` / `openai-large` | | | ✅ |
142
+ | `gemini` / `gemini-search` | ✅ | — | ✅ |
143
+ | `claude` / `claude-large` | ✅ | — | ✅ |
144
+ | `kimi` | ✅ | ✅ | — |
145
+ | `openai-audio` | ✅ | — | ✅ |
30
146
 
31
147
  ---
32
148
 
33
- ## 🐝 Understanding Pollen & Tiers
149
+ ## 💰 Understanding Pollen & Tiers
150
+
151
+ **Pollen** is Pollinations' unified credit system. **$1 ≈ 1 Pollen.**
34
152
 
35
- **Pollen** is Pollinations' unified credit system. $1 1 Pollen.
153
+ Every registered user receives a **daily Pollen grant** based on their tier:
36
154
 
37
155
  | Tier | Daily Grant | Requirement |
38
156
  |:-----|:------------|:------------|
@@ -42,76 +160,154 @@
42
160
  | 🌸 **Flower** | 10 Pollen | Published app |
43
161
  | 🍯 **Nectar** | 20 Pollen | Major contributor |
44
162
 
45
- > 💡 This plugin was published to the OpenCode ecosystem, granting its author **Flower** tier (10 Pollen/day).
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.
46
166
 
47
167
  ---
48
168
 
49
- ## 🚀 Quick Start
169
+ ## 📊 Commands
50
170
 
51
- ### 1. Install Plugin
52
- ```bash
53
- npm install -g opencode-pollinations-plugin
54
- npx opencode-pollinations-plugin
55
171
  ```
56
-
57
- ### 2. Connect Your Account
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
58
179
  ```
59
- /connect
60
- ```
61
- Select **pollinations** → Enter your API key from [enter.pollinations.ai](https://enter.pollinations.ai)
62
180
 
63
- ### 3. Start Using
64
- Select any `pollinations/*` model in OpenCode and chat!
181
+ Aliases: `/poll` works as a shorthand for all commands.
65
182
 
66
- ---
183
+ ### Config Keys & Agent Understanding
67
184
 
68
- ## 👁️ Vision Support
185
+ The plugin employs a **strict separation of concerns** between Chat/Conversational settings and Tool (generation/search) settings.
69
186
 
70
- The following models support image input:
187
+ #### 1. Chat Models & Fallbacks
188
+ | Key | Values | Description |
189
+ |-----|--------|-------------|
190
+ | `mode` | `manual` / `alwaysfree` / `pro` | Automatic fallback routing strategy for chat. |
191
+ | `threshold_tier` | `0-100` | Alert threshold percentage for Free Tier quota. |
192
+ | `threshold_wallet` | `0-100` | Alert threshold percentage for Premium Wallet balance. |
71
193
 
72
- | Model | Vision | Reasoning | Tools |
73
- |-------|:------:|:---------:|:-----:|
74
- | `openai` / `openai-large` | ✅ | - | ✅ |
75
- | `gemini` / `gemini-search` | | - | |
76
- | `claude` / `claude-large` | | - | |
77
- | `kimi` | | | - |
78
- | `openai-audio` | | - | |
194
+ #### 2. Tools Protection
195
+ | Key | Values | Description |
196
+ |-----|--------|-------------|
197
+ | `enablePaidTools` | `true` / `false` | Allows generation tools to consume Wallet pollen. |
198
+ | `costConfirmationRequired` | `true` / `false` | Demands validation if a tool estimate exceeds threshold. |
199
+ | `costThreshold` | numeric (e.g., `0.05`) | Trigger limit in Pollen for the confirmation lock. |
200
+ | `cost_estimator` | `true` / `false` | Shows live cost estimates in tool outputs. |
79
201
 
80
- **Usage**: Simply paste an image in the chat or use an image URL. The plugin handles encoding automatically.
202
+ #### 3. UI & Notifications
203
+ | Key | Values | Description |
204
+ |-----|--------|-------------|
205
+ | `status_gui` | `none` / `alert` / `all` | Toast notification verbosity. |
206
+ | `logs_gui` | `none` / `error` / `verbose` | Technical log verbosity. |
207
+ | `status_bar` | `true` / `false` | Status bar widget visibility. |
81
208
 
82
209
  ---
83
210
 
84
- ## 📊 Available Models (25+)
211
+ ## 🔑 API Key Types
212
+
213
+ | Type | Access |
214
+ |------|--------|
215
+ | **Standard (`pk_...`)** | Full access: models, usage dashboard, quota |
216
+ | **Limited** | Generation only. Dashboard shows a restriction alert. The plugin auto-switches to Manual mode to avoid quota errors. |
217
+ | **Legacy (`sk_...`)** | Accepted for backward compatibility |
85
218
 
86
- ### Text/Chat Models
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`
219
+ ---
93
220
 
94
- ### Paid-Only Models (💎)
95
- These require purchased credits (not daily grants):
96
- - `claude-large`, `gemini-large`, `veo`, `seedream-pro`
221
+ ## 🛠️ Routing Modes
222
+
223
+ | Mode | Behavior |
224
+ |------|----------|
225
+ | `manual` | You choose the model, no automatic switching |
226
+ | `alwaysfree` | Only free models, never charges Pollen |
227
+ | `pro` | Enterprise models with automatic free fallback when quota/wallet is low |
97
228
 
98
229
  ---
99
230
 
100
- ## 🔧 Commands
231
+ ## 🏗️ Architecture (Summary)
232
+
233
+ 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
234
 
102
235
  ```
103
- /pollinations usage # View Pollen balance
104
- /pollinations usage full # Detailed model breakdown
105
- /pollinations status # Plugin health check
236
+ OpenCode TUI
237
+ POST /v1/chat/completions
238
+
239
+ Local Proxy (Dynamic Port)
240
+ ├── Safety Net Logic (quota check, fallback)
241
+ ├── Model Sanitization (Azure/Vertex/Gemini/Kimi)
242
+ └── Route to:
243
+ ├── text.pollinations.ai (Free Universe)
244
+ └── gen.pollinations.ai (Enterprise Universe)
106
245
  ```
107
246
 
247
+ Config is read from (highest priority first):
248
+ 1. `~/.pollinations/config.json`
249
+ 2. `~/.local/share/opencode/auth.json`
250
+ 3. `~/.config/opencode/opencode.json`
251
+
252
+ ---
253
+
254
+ ## 🗺️ Roadmap
255
+
256
+ ### ✅ Shipped (v5.x → v6.1-beta)
257
+ - Free + Enterprise proxy with transparent fallback (Safety Net)
258
+ - Dynamic port allocation — cross-platform (no more `fuser`, no port conflicts)
259
+ - Pollen/tier quota tracking with Smart Fetch API (no local logging)
260
+ - Dynamic Pricing (Tinybird stats) and Cost Guard max thresholds
261
+ - Agent tools: image, audio, music, video generation
262
+ - Agent tools: web search, scraping, deep research
263
+ - Design tools: diagrams, palettes, QR codes
264
+ - Power tools: background removal with key rotation, frame/audio extraction
265
+ - Stealth notification mode (toasts only in relevant sessions)
266
+ - Limited-key support with automatic mode switching
267
+ - Gemini tools auto-fallback to OpenAI
268
+ - Enterprise schema sanitization (Azure, Vertex, Bedrock, Kimi)
269
+
270
+ ### 🔜 Next (v6.2 — v6.5, 2026)
271
+ - **Config file watcher** — hot-reload without restarting OpenCode
272
+ - **Signature map rotation** — LRU eviction to prevent unbounded memory growth
273
+ - **Unit tests** — coverage for `proxy.ts`, `quota.ts`, `tools/`
274
+ - **`/poll status` one-liner** — faster than opening the dashboard
275
+ - **Structured logging** — JSON format, configurable log levels, log rotation
276
+ - **Model search** — `/poll models <query>` to filter the model list
277
+
278
+ ### 🔭 Longer Term (v7.0+, 2027)
279
+ - **Smart Router** — cost-aware and latency-aware model selection
280
+ - **Multi-provider failover** — fallback to OpenRouter if Pollinations is unreachable
281
+ - **Web Dashboard** — browser UI for monitoring, config, and analytics
282
+ - **Team features** — shared quotas and API keys
283
+ - **Persistent memory** — vector DB integration for long-running agents
284
+
285
+ > Community ideas with the most votes: API usage alerts, conversation export (Markdown/JSON), model comparison mode. Open an issue to vote!
286
+
287
+ ---
288
+
289
+ ## 🤝 Contributing
290
+
291
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for setup instructions, code guidelines, and priority areas (testing, docs, i18n, DevOps).
292
+
293
+ ### Areas Where Help Is Needed
294
+ - 🧪 **Testing** — Unit + integration tests for the proxy and tools
295
+ - 📚 **Docs** — User guides, tool examples
296
+ - 🌍 **i18n** — French/English consistency, German and Spanish translations
297
+ - 🎨 **UX** — Command output formatting improvements
298
+
108
299
  ---
109
300
 
110
301
  ## 🔗 Links
111
302
 
112
- - **Get API Key**: [enter.pollinations.ai](https://enter.pollinations.ai)
113
- - **Discord**: [Join Community](https://discord.gg/pollinations-ai-885844321461485618)
114
- - **GitHub**: [Plugin Repository](https://github.com/fkom13/opencode-pollinations-plugin)
303
+ | Resource | Link |
304
+ |----------|------|
305
+ | Sign up for Pollinations (free tiers + paid models) | [enter.pollinations.ai](https://enter.pollinations.ai) |
306
+ | Pollinations website | [pollinations.ai](https://pollinations.ai) |
307
+ | Pollinations GitHub | [github.com/pollinations/pollinations](https://github.com/pollinations/pollinations) |
308
+ | Discord community | [Join us!](https://discord.gg/pollinations-ai-885844321461485618) |
309
+ | OpenCode ecosystem | [opencode.ai/docs/ecosystem](https://opencode.ai/docs/ecosystem#plugins) |
310
+ | Plugin author | [@fkom13](https://github.com/fkom13) |
115
311
 
116
312
  ---
117
313
 
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, saveConfig } from './server/config.js';
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
- const LOG_FILE = '/tmp/opencode_pollinations_v4.log';
12
- function log(msg) {
13
- try {
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,59 @@ const startProxy = () => {
67
57
  res.writeHead(404);
68
58
  res.end("Not Found");
69
59
  });
70
- // Try fixed port first, fallback to dynamic if occupied
71
- const tryListen = (port, fallbackToDynamic) => {
72
- server.listen(port, '127.0.0.1', () => {
73
- // @ts-ignore
74
- const assignedPort = server.address().port;
75
- global[GLOBAL_SERVER_KEY] = server;
76
- log(`[Proxy] Started v${require('../package.json').version} on port ${assignedPort}${port === 0 ? ' (dynamic fallback)' : ''}`);
77
- resolve(assignedPort);
78
- });
79
- server.on('error', (e) => {
80
- if (e.code === 'EADDRINUSE' && fallbackToDynamic) {
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
- log(`Plugin Initializing v${require('../package.json').version}...`);
99
- // START PROXY on fixed port
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
- // Helper: Refresh provider config (for hot-reload after /connect)
106
- let isRefreshing = false;
107
- const refreshProviderConfig = async () => {
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
- // AUTH HOOK: Native /connect integration (INLINED)
163
- auth: {
164
- provider: 'pollinations',
165
- loader: async (auth, provider) => {
166
- log('[AuthHook] loader() called');
167
- try {
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}`);
177
- }
178
- const config = loadConfig();
179
- if (config.apiKey)
180
- return { apiKey: config.apiKey };
181
- return {};
182
- },
183
- methods: [{
184
- type: 'api',
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' };
212
- }
213
- }]
101
+ "chat.message": async (input) => {
102
+ const m = input.model;
103
+ if (m && m.modelID) {
104
+ sessionModels.set(input.sessionID, `${m.providerID}/${m.modelID}`);
105
+ log(`[Hook] Saved active model ${m.providerID}/${m.modelID} for session ${input.sessionID}`);
106
+ }
214
107
  },
215
- // Event hook removed (logic moved to authorize)
216
- event: async ({ event }) => { },
108
+ tool: toolRegistry,
217
109
  async config(config) {
218
110
  log("[Hook] config() called");
219
- // Generate models based on current auth state
111
+ // STARTUP only - No complex hot reload logic
112
+ // The user must restart OpenCode to refresh this list if they change keys.
220
113
  const modelsArray = await generatePollinationsConfig();
221
114
  const modelsObj = {};
222
115
  for (const m of modelsArray) {
@@ -224,19 +117,18 @@ export const PollinationsPlugin = async (ctx) => {
224
117
  }
225
118
  if (!config.provider)
226
119
  config.provider = {};
120
+ // Dynamic Provider Name
227
121
  const version = require('../package.json').version;
228
122
  config.provider['pollinations'] = {
229
- id: 'openai',
123
+ id: 'pollinations',
230
124
  name: `Pollinations AI (v${version})`,
231
- options: {
232
- baseURL: localBaseUrl,
233
- apiKey: 'plugin-managed', // Key is managed by auth hook
234
- },
125
+ options: { baseURL: localBaseUrl },
235
126
  models: modelsObj
236
127
  };
237
128
  log(`[Hook] Registered ${Object.keys(modelsObj).length} models.`);
238
129
  },
239
130
  ...toastHooks,
131
+ ...createToolHooks(ctx.client),
240
132
  ...createStatusHooks(ctx.client),
241
133
  ...commandHooks
242
134
  };
@@ -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 {};