opencode-pollinations-plugin 6.0.0-beta.18 → 6.0.0-beta.20

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 (2) hide show
  1. package/dist/index.js +76 -12
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -15,8 +15,16 @@ function log(msg) {
15
15
  }
16
16
  catch (e) { }
17
17
  }
18
- // === PROXY SERVER ===
18
+ // === PROXY SERVER (Singleton with Fixed Port) ===
19
+ const DEFAULT_PORT = 18888;
20
+ let existingServer = null;
21
+ let existingPort = 0;
19
22
  const startProxy = () => {
23
+ // Singleton: reuse existing server if already running
24
+ if (existingServer && existingPort > 0) {
25
+ log(`[Proxy] Reusing existing server on port ${existingPort}`);
26
+ return Promise.resolve(existingPort);
27
+ }
20
28
  return new Promise((resolve) => {
21
29
  const server = http.createServer(async (req, res) => {
22
30
  log(`[Proxy] Request: ${req.method} ${req.url}`);
@@ -60,16 +68,29 @@ const startProxy = () => {
60
68
  res.writeHead(404);
61
69
  res.end("Not Found");
62
70
  });
63
- server.listen(0, '127.0.0.1', () => {
64
- // @ts-ignore
65
- const assignedPort = server.address().port;
66
- log(`[Proxy] Started v${require('../package.json').version} (Dynamic Port) on port ${assignedPort}`);
67
- resolve(assignedPort);
68
- });
69
- server.on('error', (e) => {
70
- log(`[Proxy] Fatal Error: ${e}`);
71
- resolve(0);
72
- });
71
+ // Try fixed port first, fallback to dynamic if occupied
72
+ const tryListen = (port, fallbackToDynamic) => {
73
+ server.listen(port, '127.0.0.1', () => {
74
+ // @ts-ignore
75
+ const assignedPort = server.address().port;
76
+ existingServer = server;
77
+ existingPort = assignedPort;
78
+ log(`[Proxy] Started v${require('../package.json').version} on port ${assignedPort}${port === 0 ? ' (dynamic fallback)' : ''}`);
79
+ resolve(assignedPort);
80
+ });
81
+ server.on('error', (e) => {
82
+ if (e.code === 'EADDRINUSE' && fallbackToDynamic) {
83
+ log(`[Proxy] Port ${port} in use, falling back to dynamic port`);
84
+ server.removeAllListeners('error');
85
+ tryListen(0, false); // Try dynamic port
86
+ }
87
+ else {
88
+ log(`[Proxy] Fatal Error: ${e}`);
89
+ resolve(0);
90
+ }
91
+ });
92
+ };
93
+ tryListen(DEFAULT_PORT, true);
73
94
  });
74
95
  };
75
96
  // === AUTH HOOK: Native /connect Integration ===
@@ -150,15 +171,58 @@ const createAuthHook = () => ({
150
171
  // === PLUGIN EXPORT ===
151
172
  export const PollinationsPlugin = async (ctx) => {
152
173
  log(`Plugin Initializing v${require('../package.json').version}...`);
153
- // START PROXY
174
+ // START PROXY on fixed port
154
175
  const port = await startProxy();
155
176
  const localBaseUrl = `http://127.0.0.1:${port}/v1`;
156
177
  setGlobalClient(ctx.client);
157
178
  const toastHooks = createToastHooks(ctx.client);
158
179
  const commandHooks = createCommandHooks();
180
+ // Helper: Refresh provider config (for hot-reload after /connect)
181
+ const refreshProviderConfig = async () => {
182
+ try {
183
+ log('[Event] Refreshing provider config after auth update...');
184
+ const modelsArray = await generatePollinationsConfig();
185
+ const modelsObj = {};
186
+ for (const m of modelsArray) {
187
+ modelsObj[m.id] = m;
188
+ }
189
+ const version = require('../package.json').version;
190
+ // Use Server API to PATCH the config at runtime
191
+ await ctx.client.fetch('/config', {
192
+ method: 'PATCH',
193
+ headers: { 'Content-Type': 'application/json' },
194
+ body: JSON.stringify({
195
+ provider: {
196
+ pollinations: {
197
+ id: 'openai',
198
+ name: `Pollinations AI (v${version})`,
199
+ options: {
200
+ baseURL: localBaseUrl,
201
+ apiKey: 'plugin-managed',
202
+ },
203
+ models: modelsObj
204
+ }
205
+ }
206
+ })
207
+ });
208
+ log(`[Event] Provider config refreshed with ${Object.keys(modelsObj).length} models.`);
209
+ }
210
+ catch (e) {
211
+ log(`[Event] Failed to refresh provider config: ${e}`);
212
+ }
213
+ };
159
214
  return {
160
215
  // AUTH HOOK: Native /connect integration
161
216
  auth: createAuthHook(),
217
+ // EVENT HOOK: Listen for auth/installation updates (triggered by /connect)
218
+ event: async ({ event }) => {
219
+ log(`[Event] Received: ${event.type}`);
220
+ // React to installation.updated (fired after /connect completes)
221
+ if (event.type === 'installation.updated') {
222
+ log('[Event] installation.updated detected - triggering hot-reload');
223
+ await refreshProviderConfig();
224
+ }
225
+ },
162
226
  async config(config) {
163
227
  log("[Hook] config() called");
164
228
  // Generate models based on current auth state
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "opencode-pollinations-plugin",
3
3
  "displayName": "Pollinations AI (V5.6)",
4
- "version": "6.0.0-beta.18",
4
+ "version": "6.0.0-beta.20",
5
5
  "description": "Native Pollinations.ai Provider Plugin for OpenCode",
6
6
  "publisher": "pollinations",
7
7
  "repository": {