opencode-pollinations-plugin 6.1.0-beta.12 → 6.1.0-beta.22
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 +11 -6
- package/dist/index.js +40 -10
- package/dist/server/commands.d.ts +4 -0
- package/dist/server/commands.js +296 -12
- package/dist/server/config.d.ts +5 -0
- package/dist/server/config.js +163 -35
- package/dist/server/connect-response.d.ts +2 -0
- package/dist/server/connect-response.js +141 -0
- package/dist/server/generate-config.js +10 -24
- 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.js +1 -8
- package/dist/server/proxy.js +52 -27
- package/dist/server/quota.d.ts +2 -8
- package/dist/server/quota.js +47 -89
- 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 +4 -1
- package/dist/server/toast.js +27 -10
- package/dist/tools/ffmpeg.d.ts +24 -0
- package/dist/tools/ffmpeg.js +54 -0
- package/dist/tools/index.d.ts +10 -8
- package/dist/tools/index.js +27 -25
- 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/gen_audio.d.ts +1 -1
- package/dist/tools/pollinations/gen_audio.js +65 -23
- package/dist/tools/pollinations/gen_image.d.ts +5 -7
- package/dist/tools/pollinations/gen_image.js +146 -160
- package/dist/tools/pollinations/gen_music.d.ts +1 -1
- package/dist/tools/pollinations/gen_music.js +57 -16
- package/dist/tools/pollinations/gen_video.d.ts +1 -1
- package/dist/tools/pollinations/gen_video.js +99 -65
- 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/shared.d.ts +34 -39
- package/dist/tools/pollinations/shared.js +300 -89
- 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 +5 -9
- package/dist/tools/pollinations/transcribe_audio.js +31 -72
- package/dist/tools/power/extract_audio.js +26 -27
- package/dist/tools/power/extract_frames.js +24 -27
- package/dist/tools/power/remove_background.js +2 -1
- package/dist/tools/power/rmbg_keys.js +2 -1
- package/dist/tools/shared.js +9 -3
- package/package.json +2 -2
|
@@ -3,40 +3,36 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Updated: 2026-02-12 - Verified API Reference
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* - Can handle both audio input and output
|
|
6
|
+
* 1. whisper-large-v3 (DEFAULT): High accuracy Whisper model
|
|
7
|
+
* 2. whisper-1: Standard Whisper model
|
|
8
|
+
* 3. scribe: ElevenLabs Scribe v2
|
|
10
9
|
*
|
|
11
|
-
*
|
|
12
|
-
* - POST ONLY with multipart/form-data
|
|
13
|
-
* - Specialized for transcription
|
|
14
|
-
* - Higher accuracy for long audio
|
|
10
|
+
* All models use /v1/audio/transcriptions (POST multipart)
|
|
15
11
|
*/
|
|
16
12
|
import { tool } from '@opencode-ai/plugin/tool';
|
|
17
13
|
import * as fs from 'fs';
|
|
18
14
|
import * as path from 'path';
|
|
19
|
-
import { getApiKey,
|
|
15
|
+
import { getApiKey, httpsPostMultipart, ensureDir, formatFileSize, getAudioModels, } from './shared.js';
|
|
20
16
|
// ─── Constants ─────────────────────────────────────────────────────────────
|
|
21
|
-
const DEFAULT_MODEL = '
|
|
22
|
-
const SUPPORTED_FORMATS = ['mp3', 'wav', 'm4a', 'webm', 'mp4', 'mpeg', 'mpga', '
|
|
17
|
+
const DEFAULT_MODEL = 'whisper-large-v3';
|
|
18
|
+
const SUPPORTED_FORMATS = ['mp3', 'wav', 'm4a', 'webm', 'mp4', 'mpeg', 'mpga', 'ogg'];
|
|
23
19
|
// ─── Tool Definition ──────────────────────────────────────────────────────
|
|
24
|
-
export const
|
|
20
|
+
export const polliSttTool = tool({
|
|
25
21
|
description: `Transcribe audio to text using Pollinations AI.
|
|
26
22
|
|
|
27
23
|
**🎙️ Models:**
|
|
28
24
|
|
|
29
|
-
| Model |
|
|
30
|
-
|
|
31
|
-
|
|
|
32
|
-
| whisper |
|
|
25
|
+
| Model | Supplier | Notes |
|
|
26
|
+
|-------|----------|-------|
|
|
27
|
+
| whisper-large-v3 | OpenAI | **DEFAULT** - High accuracy, long audio |
|
|
28
|
+
| whisper-1 | OpenAI | Standard accuracy |
|
|
29
|
+
| scribe | ElevenLabs | Scribe v2 model |
|
|
33
30
|
|
|
34
31
|
**📁 Supported Formats:**
|
|
35
|
-
mp3, wav, m4a, webm, mp4, mpeg, mpga,
|
|
32
|
+
mp3, wav, m4a, webm, mp4, mpeg, mpga, ogg
|
|
36
33
|
|
|
37
34
|
**💡 Tips:**
|
|
38
|
-
- Use \`
|
|
39
|
-
- Use \`whisper\` for highest accuracy on long recordings
|
|
35
|
+
- Use \`whisper-large-v3\` for the highest accuracy on long recordings
|
|
40
36
|
- Supports both local files and URLs
|
|
41
37
|
|
|
42
38
|
**📋 Output:**
|
|
@@ -57,10 +53,11 @@ mp3, wav, m4a, webm, mp4, mpeg, mpga, oga, ogg
|
|
|
57
53
|
}
|
|
58
54
|
const model = args.model || DEFAULT_MODEL;
|
|
59
55
|
// Validate model
|
|
60
|
-
const
|
|
56
|
+
const audioModels = getAudioModels();
|
|
57
|
+
const modelInfo = audioModels[model];
|
|
61
58
|
if (!modelInfo || (modelInfo.type !== 'stt' && modelInfo.type !== 'both')) {
|
|
62
59
|
return `❌ Modèle STT inconnu: ${model}
|
|
63
|
-
💡 Modèles STT disponibles: ${Object.entries(
|
|
60
|
+
💡 Modèles STT disponibles: ${Object.entries(audioModels)
|
|
64
61
|
.filter(([, info]) => info.type === 'stt' || info.type === 'both')
|
|
65
62
|
.map(([name]) => name)
|
|
66
63
|
.join(', ')}`;
|
|
@@ -130,58 +127,20 @@ mp3, wav, m4a, webm, mp4, mpeg, mpga, oga, ogg
|
|
|
130
127
|
try {
|
|
131
128
|
let transcript = '';
|
|
132
129
|
let detectedLanguage = '';
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
const response = await httpsPost('https://gen.pollinations.ai/v1/chat/completions', {
|
|
141
|
-
model: 'openai-audio',
|
|
142
|
-
modalities: ['text', 'audio'],
|
|
143
|
-
messages: [
|
|
144
|
-
{
|
|
145
|
-
role: 'user',
|
|
146
|
-
content: [
|
|
147
|
-
{
|
|
148
|
-
type: 'text',
|
|
149
|
-
text: args.language
|
|
150
|
-
? `Transcribe this audio to text. Language: ${args.language}`
|
|
151
|
-
: 'Transcribe this audio to text.'
|
|
152
|
-
},
|
|
153
|
-
{
|
|
154
|
-
type: 'input_audio',
|
|
155
|
-
input_audio: {
|
|
156
|
-
data: base64Audio,
|
|
157
|
-
format: fileName.endsWith('.mp3') ? 'mp3' : 'wav'
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
]
|
|
161
|
-
}
|
|
162
|
-
]
|
|
163
|
-
}, {
|
|
164
|
-
'Authorization': `Bearer ${apiKey}`,
|
|
165
|
-
});
|
|
166
|
-
const data = JSON.parse(response.data.toString());
|
|
167
|
-
transcript = data.choices?.[0]?.message?.content || '';
|
|
168
|
-
}
|
|
169
|
-
else if (model === 'whisper') {
|
|
170
|
-
// === Whisper: Use multipart endpoint ===
|
|
171
|
-
const fields = {
|
|
172
|
-
file: audioBuffer,
|
|
173
|
-
model: 'whisper',
|
|
174
|
-
};
|
|
175
|
-
if (args.language) {
|
|
176
|
-
fields.language = args.language;
|
|
177
|
-
}
|
|
178
|
-
const response = await httpsPostMultipart('https://gen.pollinations.ai/v1/audio/transcriptions', fields, {
|
|
179
|
-
'Authorization': `Bearer ${apiKey}`,
|
|
180
|
-
});
|
|
181
|
-
const data = JSON.parse(response.data.toString());
|
|
182
|
-
transcript = data.text || '';
|
|
183
|
-
detectedLanguage = data.language || '';
|
|
130
|
+
// === All STT models use multipart endpoint ===
|
|
131
|
+
const fields = {
|
|
132
|
+
file: audioBuffer,
|
|
133
|
+
model: model,
|
|
134
|
+
};
|
|
135
|
+
if (args.language) {
|
|
136
|
+
fields.language = args.language;
|
|
184
137
|
}
|
|
138
|
+
const response = await httpsPostMultipart('https://gen.pollinations.ai/v1/audio/transcriptions', fields, {
|
|
139
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
140
|
+
});
|
|
141
|
+
const data = JSON.parse(response.data.toString());
|
|
142
|
+
transcript = data.text || '';
|
|
143
|
+
detectedLanguage = data.language || '';
|
|
185
144
|
if (!transcript) {
|
|
186
145
|
return `❌ Aucune transcription générée.
|
|
187
146
|
💡 Vérifiez que l'audio contient de la parole claire.`;
|
|
@@ -5,6 +5,7 @@ import * as os from 'os';
|
|
|
5
5
|
import * as https from 'https';
|
|
6
6
|
import * as http from 'http';
|
|
7
7
|
import { resolveOutputDir, formatFileSize, safeName, formatTimestamp, TOOL_DIRS } from '../shared.js';
|
|
8
|
+
import { hasSystemFFmpeg, getFFmpegInstallInstructions, runFFmpeg, runFFprobe } from '../ffmpeg.js';
|
|
8
9
|
// ─── Download helper ────────────────────────────────────────────────────────
|
|
9
10
|
function downloadFile(url) {
|
|
10
11
|
return new Promise((resolve, reject) => {
|
|
@@ -27,16 +28,6 @@ function downloadFile(url) {
|
|
|
27
28
|
req.setTimeout(120000, () => { req.destroy(); reject(new Error('Timeout (120s)')); });
|
|
28
29
|
});
|
|
29
30
|
}
|
|
30
|
-
// ─── FFmpeg check ───────────────────────────────────────────────────────────
|
|
31
|
-
function hasSystemFFmpeg() {
|
|
32
|
-
try {
|
|
33
|
-
require('child_process').execSync('ffmpeg -version', { stdio: 'ignore' });
|
|
34
|
-
return true;
|
|
35
|
-
}
|
|
36
|
-
catch {
|
|
37
|
-
return false;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
31
|
// ─── Tool Definition ────────────────────────────────────────────────────────
|
|
41
32
|
export const extractAudioTool = tool({
|
|
42
33
|
description: `Extract the audio track from a video file or URL.
|
|
@@ -63,9 +54,7 @@ Free to use — no API key needed.`,
|
|
|
63
54
|
`❌ FFmpeg non trouvé!`,
|
|
64
55
|
``,
|
|
65
56
|
`Cet outil nécessite ffmpeg :`,
|
|
66
|
-
` •
|
|
67
|
-
` • macOS: brew install ffmpeg`,
|
|
68
|
-
` • Windows: choco install ffmpeg`,
|
|
57
|
+
getFFmpegInstallInstructions().split('\n').map(l => ` • ${l}`).join('\n')
|
|
69
58
|
].join('\n');
|
|
70
59
|
}
|
|
71
60
|
// Resolve source
|
|
@@ -89,8 +78,14 @@ Free to use — no API key needed.`,
|
|
|
89
78
|
}
|
|
90
79
|
// Check if video has audio
|
|
91
80
|
try {
|
|
92
|
-
|
|
93
|
-
const probe =
|
|
81
|
+
// Using runFFprobe helper
|
|
82
|
+
const probe = runFFprobe([
|
|
83
|
+
'-v', 'quiet',
|
|
84
|
+
'-select_streams', 'a',
|
|
85
|
+
'-show_entries', 'stream=codec_type',
|
|
86
|
+
'-of', 'csv=p=0',
|
|
87
|
+
videoPath
|
|
88
|
+
], { timeout: 10000 }).trim();
|
|
94
89
|
if (!probe) {
|
|
95
90
|
if (isRemote)
|
|
96
91
|
try {
|
|
@@ -109,31 +104,30 @@ Free to use — no API key needed.`,
|
|
|
109
104
|
const outputFile = path.join(outputDir, `${baseName}.${outputFormat}`);
|
|
110
105
|
try {
|
|
111
106
|
context.metadata({ title: `🎵 Extraction audio...` });
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
let cmd = `ffmpeg -y -i "${videoPath}" -vn`;
|
|
107
|
+
// Build ffmpeg args
|
|
108
|
+
const ffmpegArgs = ['-y', '-i', videoPath, '-vn'];
|
|
115
109
|
// Time range
|
|
116
110
|
if (args.start)
|
|
117
|
-
|
|
111
|
+
ffmpegArgs.push('-ss', args.start);
|
|
118
112
|
if (args.end)
|
|
119
|
-
|
|
113
|
+
ffmpegArgs.push('-to', args.end);
|
|
120
114
|
// Format-specific encoding
|
|
121
115
|
switch (outputFormat) {
|
|
122
116
|
case 'mp3':
|
|
123
|
-
|
|
117
|
+
ffmpegArgs.push('-acodec', 'libmp3lame', '-q:a', '2');
|
|
124
118
|
break;
|
|
125
119
|
case 'wav':
|
|
126
|
-
|
|
120
|
+
ffmpegArgs.push('-acodec', 'pcm_s16le');
|
|
127
121
|
break;
|
|
128
122
|
case 'aac':
|
|
129
|
-
|
|
123
|
+
ffmpegArgs.push('-acodec', 'aac', '-b:a', '192k');
|
|
130
124
|
break;
|
|
131
125
|
case 'flac':
|
|
132
|
-
|
|
126
|
+
ffmpegArgs.push('-acodec', 'flac');
|
|
133
127
|
break;
|
|
134
128
|
}
|
|
135
|
-
|
|
136
|
-
|
|
129
|
+
ffmpegArgs.push(outputFile);
|
|
130
|
+
runFFmpeg(ffmpegArgs, { timeout: 120000 });
|
|
137
131
|
// Cleanup
|
|
138
132
|
if (isRemote && fs.existsSync(videoPath)) {
|
|
139
133
|
try {
|
|
@@ -148,7 +142,12 @@ Free to use — no API key needed.`,
|
|
|
148
142
|
// Get audio duration
|
|
149
143
|
let durationStr = 'N/A';
|
|
150
144
|
try {
|
|
151
|
-
const durRaw =
|
|
145
|
+
const durRaw = runFFprobe([
|
|
146
|
+
'-v', 'quiet',
|
|
147
|
+
'-show_entries', 'format=duration',
|
|
148
|
+
'-of', 'csv=p=0',
|
|
149
|
+
outputFile
|
|
150
|
+
], { timeout: 5000 }).trim();
|
|
152
151
|
const dur = parseFloat(durRaw);
|
|
153
152
|
if (!isNaN(dur))
|
|
154
153
|
durationStr = formatTimestamp(dur);
|
|
@@ -5,12 +5,17 @@ import * as os from 'os';
|
|
|
5
5
|
import * as https from 'https';
|
|
6
6
|
import * as http from 'http';
|
|
7
7
|
import { resolveOutputDir, formatFileSize, safeName, formatTimestamp, TOOL_DIRS } from '../shared.js';
|
|
8
|
+
import { hasSystemFFmpeg, getFFmpegInstallInstructions, runFFmpeg, runFFprobe } from '../ffmpeg.js';
|
|
8
9
|
function extractMetadata(videoPath) {
|
|
9
10
|
try {
|
|
10
|
-
const { execSync } = require('child_process');
|
|
11
11
|
// Use ffprobe JSON output for reliable parsing
|
|
12
|
-
const
|
|
13
|
-
|
|
12
|
+
const raw = runFFprobe([
|
|
13
|
+
'-v', 'quiet',
|
|
14
|
+
'-print_format', 'json',
|
|
15
|
+
'-show_format',
|
|
16
|
+
'-show_streams',
|
|
17
|
+
videoPath
|
|
18
|
+
]);
|
|
14
19
|
const data = JSON.parse(raw);
|
|
15
20
|
const videoStream = data.streams?.find((s) => s.codec_type === 'video');
|
|
16
21
|
const audioStream = data.streams?.find((s) => s.codec_type === 'audio');
|
|
@@ -82,38 +87,32 @@ function downloadVideo(url) {
|
|
|
82
87
|
req.setTimeout(120000, () => { req.destroy(); reject(new Error('Timeout téléchargement (120s)')); });
|
|
83
88
|
});
|
|
84
89
|
}
|
|
85
|
-
// ─── FFmpeg availability ────────────────────────────────────────────────────
|
|
86
|
-
function hasSystemFFmpeg() {
|
|
87
|
-
try {
|
|
88
|
-
const { execSync } = require('child_process');
|
|
89
|
-
execSync('ffmpeg -version', { stdio: 'ignore' });
|
|
90
|
-
return true;
|
|
91
|
-
}
|
|
92
|
-
catch {
|
|
93
|
-
return false;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
90
|
// ─── Frame extraction ───────────────────────────────────────────────────────
|
|
97
91
|
function extractWithSystemFFmpeg(videoPath, outputDir, baseName, options) {
|
|
98
|
-
const { execSync } = require('child_process');
|
|
99
92
|
const outputs = [];
|
|
100
|
-
let cmd = `ffmpeg -y -i "${videoPath}"`;
|
|
101
93
|
if (options.at_time) {
|
|
94
|
+
// Single Frame Extraction
|
|
102
95
|
const singleOutput = path.join(outputDir, `${baseName}_at_${options.at_time.replace(/:/g, '-')}.png`);
|
|
103
|
-
|
|
104
|
-
|
|
96
|
+
runFFmpeg([
|
|
97
|
+
'-y', '-i', videoPath,
|
|
98
|
+
'-ss', options.at_time,
|
|
99
|
+
'-frames:v', '1',
|
|
100
|
+
singleOutput
|
|
101
|
+
], { timeout: 60000 });
|
|
105
102
|
if (fs.existsSync(singleOutput))
|
|
106
103
|
outputs.push(singleOutput);
|
|
107
104
|
}
|
|
108
105
|
else {
|
|
109
|
-
|
|
110
|
-
cmd += ` -ss ${options.start}`;
|
|
111
|
-
if (options.end)
|
|
112
|
-
cmd += ` -to ${options.end}`;
|
|
106
|
+
// Range Extraction
|
|
113
107
|
const fps = options.fps || 1;
|
|
114
108
|
const outputPattern = path.join(outputDir, `${baseName}_%03d.png`);
|
|
115
|
-
|
|
116
|
-
|
|
109
|
+
const args = ['-y', '-i', videoPath];
|
|
110
|
+
if (options.start)
|
|
111
|
+
args.push('-ss', options.start);
|
|
112
|
+
if (options.end)
|
|
113
|
+
args.push('-to', options.end);
|
|
114
|
+
args.push('-vf', `fps=${fps}`, outputPattern);
|
|
115
|
+
runFFmpeg(args, { timeout: 120000 });
|
|
117
116
|
fs.readdirSync(outputDir)
|
|
118
117
|
.filter(f => f.startsWith(baseName) && f.endsWith('.png'))
|
|
119
118
|
.sort()
|
|
@@ -146,9 +145,7 @@ Free to use — no API key needed.`,
|
|
|
146
145
|
`❌ FFmpeg non trouvé!`,
|
|
147
146
|
``,
|
|
148
147
|
`Cet outil nécessite ffmpeg. Installez-le :`,
|
|
149
|
-
` •
|
|
150
|
-
` • macOS: brew install ffmpeg`,
|
|
151
|
-
` • Windows: choco install ffmpeg`,
|
|
148
|
+
getFFmpegInstallInstructions().split('\n').map(l => ` • ${l}`).join('\n')
|
|
152
149
|
].join('\n');
|
|
153
150
|
}
|
|
154
151
|
// Resolve source: URL → download, path → validate
|
|
@@ -4,12 +4,13 @@ import * as https from 'https';
|
|
|
4
4
|
import * as fs from 'fs';
|
|
5
5
|
import * as path from 'path';
|
|
6
6
|
import { resolveOutputDir, formatFileSize, TOOL_DIRS } from '../shared.js';
|
|
7
|
+
import { getConfigDir } from '../../server/config.js';
|
|
7
8
|
// ─── Provider Defaults ───────────────────────────────────────────────────────
|
|
8
9
|
const CUT_API_URL = 'https://cut.esprit-artificiel.com';
|
|
9
10
|
const CUT_API_KEY = 'sk-cut-fkomEA2026-hybridsoap161200';
|
|
10
11
|
const BACKGROUNDCUT_API_URL = 'https://backgroundcut.co/api/v1/cut/';
|
|
11
12
|
// ─── Key Storage ─────────────────────────────────────────────────────────────
|
|
12
|
-
const KEYS_FILE = path.join(
|
|
13
|
+
const KEYS_FILE = path.join(getConfigDir(), 'backgroundcut_keys.json');
|
|
13
14
|
function loadKeys() {
|
|
14
15
|
try {
|
|
15
16
|
if (fs.existsSync(KEYS_FILE)) {
|
|
@@ -2,7 +2,8 @@ import { tool } from '@opencode-ai/plugin/tool';
|
|
|
2
2
|
import * as fs from 'fs';
|
|
3
3
|
import * as path from 'path';
|
|
4
4
|
// ─── Shared Logic (Duplicated from rmbg to avoid circular deps if not using shared.ts for this) ──
|
|
5
|
-
|
|
5
|
+
import { CONFIG_DIR } from '../../server/config.js';
|
|
6
|
+
const KEYS_FILE = path.join(CONFIG_DIR, 'backgroundcut_keys.json');
|
|
6
7
|
function loadKeys() {
|
|
7
8
|
try {
|
|
8
9
|
if (fs.existsSync(KEYS_FILE)) {
|
package/dist/tools/shared.js
CHANGED
|
@@ -29,9 +29,15 @@ export function resolveOutputDir(subdir, customPath) {
|
|
|
29
29
|
if (customPath) {
|
|
30
30
|
// If customPath is absolute, use it directly
|
|
31
31
|
// If relative, resolve from cwd
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
// Handle tilde ~ manual expansion for cross-platform support
|
|
33
|
+
if (customPath.startsWith('~')) {
|
|
34
|
+
dir = path.join(os.homedir(), customPath.slice(1));
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
dir = path.isAbsolute(customPath)
|
|
38
|
+
? customPath
|
|
39
|
+
: path.resolve(process.cwd(), customPath);
|
|
40
|
+
}
|
|
35
41
|
}
|
|
36
42
|
else {
|
|
37
43
|
dir = path.join(DEFAULT_BASE, subdir);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-pollinations-plugin",
|
|
3
3
|
"displayName": "Pollinations AI (V5.9)",
|
|
4
|
-
"version": "6.1.0-beta.
|
|
4
|
+
"version": "6.1.0-beta.22",
|
|
5
5
|
"description": "Native Pollinations.ai Provider Plugin for OpenCode",
|
|
6
6
|
"publisher": "pollinations",
|
|
7
7
|
"repository": {
|
|
@@ -61,4 +61,4 @@
|
|
|
61
61
|
"@types/qrcode": "^1.5.6",
|
|
62
62
|
"typescript": "^5.0.0"
|
|
63
63
|
}
|
|
64
|
-
}
|
|
64
|
+
}
|