opencode-pollinations-plugin 5.8.3 → 5.8.4-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/dist/index.js
CHANGED
|
@@ -100,11 +100,13 @@ export const PollinationsPlugin = async (ctx) => {
|
|
|
100
100
|
const version = require('../package.json').version;
|
|
101
101
|
config.provider['pollinations'] = {
|
|
102
102
|
id: 'pollinations',
|
|
103
|
+
npm: require('../package.json').name,
|
|
103
104
|
name: `Pollinations AI (v${version})`,
|
|
104
105
|
options: { baseURL: localBaseUrl },
|
|
105
106
|
models: modelsObj
|
|
106
107
|
};
|
|
107
108
|
log(`[Hook] Registered ${Object.keys(modelsObj).length} models.`);
|
|
109
|
+
log(`[Hook] Keys: ${Object.keys(modelsObj).join(', ')}`);
|
|
108
110
|
},
|
|
109
111
|
...toastHooks,
|
|
110
112
|
...createStatusHooks(ctx.client),
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
interface OpenCodeModel {
|
|
2
2
|
id: string;
|
|
3
3
|
name: string;
|
|
4
|
-
object: string;
|
|
5
|
-
variants?: any;
|
|
6
4
|
options?: any;
|
|
7
5
|
limit?: {
|
|
8
6
|
context?: number;
|
|
@@ -12,6 +10,7 @@ interface OpenCodeModel {
|
|
|
12
10
|
input?: string[];
|
|
13
11
|
output?: string[];
|
|
14
12
|
};
|
|
13
|
+
tool_call?: boolean;
|
|
15
14
|
}
|
|
16
15
|
export declare function generatePollinationsConfig(forceApiKey?: string, forceStrict?: boolean): Promise<OpenCodeModel[]>;
|
|
17
16
|
export {};
|
|
@@ -7,15 +7,8 @@ const HOMEDIR = os.homedir();
|
|
|
7
7
|
const CONFIG_DIR_POLLI = path.join(HOMEDIR, '.pollinations');
|
|
8
8
|
const CACHE_FILE = path.join(CONFIG_DIR_POLLI, 'models-cache.json');
|
|
9
9
|
// --- CONSTANTS ---
|
|
10
|
-
// Seed from
|
|
11
|
-
|
|
12
|
-
{ "name": "gemini", "description": "Gemini 2.5 Flash Lite", "tier": "anonymous", "tools": true, "input_modalities": ["text", "image"], "output_modalities": ["text"], "vision": true },
|
|
13
|
-
{ "name": "mistral", "description": "Mistral Small 3.2 24B", "tier": "anonymous", "tools": true, "input_modalities": ["text"], "output_modalities": ["text"], "vision": false },
|
|
14
|
-
{ "name": "openai-fast", "description": "GPT-OSS 20B Reasoning LLM (OVH)", "tier": "anonymous", "tools": true, "input_modalities": ["text"], "output_modalities": ["text"], "vision": false, "reasoning": true },
|
|
15
|
-
{ "name": "bidara", "description": "BIDARA (Biomimetic Designer)", "tier": "anonymous", "community": true, "input_modalities": ["text", "image"], "output_modalities": ["text"], "vision": true },
|
|
16
|
-
{ "name": "chickytutor", "description": "ChickyTutor AI Language Tutor", "tier": "anonymous", "community": true, "input_modalities": ["text"], "output_modalities": ["text"] },
|
|
17
|
-
{ "name": "midijourney", "description": "MIDIjourney", "tier": "anonymous", "community": true, "input_modalities": ["text"], "output_modalities": ["text"] }
|
|
18
|
-
];
|
|
10
|
+
// Seed from models-seed.ts
|
|
11
|
+
import { FREE_MODELS_SEED } from './models-seed.js';
|
|
19
12
|
// --- LOGGING ---
|
|
20
13
|
const LOG_FILE = '/tmp/opencode_pollinations_config.log';
|
|
21
14
|
function log(msg) {
|
|
@@ -28,23 +21,39 @@ function log(msg) {
|
|
|
28
21
|
catch (e) { }
|
|
29
22
|
}
|
|
30
23
|
// --- NETWORK HELPER ---
|
|
24
|
+
function fetchHead(url) {
|
|
25
|
+
return new Promise((resolve) => {
|
|
26
|
+
// Use Node.js native https check for minimal overhead
|
|
27
|
+
const req = https.request(url, { method: 'HEAD', timeout: 5000 }, (res) => {
|
|
28
|
+
resolve(res.headers['etag'] || null);
|
|
29
|
+
});
|
|
30
|
+
req.on('error', () => resolve(null));
|
|
31
|
+
req.on('timeout', () => { req.destroy(); resolve(null); });
|
|
32
|
+
req.end();
|
|
33
|
+
});
|
|
34
|
+
}
|
|
31
35
|
function fetchJson(url, headers = {}) {
|
|
32
36
|
return new Promise((resolve, reject) => {
|
|
33
37
|
const finalHeaders = {
|
|
34
38
|
...headers,
|
|
35
|
-
'User-Agent': 'Mozilla/5.0 (compatible; OpenCode/5.8.
|
|
39
|
+
'User-Agent': 'Mozilla/5.0 (compatible; OpenCode/5.8.4; +https://opencode.ai)'
|
|
36
40
|
};
|
|
37
41
|
const req = https.get(url, { headers: finalHeaders }, (res) => {
|
|
42
|
+
const etag = res.headers['etag'];
|
|
38
43
|
let data = '';
|
|
39
44
|
res.on('data', chunk => data += chunk);
|
|
40
45
|
res.on('end', () => {
|
|
41
46
|
try {
|
|
42
47
|
const json = JSON.parse(data);
|
|
48
|
+
// HACK: Attach ETag to the object to pass it up
|
|
49
|
+
if (etag && typeof json === 'object') {
|
|
50
|
+
Object.defineProperty(json, '_etag', { value: etag, enumerable: false, writable: true });
|
|
51
|
+
}
|
|
43
52
|
resolve(json);
|
|
44
53
|
}
|
|
45
54
|
catch (e) {
|
|
46
55
|
log(`JSON Parse Error for ${url}: ${e}`);
|
|
47
|
-
resolve([]); // Fail safe
|
|
56
|
+
resolve([]); // Fail safe
|
|
48
57
|
}
|
|
49
58
|
});
|
|
50
59
|
});
|
|
@@ -58,7 +67,6 @@ function fetchJson(url, headers = {}) {
|
|
|
58
67
|
});
|
|
59
68
|
});
|
|
60
69
|
}
|
|
61
|
-
// --- CACHE MANAGER ---
|
|
62
70
|
function loadCache() {
|
|
63
71
|
try {
|
|
64
72
|
if (fs.existsSync(CACHE_FILE)) {
|
|
@@ -71,10 +79,11 @@ function loadCache() {
|
|
|
71
79
|
}
|
|
72
80
|
return null;
|
|
73
81
|
}
|
|
74
|
-
function saveCache(models) {
|
|
82
|
+
function saveCache(models, etag) {
|
|
75
83
|
try {
|
|
76
84
|
const data = {
|
|
77
85
|
timestamp: Date.now(),
|
|
86
|
+
etag: etag,
|
|
78
87
|
models: models
|
|
79
88
|
};
|
|
80
89
|
if (!fs.existsSync(CONFIG_DIR_POLLI))
|
|
@@ -89,25 +98,43 @@ function saveCache(models) {
|
|
|
89
98
|
export async function generatePollinationsConfig(forceApiKey, forceStrict = false) {
|
|
90
99
|
const config = loadConfig();
|
|
91
100
|
const modelsOutput = [];
|
|
92
|
-
log(`Starting Configuration (v5.8.
|
|
101
|
+
log(`Starting Configuration (v5.8.4-Debug-Tools)...`);
|
|
93
102
|
const effectiveKey = forceApiKey || config.apiKey;
|
|
94
|
-
// 1. FREE UNIVERSE (Cache System)
|
|
103
|
+
// 1. FREE UNIVERSE (Smart Cache System)
|
|
95
104
|
let freeModelsList = [];
|
|
96
105
|
let isOffline = false;
|
|
97
106
|
let cache = loadCache();
|
|
98
107
|
const CACHE_TTL = 7 * 24 * 3600 * 1000; // 7 days
|
|
99
|
-
// Decision
|
|
108
|
+
// Decision Logic
|
|
100
109
|
const now = Date.now();
|
|
101
110
|
let shouldFetch = !cache || (now - cache.timestamp > CACHE_TTL);
|
|
111
|
+
// ETag Check: If cache is valid but we want to be proactive
|
|
112
|
+
if (!shouldFetch && cache && cache.etag) {
|
|
113
|
+
try {
|
|
114
|
+
log('Smart Refresh: Checking for updates (HEAD)...');
|
|
115
|
+
const remoteEtag = await fetchHead('https://text.pollinations.ai/models');
|
|
116
|
+
if (remoteEtag && remoteEtag !== cache.etag) {
|
|
117
|
+
log(`Update Detected! (Remote: ${remoteEtag} != Local: ${cache.etag}). Forcing refresh.`);
|
|
118
|
+
shouldFetch = true;
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
log('Cache is clean (ETag match). No refresh needed.');
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
catch (e) {
|
|
125
|
+
log(`Smart Refresh check failed: ${e}. Ignoring.`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
102
128
|
if (shouldFetch) {
|
|
103
|
-
log('
|
|
129
|
+
log('Fetching fresh Free models...');
|
|
104
130
|
try {
|
|
105
131
|
const raw = await fetchJson('https://text.pollinations.ai/models');
|
|
106
132
|
const list = Array.isArray(raw) ? raw : (raw.data || []);
|
|
133
|
+
const newEtag = raw._etag; // Get hidden ETag
|
|
107
134
|
if (list.length > 0) {
|
|
108
135
|
freeModelsList = list;
|
|
109
|
-
saveCache(list);
|
|
110
|
-
log(`Fetched and cached ${list.length} models.`);
|
|
136
|
+
saveCache(list, newEtag);
|
|
137
|
+
log(`Fetched and cached ${list.length} models (ETag: ${newEtag || 'N/A'}).`);
|
|
111
138
|
}
|
|
112
139
|
else {
|
|
113
140
|
throw new Error('API returned empty list');
|
|
@@ -116,29 +143,27 @@ export async function generatePollinationsConfig(forceApiKey, forceStrict = fals
|
|
|
116
143
|
catch (e) {
|
|
117
144
|
log(`Fetch failed: ${e}.`);
|
|
118
145
|
isOffline = true;
|
|
119
|
-
// Fallback
|
|
146
|
+
// Fallback Logic
|
|
120
147
|
if (cache && cache.models.length > 0) {
|
|
121
148
|
log('Using cached models (Offline).');
|
|
122
149
|
freeModelsList = cache.models;
|
|
123
150
|
}
|
|
124
151
|
else {
|
|
125
152
|
log('Using DEFAULT SEED models (Offline + No Cache).');
|
|
126
|
-
freeModelsList =
|
|
153
|
+
freeModelsList = FREE_MODELS_SEED;
|
|
127
154
|
}
|
|
128
155
|
}
|
|
129
156
|
}
|
|
130
157
|
else {
|
|
131
|
-
log('
|
|
158
|
+
log('Using cached models (Skipped fetch).');
|
|
132
159
|
freeModelsList = cache.models;
|
|
133
160
|
}
|
|
134
161
|
// Map Free Models
|
|
135
162
|
freeModelsList.forEach((m) => {
|
|
136
|
-
//
|
|
137
|
-
//
|
|
138
|
-
// when valid list is reached but date > 8 days (deprecated) or fallback used?
|
|
139
|
-
// Let's mark it only if we tried to fetch and failed.
|
|
163
|
+
// Tag (Offline) only if we explicitly failed a fetch attempt or are using Fallback SEED when fetch failed.
|
|
164
|
+
// If we use cache because it's valid (Skipped fetch), we don't tag (Offline).
|
|
140
165
|
const suffix = isOffline ? ' (Offline)' : '';
|
|
141
|
-
const mapped = mapModel(m, 'free
|
|
166
|
+
const mapped = mapModel(m, 'free-', `[Free] `, suffix);
|
|
142
167
|
modelsOutput.push(mapped);
|
|
143
168
|
});
|
|
144
169
|
// 2. ENTERPRISE UNIVERSE
|
|
@@ -151,17 +176,17 @@ export async function generatePollinationsConfig(forceApiKey, forceStrict = fals
|
|
|
151
176
|
enterList.forEach((m) => {
|
|
152
177
|
if (m.tools === false)
|
|
153
178
|
return;
|
|
154
|
-
const mapped = mapModel(m, 'enter
|
|
179
|
+
const mapped = mapModel(m, 'enter-', '[Enter] ');
|
|
155
180
|
modelsOutput.push(mapped);
|
|
156
181
|
});
|
|
157
182
|
log(`Total models (Free+Pro): ${modelsOutput.length}`);
|
|
183
|
+
log(`Generated IDs: ${modelsOutput.map(m => m.id).join(', ')}`);
|
|
158
184
|
}
|
|
159
185
|
catch (e) {
|
|
160
186
|
log(`Error fetching Enterprise models: ${e}`);
|
|
161
187
|
if (forceStrict)
|
|
162
188
|
throw e;
|
|
163
|
-
// Fallback
|
|
164
|
-
modelsOutput.push({ id: "enter/gpt-4o", name: "[Enter] GPT-4o (Fallback)", object: "model", variants: {} });
|
|
189
|
+
// STRICT: No Fallback for Enterprise. If API is down, we have 0 Enter models.
|
|
165
190
|
}
|
|
166
191
|
}
|
|
167
192
|
return modelsOutput;
|
|
@@ -209,14 +234,16 @@ function mapModel(raw, prefix, namePrefix, nameSuffix = '') {
|
|
|
209
234
|
const modelObj = {
|
|
210
235
|
id: fullId,
|
|
211
236
|
name: finalName,
|
|
212
|
-
object: 'model',
|
|
213
|
-
variants: {},
|
|
237
|
+
// object: 'model',
|
|
238
|
+
// variants: {}, // POTENTIAL SCHEMA VIOLATION
|
|
214
239
|
modalities: {
|
|
215
240
|
input: raw.input_modalities || ['text'],
|
|
216
241
|
output: raw.output_modalities || ['text']
|
|
217
|
-
}
|
|
242
|
+
},
|
|
243
|
+
tool_call: false // FORCE DEBUG DISABLE
|
|
218
244
|
};
|
|
219
245
|
// Enrichissements
|
|
246
|
+
/*
|
|
220
247
|
if (raw.reasoning === true || rawId.includes('thinking') || rawId.includes('reasoning')) {
|
|
221
248
|
modelObj.variants = { ...modelObj.variants, high_reasoning: { options: { reasoningEffort: "high", budgetTokens: 16000 } } };
|
|
222
249
|
}
|
|
@@ -228,16 +255,24 @@ function mapModel(raw, prefix, namePrefix, nameSuffix = '') {
|
|
|
228
255
|
if (rawId.includes('claude') || rawId.includes('mistral') || rawId.includes('llama')) {
|
|
229
256
|
modelObj.variants.safe_tokens = { options: { maxTokens: 8000 } };
|
|
230
257
|
}
|
|
258
|
+
*/
|
|
231
259
|
if (rawId.includes('nova')) {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
260
|
+
if (rawId.includes('nova')) {
|
|
261
|
+
modelObj.limit = { output: 8000, context: 128000 };
|
|
262
|
+
}
|
|
263
|
+
if (rawId.includes('nomnom') || rawId.includes('scrape')) {
|
|
264
|
+
modelObj.limit = { output: 2048, context: 32768 };
|
|
265
|
+
}
|
|
266
|
+
if (rawId.includes('chicky')) {
|
|
267
|
+
modelObj.limit = { output: 8192, context: 8192 };
|
|
268
|
+
}
|
|
269
|
+
/*
|
|
237
270
|
if (rawId.includes('fast') || rawId.includes('flash')) {
|
|
238
271
|
if (!rawId.includes('gemini')) {
|
|
239
272
|
modelObj.variants.speed = { options: { thinking: { disabled: true } } };
|
|
240
273
|
}
|
|
241
274
|
}
|
|
275
|
+
*/
|
|
276
|
+
}
|
|
242
277
|
return modelObj;
|
|
243
278
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface PollinationsModel {
|
|
2
|
+
name: string;
|
|
3
|
+
description?: string;
|
|
4
|
+
type?: string;
|
|
5
|
+
tools?: boolean;
|
|
6
|
+
reasoning?: boolean;
|
|
7
|
+
context?: number;
|
|
8
|
+
context_window?: number;
|
|
9
|
+
input_modalities?: string[];
|
|
10
|
+
output_modalities?: string[];
|
|
11
|
+
paid_only?: boolean;
|
|
12
|
+
vision?: boolean;
|
|
13
|
+
audio?: boolean;
|
|
14
|
+
community?: boolean;
|
|
15
|
+
censored?: boolean;
|
|
16
|
+
[key: string]: any;
|
|
17
|
+
}
|
|
18
|
+
export declare const FREE_MODELS_SEED: PollinationsModel[];
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
export const FREE_MODELS_SEED = [
|
|
2
|
+
{
|
|
3
|
+
"name": "gemini",
|
|
4
|
+
"description": "Gemini 2.5 Flash Lite",
|
|
5
|
+
"tier": "anonymous",
|
|
6
|
+
"tools": true,
|
|
7
|
+
"input_modalities": ["text", "image"],
|
|
8
|
+
"output_modalities": ["text"],
|
|
9
|
+
"vision": true
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"name": "mistral",
|
|
13
|
+
"description": "Mistral Small 3.2 24B",
|
|
14
|
+
"tier": "anonymous",
|
|
15
|
+
"tools": true,
|
|
16
|
+
"input_modalities": ["text"],
|
|
17
|
+
"output_modalities": ["text"],
|
|
18
|
+
"vision": false
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"name": "openai-fast",
|
|
22
|
+
"description": "GPT-OSS 20B Reasoning LLM (OVH)",
|
|
23
|
+
"tier": "anonymous",
|
|
24
|
+
"tools": true,
|
|
25
|
+
"input_modalities": ["text"],
|
|
26
|
+
"output_modalities": ["text"],
|
|
27
|
+
"vision": false,
|
|
28
|
+
"reasoning": true
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"name": "bidara",
|
|
32
|
+
"description": "BIDARA (Biomimetic Designer)",
|
|
33
|
+
"tier": "anonymous",
|
|
34
|
+
"community": true,
|
|
35
|
+
"input_modalities": ["text", "image"],
|
|
36
|
+
"output_modalities": ["text"],
|
|
37
|
+
"vision": true
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"name": "chickytutor",
|
|
41
|
+
"description": "ChickyTutor AI Language Tutor",
|
|
42
|
+
"tier": "anonymous",
|
|
43
|
+
"community": true,
|
|
44
|
+
"input_modalities": ["text"],
|
|
45
|
+
"output_modalities": ["text"]
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"name": "midijourney",
|
|
49
|
+
"description": "MIDIjourney",
|
|
50
|
+
"tier": "anonymous",
|
|
51
|
+
"community": true,
|
|
52
|
+
"input_modalities": ["text"],
|
|
53
|
+
"output_modalities": ["text"]
|
|
54
|
+
}
|
|
55
|
+
];
|
package/dist/server/proxy.js
CHANGED
|
@@ -153,6 +153,11 @@ export async function handleChatCompletion(req, res, bodyRaw) {
|
|
|
153
153
|
const config = loadConfig();
|
|
154
154
|
// DEBUG: Trace Config State for Hot Reload verification
|
|
155
155
|
log(`[Proxy Request] Config Loaded. Mode: ${config.mode}, HasKey: ${!!config.apiKey}, KeyLength: ${config.apiKey ? config.apiKey.length : 0}`);
|
|
156
|
+
// SPY LOGGING
|
|
157
|
+
try {
|
|
158
|
+
fs.appendFileSync('/tmp/opencode_spy.log', `\n\n=== REQUEST ${new Date().toISOString()} ===\nMODEL: ${body.model}\nBODY:\n${JSON.stringify(body, null, 2)}\n==========================\n`);
|
|
159
|
+
}
|
|
160
|
+
catch (e) { }
|
|
156
161
|
// 0. COMMAND HANDLING
|
|
157
162
|
if (body.messages && body.messages.length > 0) {
|
|
158
163
|
const lastMsg = body.messages[body.messages.length - 1];
|
|
@@ -416,13 +421,32 @@ export async function handleChatCompletion(req, res, bodyRaw) {
|
|
|
416
421
|
// Restore Tools but REMOVE conflicting ones (Search)
|
|
417
422
|
// B. GEMINI UNIFIED FIX (Free, Fast, Pro, Enterprise, Legacy)
|
|
418
423
|
// Handles: "tools" vs "grounding" conflicts, and "infinite loops" via Stop Sequences.
|
|
424
|
+
// GLOBAL BEDROCK FIX (All Models)
|
|
425
|
+
// Check if history has tools but current request misses tools definition.
|
|
426
|
+
// This happens when OpenCode sends the Tool Result (optimisation),
|
|
427
|
+
// but Bedrock requires toolConfig to validate the history.
|
|
428
|
+
const hasToolHistory = proxyBody.messages?.some((m) => m.role === 'tool' || m.tool_calls);
|
|
429
|
+
if (hasToolHistory && (!proxyBody.tools || proxyBody.tools.length === 0)) {
|
|
430
|
+
// Inject Shim Tool to satisfy Bedrock
|
|
431
|
+
proxyBody.tools = [{
|
|
432
|
+
type: 'function',
|
|
433
|
+
function: {
|
|
434
|
+
name: '_bedrock_compatibility_shim',
|
|
435
|
+
description: 'Internal system tool to satisfy Bedrock strict toolConfig requirement. Do not use.',
|
|
436
|
+
parameters: { type: 'object', properties: {} }
|
|
437
|
+
}
|
|
438
|
+
}];
|
|
439
|
+
log(`[Proxy] Bedrock Fix: Injected shim tool for ${actualModel} (History has tools, Request missing tools)`);
|
|
440
|
+
}
|
|
419
441
|
// B. GEMINI UNIFIED FIX (Free, Fast, Pro, Enterprise, Legacy)
|
|
420
442
|
// Fixes "Multiple tools" error (Vertex) and "JSON body validation failed" (v5.3.5 regression)
|
|
421
|
-
|
|
443
|
+
// Added ChickyTutor (Claude/Gemini based) to fix "toolConfig must be defined" error.
|
|
444
|
+
else if (actualModel.includes("gemini") || actualModel.includes("chickytutor")) {
|
|
422
445
|
let hasFunctions = false;
|
|
423
446
|
if (proxyBody.tools && Array.isArray(proxyBody.tools)) {
|
|
424
447
|
hasFunctions = proxyBody.tools.some((t) => t.type === 'function' || t.function);
|
|
425
448
|
}
|
|
449
|
+
// Old Shim logic removed (moved up)
|
|
426
450
|
if (hasFunctions) {
|
|
427
451
|
// 1. Strict cleanup of 'google_search' tool
|
|
428
452
|
proxyBody.tools = proxyBody.tools.filter((t) => {
|
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": "5.8.
|
|
4
|
+
"version": "5.8.4-beta.10",
|
|
5
5
|
"description": "Native Pollinations.ai Provider Plugin for OpenCode",
|
|
6
6
|
"publisher": "pollinations",
|
|
7
7
|
"repository": {
|
|
@@ -55,4 +55,4 @@
|
|
|
55
55
|
"@types/node": "^20.0.0",
|
|
56
56
|
"typescript": "^5.0.0"
|
|
57
57
|
}
|
|
58
|
-
}
|
|
58
|
+
}
|