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.
- package/README.md +140 -87
- package/dist/index.js +33 -154
- package/dist/server/commands.d.ts +2 -0
- package/dist/server/commands.js +84 -25
- package/dist/server/config.d.ts +6 -0
- package/dist/server/config.js +4 -1
- package/dist/server/generate-config.d.ts +3 -30
- package/dist/server/generate-config.js +172 -100
- package/dist/server/index.d.ts +2 -1
- package/dist/server/index.js +124 -149
- package/dist/server/pollinations-api.d.ts +11 -0
- package/dist/server/pollinations-api.js +20 -0
- package/dist/server/proxy.js +158 -72
- package/dist/server/quota.d.ts +8 -0
- package/dist/server/quota.js +106 -61
- package/dist/server/toast.d.ts +3 -0
- package/dist/server/toast.js +16 -0
- 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/index.d.ts +22 -0
- package/dist/tools/index.js +81 -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 +204 -0
- package/dist/tools/pollinations/gen_image.d.ts +13 -0
- package/dist/tools/pollinations/gen_image.js +239 -0
- package/dist/tools/pollinations/gen_music.d.ts +14 -0
- package/dist/tools/pollinations/gen_music.js +139 -0
- package/dist/tools/pollinations/gen_video.d.ts +16 -0
- package/dist/tools/pollinations/gen_video.js +222 -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 +170 -0
- package/dist/tools/pollinations/shared.js +454 -0
- package/dist/tools/pollinations/transcribe_audio.d.ts +17 -0
- package/dist/tools/pollinations/transcribe_audio.js +235 -0
- package/dist/tools/power/extract_audio.d.ts +2 -0
- package/dist/tools/power/extract_audio.js +180 -0
- package/dist/tools/power/extract_frames.d.ts +2 -0
- package/dist/tools/power/extract_frames.js +240 -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 +365 -0
- package/dist/tools/power/rmbg_keys.d.ts +2 -0
- package/dist/tools/power/rmbg_keys.js +78 -0
- package/dist/tools/shared.d.ts +30 -0
- package/dist/tools/shared.js +74 -0
- package/package.json +9 -3
- package/dist/server/models-seed.d.ts +0 -18
- package/dist/server/models-seed.js +0 -55
package/dist/server/index.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
-
|
|
64
|
-
|
|
65
|
-
res.
|
|
56
|
+
catch (e) {
|
|
57
|
+
log(`[AUTH] Error: ${e}`);
|
|
58
|
+
res.writeHead(500);
|
|
59
|
+
res.end(JSON.stringify({ error: String(e) }));
|
|
66
60
|
}
|
|
67
|
-
}
|
|
68
|
-
|
|
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
|
-
|
|
94
|
-
|
|
95
|
-
res.writeHead(
|
|
96
|
-
res.end(JSON.stringify({
|
|
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
|
-
|
|
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
|
|
107
|
-
|
|
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
|
|
82
|
+
log(`Error fetching models: ${e}`);
|
|
111
83
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
112
|
-
res.end(JSON.stringify({ error: "
|
|
84
|
+
res.end(JSON.stringify({ error: "Failed to fetch models" }));
|
|
113
85
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
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
|
-
|
|
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
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
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
|
-
|
|
171
|
-
|
|
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
|
|
177
|
-
|
|
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([
|