opencode-pollinations-plugin 6.0.0 → 6.1.0-beta.10

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 (56) hide show
  1. package/README.md +140 -87
  2. package/dist/index.js +33 -154
  3. package/dist/server/commands.d.ts +2 -0
  4. package/dist/server/commands.js +84 -25
  5. package/dist/server/config.d.ts +6 -0
  6. package/dist/server/config.js +4 -1
  7. package/dist/server/generate-config.d.ts +3 -30
  8. package/dist/server/generate-config.js +172 -100
  9. package/dist/server/index.d.ts +2 -1
  10. package/dist/server/index.js +124 -149
  11. package/dist/server/pollinations-api.d.ts +11 -0
  12. package/dist/server/pollinations-api.js +20 -0
  13. package/dist/server/proxy.js +158 -72
  14. package/dist/server/quota.d.ts +8 -0
  15. package/dist/server/quota.js +106 -61
  16. package/dist/server/toast.d.ts +3 -0
  17. package/dist/server/toast.js +16 -0
  18. package/dist/tools/design/gen_diagram.d.ts +2 -0
  19. package/dist/tools/design/gen_diagram.js +94 -0
  20. package/dist/tools/design/gen_palette.d.ts +2 -0
  21. package/dist/tools/design/gen_palette.js +182 -0
  22. package/dist/tools/design/gen_qrcode.d.ts +2 -0
  23. package/dist/tools/design/gen_qrcode.js +50 -0
  24. package/dist/tools/index.d.ts +22 -0
  25. package/dist/tools/index.js +81 -0
  26. package/dist/tools/pollinations/deepsearch.d.ts +7 -0
  27. package/dist/tools/pollinations/deepsearch.js +80 -0
  28. package/dist/tools/pollinations/gen_audio.d.ts +18 -0
  29. package/dist/tools/pollinations/gen_audio.js +204 -0
  30. package/dist/tools/pollinations/gen_image.d.ts +13 -0
  31. package/dist/tools/pollinations/gen_image.js +239 -0
  32. package/dist/tools/pollinations/gen_music.d.ts +14 -0
  33. package/dist/tools/pollinations/gen_music.js +139 -0
  34. package/dist/tools/pollinations/gen_video.d.ts +16 -0
  35. package/dist/tools/pollinations/gen_video.js +222 -0
  36. package/dist/tools/pollinations/search_crawl_scrape.d.ts +7 -0
  37. package/dist/tools/pollinations/search_crawl_scrape.js +85 -0
  38. package/dist/tools/pollinations/shared.d.ts +170 -0
  39. package/dist/tools/pollinations/shared.js +454 -0
  40. package/dist/tools/pollinations/transcribe_audio.d.ts +17 -0
  41. package/dist/tools/pollinations/transcribe_audio.js +235 -0
  42. package/dist/tools/power/extract_audio.d.ts +2 -0
  43. package/dist/tools/power/extract_audio.js +180 -0
  44. package/dist/tools/power/extract_frames.d.ts +2 -0
  45. package/dist/tools/power/extract_frames.js +240 -0
  46. package/dist/tools/power/file_to_url.d.ts +2 -0
  47. package/dist/tools/power/file_to_url.js +217 -0
  48. package/dist/tools/power/remove_background.d.ts +2 -0
  49. package/dist/tools/power/remove_background.js +365 -0
  50. package/dist/tools/power/rmbg_keys.d.ts +2 -0
  51. package/dist/tools/power/rmbg_keys.js +78 -0
  52. package/dist/tools/shared.d.ts +30 -0
  53. package/dist/tools/shared.js +74 -0
  54. package/package.json +9 -3
  55. package/dist/server/models-seed.d.ts +0 -18
  56. package/dist/server/models-seed.js +0 -55
@@ -4,7 +4,9 @@ import * as path from 'path';
4
4
  import { getAggregatedModels } from './pollinations-api.js';
5
5
  import { loadConfig, saveConfig } from './config.js';
6
6
  import { handleChatCompletion } from './proxy.js';
7
- const LOG_FILE = path.join(process.env.HOME || '/tmp', '.config/opencode/plugins/pollinations-v3.log');
7
+ import { createCommandHooks, setClientForCommands, checkKeyPermissions } from './commands.js';
8
+ import { fileURLToPath } from 'url';
9
+ const LOG_FILE = path.join(process.env.HOME || '/tmp', '.config/opencode/plugins/pollinations-v6.log');
8
10
  // Simple file logger
9
11
  function log(msg) {
10
12
  const ts = new Date().toISOString();
@@ -14,170 +16,143 @@ function log(msg) {
14
16
  }
15
17
  fs.appendFileSync(LOG_FILE, `[${ts}] ${msg}\n`);
16
18
  }
17
- catch (e) {
18
- // silent fail
19
- }
20
- }
21
- // CRASH GUARD
22
- const CRASH_LOG = '/tmp/opencode_pollinations_crash.log';
23
- process.on('uncaughtException', (err) => {
24
- try {
25
- const msg = `[CRASH] Uncaught Exception: ${err.message}\n${err.stack}\n`;
26
- fs.appendFileSync(CRASH_LOG, msg);
27
- console.error(msg);
28
- }
29
19
  catch (e) { }
30
- process.exit(1);
31
- });
32
- process.on('unhandledRejection', (reason, promise) => {
33
- try {
34
- const msg = `[CRASH] Unhandled Rejection: ${reason}\n`;
35
- fs.appendFileSync(CRASH_LOG, msg);
36
- }
37
- catch (e) { }
38
- });
39
- const server = http.createServer(async (req, res) => {
40
- log(`${req.method} ${req.url}`);
41
- // CORS Headers
42
- res.setHeader('Access-Control-Allow-Origin', '*');
43
- res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
44
- res.setHeader('Access-Control-Allow-Headers', '*');
45
- if (req.method === 'OPTIONS') {
46
- res.writeHead(204);
47
- res.end();
20
+ }
21
+ const PORT = parseInt(process.env.POLLINATIONS_PORT || '10001', 10);
22
+ let serverInstance = null;
23
+ // --- SERVER LOGIC ---
24
+ function startServer() {
25
+ if (serverInstance)
48
26
  return;
49
- }
50
- // AUTH ENDPOINT (Kept for compatibility, though Native Auth is preferred)
51
- if (req.method === 'POST' && req.url === '/v1/auth') {
52
- const chunks = [];
53
- req.on('data', chunk => chunks.push(chunk));
54
- req.on('end', async () => {
55
- try {
56
- const body = JSON.parse(Buffer.concat(chunks).toString());
57
- if (body && body.apiKey) {
58
- saveConfig({ apiKey: body.apiKey, mode: 'pro' });
59
- log(`[AUTH] Key saved via Server Endpoint`);
60
- res.writeHead(200, { 'Content-Type': 'application/json' });
61
- res.end(JSON.stringify({ status: "ok" }));
27
+ serverInstance = http.createServer(async (req, res) => {
28
+ log(`${req.method} ${req.url}`);
29
+ // CORS Headers
30
+ res.setHeader('Access-Control-Allow-Origin', '*');
31
+ res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
32
+ res.setHeader('Access-Control-Allow-Headers', '*');
33
+ if (req.method === 'OPTIONS') {
34
+ res.writeHead(204);
35
+ res.end();
36
+ return;
37
+ }
38
+ // AUTH ENDPOINT
39
+ if (req.method === 'POST' && req.url === '/v1/auth') {
40
+ const chunks = [];
41
+ req.on('data', chunk => chunks.push(chunk));
42
+ req.on('end', async () => {
43
+ try {
44
+ const body = JSON.parse(Buffer.concat(chunks).toString());
45
+ if (body && body.apiKey) {
46
+ saveConfig({ apiKey: body.apiKey, mode: 'pro' });
47
+ log(`[AUTH] Key saved via Server Endpoint`);
48
+ res.writeHead(200, { 'Content-Type': 'application/json' });
49
+ res.end(JSON.stringify({ status: "ok" }));
50
+ }
51
+ else {
52
+ res.writeHead(400);
53
+ res.end(JSON.stringify({ error: "Missing apiKey" }));
54
+ }
62
55
  }
63
- else {
64
- res.writeHead(400);
65
- res.end(JSON.stringify({ error: "Missing apiKey" }));
56
+ catch (e) {
57
+ log(`[AUTH] Error: ${e}`);
58
+ res.writeHead(500);
59
+ res.end(JSON.stringify({ error: String(e) }));
66
60
  }
67
- }
68
- catch (e) {
69
- log(`[AUTH] Error: ${e}`);
70
- res.writeHead(500);
71
- res.end(JSON.stringify({ error: String(e) }));
72
- }
73
- });
74
- return;
75
- }
76
- if (req.method === 'GET' && req.url === '/health') {
77
- const config = loadConfig();
78
- res.writeHead(200, { 'Content-Type': 'application/json' });
79
- res.end(JSON.stringify({
80
- status: "ok",
81
- version: "v3.0.0-phase3",
82
- mode: config.mode,
83
- hasKey: !!config.apiKey
84
- }));
85
- return;
86
- }
87
- if (req.method === 'GET' && req.url === '/v1/models') {
88
- try {
89
- const models = await getAggregatedModels();
90
- res.writeHead(200, { 'Content-Type': 'application/json' });
91
- res.end(JSON.stringify(models));
61
+ });
62
+ return;
92
63
  }
93
- catch (e) {
94
- log(`Error fetching models: ${e}`);
95
- res.writeHead(500, { 'Content-Type': 'application/json' });
96
- res.end(JSON.stringify({ error: "Failed to fetch models" }));
64
+ if (req.method === 'GET' && req.url === '/health') {
65
+ const config = loadConfig();
66
+ res.writeHead(200, { 'Content-Type': 'application/json' });
67
+ res.end(JSON.stringify({
68
+ status: "ok",
69
+ version: "v6.0.0-beta.99",
70
+ mode: config.mode,
71
+ hasKey: !!config.apiKey
72
+ }));
73
+ return;
97
74
  }
98
- return;
99
- }
100
- if (req.method === 'POST' && req.url === '/v1/chat/completions') {
101
- // Accumulate body for the proxy
102
- const chunks = [];
103
- req.on('data', chunk => chunks.push(chunk));
104
- req.on('end', async () => {
75
+ if (req.method === 'GET' && req.url === '/v1/models') {
105
76
  try {
106
- const bodyRaw = Buffer.concat(chunks).toString();
107
- await handleChatCompletion(req, res, bodyRaw);
77
+ const models = await getAggregatedModels();
78
+ res.writeHead(200, { 'Content-Type': 'application/json' });
79
+ res.end(JSON.stringify(models));
108
80
  }
109
81
  catch (e) {
110
- log(`Error in chat handler: ${e}`);
82
+ log(`Error fetching models: ${e}`);
111
83
  res.writeHead(500, { 'Content-Type': 'application/json' });
112
- res.end(JSON.stringify({ error: "Internal Server Error in Chat Handler" }));
84
+ res.end(JSON.stringify({ error: "Failed to fetch models" }));
113
85
  }
114
- });
115
- return;
116
- }
117
- res.writeHead(404);
118
- res.end("Not Found");
119
- });
120
- const PORT = parseInt(process.env.POLLINATIONS_PORT || '10001', 10);
121
- // ANTI-ZOMBIE (Visible Logs Restored)
122
- try {
123
- const { execSync } = require('child_process');
124
- try {
125
- console.log(`[POLLINATIONS] Checking port ${PORT}...`);
126
- execSync(`fuser -k ${PORT}/tcp || true`);
127
- console.log(`[POLLINATIONS] Port ${PORT} cleared.`);
128
- }
129
- catch (e) {
130
- console.log(`[POLLINATIONS] Port check skipped (cmd missing?)`);
131
- }
132
- }
133
- catch (e) { }
134
- // LIFECYCLE DEBUG (Sync Write)
135
- const LIFE_LOG = '/tmp/POLLI_LIFECYCLE.log';
136
- const LOC_LOG = '/tmp/POLLI_LOCATION.log'; // NEW: Track source location
137
- try {
138
- fs.appendFileSync(LIFE_LOG, `[${new Date().toISOString()}] [STARTUP] PID:${process.pid} Initializing...\n`);
139
- fs.writeFileSync(LOC_LOG, `[${new Date().toISOString()}] RUNNING FROM: ${__filename}\n`);
140
- }
141
- catch (e) { }
142
- process.on('exit', (code) => {
86
+ return;
87
+ }
88
+ if (req.method === 'POST' && req.url === '/v1/chat/completions') {
89
+ const chunks = [];
90
+ req.on('data', chunk => chunks.push(chunk));
91
+ req.on('end', async () => {
92
+ try {
93
+ const bodyRaw = Buffer.concat(chunks).toString();
94
+ await handleChatCompletion(req, res, bodyRaw);
95
+ }
96
+ catch (e) {
97
+ log(`Error in chat handler: ${e}`);
98
+ res.writeHead(500, { 'Content-Type': 'application/json' });
99
+ res.end(JSON.stringify({ error: "Internal Server Error in Chat Handler" }));
100
+ }
101
+ });
102
+ return;
103
+ }
104
+ res.writeHead(404);
105
+ res.end("Not Found");
106
+ });
107
+ // ANTI-ZOMBIE
143
108
  try {
144
- fs.appendFileSync(LIFE_LOG, `[${new Date().toISOString()}] [EXIT] PID:${process.pid} Exiting with code ${code}\n`);
109
+ const { execSync } = require('child_process');
110
+ try {
111
+ console.log(`[POLLINATIONS] Checking port ${PORT}...`);
112
+ execSync(`fuser -k ${PORT}/tcp || true`);
113
+ console.log(`[POLLINATIONS] Port ${PORT} cleared.`);
114
+ }
115
+ catch (e) { }
145
116
  }
146
117
  catch (e) { }
147
- });
148
- // STARTUP CHECK: Re-validate Key (in case of upgrade/config drift)
149
- import { checkKeyPermissions } from './commands.js';
150
- (async () => {
151
- const config = loadConfig();
152
- if (config.apiKey) {
153
- try {
154
- console.log('Pollinations Plugin: Verifying API Key on startup...');
155
- const check = await checkKeyPermissions(config.apiKey);
156
- if (!check.ok) {
157
- console.warn(`Pollinations Plugin: Limited Key Detected on Startup (${check.reason}). Enforcing Manual Mode.`);
158
- saveConfig({
159
- apiKey: config.apiKey,
160
- mode: 'manual',
161
- keyHasAccessToProfile: false
162
- });
163
- }
164
- else {
165
- if (config.keyHasAccessToProfile === false) {
166
- saveConfig({ apiKey: config.apiKey, keyHasAccessToProfile: true });
118
+ // STARTUP CHECK
119
+ (async () => {
120
+ const config = loadConfig();
121
+ if (config.apiKey) {
122
+ try {
123
+ console.log('Pollinations Plugin: Verifying API Key on startup...');
124
+ const check = await checkKeyPermissions(config.apiKey);
125
+ if (!check.ok) {
126
+ console.warn(`Pollinations Plugin: Limited Key Detected on Startup (${check.reason}). Enforcing Manual Mode.`);
127
+ saveConfig({ apiKey: config.apiKey, mode: 'manual', keyHasAccessToProfile: false });
128
+ }
129
+ else {
130
+ if (config.keyHasAccessToProfile === false)
131
+ saveConfig({ apiKey: config.apiKey, keyHasAccessToProfile: true });
167
132
  }
168
133
  }
134
+ catch (e) {
135
+ console.error('Pollinations Plugin: Startup Check Failed:', e);
136
+ }
169
137
  }
170
- catch (e) {
171
- console.error('Pollinations Plugin: Startup Check Failed:', e);
172
- }
173
- }
174
- server.listen(PORT, '127.0.0.1', () => {
138
+ })();
139
+ serverInstance.listen(PORT, '127.0.0.1', () => {
175
140
  const url = `http://127.0.0.1:${PORT}`;
176
- log(`[SERVER] Started V3 Phase 3 (Auth Enabled) on port ${PORT}`);
177
- try {
178
- fs.appendFileSync(LIFE_LOG, `[${new Date().toISOString()}] [LISTEN] PID:${process.pid} Listening on ${PORT}\n`);
179
- }
180
- catch (e) { }
181
- console.log(`POLLINATIONS_V3_URL=${url}`);
141
+ log(`[SERVER] Started V6 (Plugin Mode) on port ${PORT}`);
142
+ console.log(`POLLINATIONS_V6_URL=${url}`);
182
143
  });
183
- })();
144
+ }
145
+ // --- OPENCODE PLUGIN EXPORT ---
146
+ export const plugin = async ({ client }) => {
147
+ // 1. Inject Client for Command Handling
148
+ setClientForCommands(client);
149
+ // 2. Start Local Proxy Server
150
+ startServer();
151
+ // 3. Register Hooks (Commands, TUI, etc.)
152
+ return createCommandHooks();
153
+ };
154
+ // --- STANDALONE SUPPORT ---
155
+ // If run directly via `node dist/index.js`, start server immediately
156
+ if (process.argv[1] === fileURLToPath(import.meta.url)) {
157
+ startServer();
158
+ }
@@ -41,6 +41,17 @@ export interface DetailedUsageResponse {
41
41
  count: number;
42
42
  }
43
43
  export declare function getDetailedUsage(apiKey: string): Promise<DetailedUsageResponse | null>;
44
+ export interface DailyUsageEntry {
45
+ date: string;
46
+ model: string;
47
+ meter_source: 'tier' | 'pack' | 'combined';
48
+ requests: number;
49
+ cost_usd: number;
50
+ }
51
+ export interface DailyUsageResponse {
52
+ usage: DailyUsageEntry[];
53
+ }
54
+ export declare function getDailyUsage(apiKey: string): Promise<DailyUsageResponse | null>;
44
55
  export declare function getAggregatedModels(): Promise<{
45
56
  object: string;
46
57
  data: OpenAIModel[];
@@ -136,6 +136,26 @@ export async function getDetailedUsage(apiKey) {
136
136
  return null;
137
137
  }
138
138
  }
139
+ export async function getDailyUsage(apiKey) {
140
+ if (!apiKey || apiKey.length < 10)
141
+ return null;
142
+ try {
143
+ logDebug("Fetching Daily Usage (aggregated)...");
144
+ const response = await fetch('https://gen.pollinations.ai/account/usage/daily', {
145
+ headers: { 'Authorization': `Bearer ${apiKey}` }
146
+ });
147
+ if (!response.ok) {
148
+ logDebug(`Daily Usage API Error: ${response.status}`);
149
+ return null;
150
+ }
151
+ const data = await response.json();
152
+ return data;
153
+ }
154
+ catch (e) {
155
+ logDebug(`Error Daily Usage: ${e}`);
156
+ return null;
157
+ }
158
+ }
139
159
  export async function getAggregatedModels() {
140
160
  const config = loadConfig();
141
161
  const [free, enter] = await Promise.all([