avg-nexus 1.0.5 → 1.0.7

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/bin/avg-nexus.js +235 -150
  2. package/package.json +10 -9
package/bin/avg-nexus.js CHANGED
@@ -6,178 +6,157 @@ const http = require('http');
6
6
  const path = require('path');
7
7
  const fs = require('fs');
8
8
 
9
- async function main() {
10
- console.log('\nšŸš€ avg-nexus ishga tushmoqda...\n');
11
-
12
- // 1. Ollama topish
13
- const ollamaPath = findOllama();
14
- if (!ollamaPath) {
15
- console.error('āŒ Ollama topilmadi! https://ollama.com dan yuklab oling.');
16
- process.exit(1);
17
- }
18
- console.log('āœ… Ollama topildi:', ollamaPath);
19
-
20
- // 2. Ollama ishga tushirish
21
- startOllama(ollamaPath);
22
- await sleep(2000);
23
-
24
- // 3. Model tekshirish
25
- const model = await getBestModel();
26
- console.log('āœ… Model:', model);
27
-
28
- // 4. Proxy serverini ishga tushirish
29
- startProxy(model);
30
- console.log('āœ… OpenRouter proxy: http://localhost:11435');
31
-
32
- // 5. NexusAI saytini topish va ishga tushirish
33
- const projectDir = findProject();
34
- if (!projectDir) {
35
- console.error('āŒ NexusAI loyihasi topilmadi!');
36
- console.error(' Loyiha papkasida turib avg-nexus buyrug\'ini bering.');
37
- process.exit(1);
38
- }
39
- console.log('āœ… Loyiha:', projectDir);
40
-
41
- // .env.local yaratish
42
- setupEnv(projectDir);
43
-
44
- // npm install (kerak bo'lsa)
45
- if (!fs.existsSync(path.join(projectDir, 'node_modules'))) {
46
- console.log('šŸ“¦ Dependencylar o\'rnatilmoqda...');
47
- execSync('npm install', { cwd: projectDir, stdio: 'inherit' });
48
- }
49
-
50
- console.log('\n🌐 Sayt ishga tushmoqda: http://localhost:3000\n');
51
- const dev = spawn('npm', ['run', 'dev'], {
52
- cwd: projectDir,
53
- stdio: 'inherit',
54
- shell: true,
55
- env: {
56
- ...process.env,
57
- OPENROUTER_BASE_URL: 'http://localhost:11435/api/v1',
58
- OPENROUTER_API_KEY_1: 'ollama-local',
59
- }
60
- });
9
+ // ── Ranglar ──
10
+ const C = {
11
+ reset: '\x1b[0m',
12
+ green: '\x1b[32m',
13
+ red: '\x1b[31m',
14
+ yellow: '\x1b[33m',
15
+ cyan: '\x1b[36m',
16
+ bold: '\x1b[1m',
17
+ dim: '\x1b[2m',
18
+ };
61
19
 
62
- dev.on('error', (err) => console.error('āŒ Sayt xatosi:', err));
20
+ function log(icon, msg, color = C.reset) {
21
+ console.log(`${color}${icon} ${msg}${C.reset}`);
63
22
  }
64
23
 
24
+ // ── Ollama topish (barcha disklar) ──
65
25
  function findOllama() {
66
- // PATH dan qidirish
26
+ // 1. PATH dan
67
27
  try {
68
- const result = execSync('where ollama', { encoding: 'utf8' }).trim();
69
- if (result) return result.split('\n')[0].trim();
28
+ const r = execSync('where ollama', { encoding: 'utf8', stdio: ['pipe','pipe','pipe'] }).trim();
29
+ if (r) return r.split('\n')[0].trim();
70
30
  } catch {}
71
31
 
72
- // Barcha disklarda qidirish
73
- const drives = ['C', 'D', 'E', 'F', 'G'];
32
+ // 2. Barcha disklarda qidirish
33
+ const drives = ['A','B','C','D','E','F','G','H','I','J'];
74
34
  const locations = [
75
35
  'Ollama\\ollama.exe',
76
36
  'Program Files\\Ollama\\ollama.exe',
77
- 'Users\\' + (process.env.USERNAME || 'user') + '\\AppData\\Local\\Programs\\Ollama\\ollama.exe',
37
+ `Users\\${process.env.USERNAME || 'user'}\\AppData\\Local\\Programs\\Ollama\\ollama.exe`,
38
+ 'AppData\\Local\\Programs\\Ollama\\ollama.exe',
78
39
  ];
79
40
 
80
41
  for (const drive of drives) {
81
42
  for (const loc of locations) {
82
43
  const full = `${drive}:\\${loc}`;
83
- if (fs.existsSync(full)) return full;
44
+ try {
45
+ if (fs.existsSync(full)) return full;
46
+ } catch {}
84
47
  }
85
48
  }
86
49
  return null;
87
50
  }
88
51
 
89
- function startOllama(ollamaPath) {
90
- try {
91
- execSync('tasklist /FI "IMAGENAME eq ollama.exe"', { encoding: 'utf8' });
92
- } catch {}
93
-
94
- try {
95
- spawn(ollamaPath, ['serve'], {
96
- detached: true,
97
- stdio: 'ignore',
98
- shell: false
99
- }).unref();
100
- console.log('āœ… Ollama serve ishga tushdi');
101
- } catch (e) {
102
- console.log('ā„¹ļø Ollama allaqachon ishlamoqda');
103
- }
104
- }
105
-
106
- async function getBestModel() {
107
- await sleep(2000)
108
- try {
109
- const res = await fetch('http://localhost:11434/api/tags')
110
- const data = await res.json()
111
- const models = data.models || []
112
-
113
- if (models.length > 0) {
114
- const preferred = ['gemma4', 'gemma3', 'gemma2', 'qwen', 'llama', 'mistral', 'deepseek']
115
- for (const p of preferred) {
116
- const found = models.find(m => m.name.toLowerCase().includes(p))
117
- if (found) {
118
- console.log('āœ… Model topildi:', found.name)
119
- return found.name
52
+ // ── Model topish (barcha disklar) ──
53
+ async function findBestModel() {
54
+ // 10 marta urinib ko'rish
55
+ for (let i = 0; i < 10; i++) {
56
+ await sleep(4000);
57
+ try {
58
+ const res = await fetch('http://localhost:11434/api/tags');
59
+ const data = await res.json();
60
+ const models = data.models || [];
61
+ if (models.length > 0) {
62
+ const preferred = ['gemma4', 'gemma3', 'gemma2', 'qwen', 'llama', 'mistral', 'deepseek', 'phi'];
63
+ for (const pref of preferred) {
64
+ const found = models.find(m => m.name.toLowerCase().includes(pref));
65
+ if (found) {
66
+ log('āœ…', `Model topildi: ${found.name}`, C.green);
67
+ return found.name;
68
+ }
120
69
  }
70
+ return models[0].name;
121
71
  }
122
- console.log('āœ… Model:', models[0].name)
123
- return models[0].name
72
+ log('ā³', `Model kutilmoqda... (${i+1}/10)`, C.yellow);
73
+ } catch {
74
+ log('ā³', `Ollama kutilmoqda... (${i+1}/10)`, C.yellow);
124
75
  }
125
-
126
- // Hech model yo'q — yuklab olish
127
- console.log('šŸ“„ Model topilmadi, gemma4:e4b yuklanmoqda...')
128
- execSync('ollama pull gemma4:e4b', { stdio: 'inherit' })
129
- return 'gemma4:e4b'
130
-
131
- } catch (e) {
132
- return 'gemma4:e4b'
133
76
  }
77
+ log('šŸ“„', 'Model topilmadi, gemma4:e4b yuklanmoqda...', C.yellow);
78
+ execSync('ollama pull gemma4:e4b', { stdio: 'inherit' });
79
+ return 'gemma4:e4b';
80
+ }
81
+
82
+ // ── Ollama ishga tushirish ──
83
+ function startOllama(ollamaPath) {
84
+ try {
85
+ execSync('curl -s http://localhost:11434', { timeout: 1000, stdio: 'pipe' });
86
+ log('āœ…', 'Ollama allaqachon ishlamoqda', C.green);
87
+ return;
88
+ } catch {}
89
+
90
+ log('šŸš€', 'Ollama ishga tushirilmoqda...', C.cyan);
91
+ const proc = spawn(ollamaPath, ['serve'], {
92
+ detached: true,
93
+ stdio: 'ignore',
94
+ windowsHide: true,
95
+ });
96
+ proc.unref();
97
+ log('āœ…', 'Ollama serve ishga tushdi', C.green);
134
98
  }
135
99
 
100
+ // ── OpenRouter Proxy ──
136
101
  function startProxy(model) {
102
+ const PROXY_PORT = 11435;
103
+
137
104
  const server = http.createServer(async (req, res) => {
105
+ res.setHeader('Access-Control-Allow-Origin', '*');
106
+ res.setHeader('Access-Control-Allow-Headers', '*');
107
+ res.setHeader('Access-Control-Allow-Methods', 'POST, GET, OPTIONS');
108
+
109
+ if (req.method === 'OPTIONS') {
110
+ res.writeHead(200);
111
+ res.end();
112
+ return;
113
+ }
114
+
115
+ // Models endpoint
116
+ if (req.url && req.url.includes('/models')) {
117
+ res.writeHead(200, { 'Content-Type': 'application/json' });
118
+ res.end(JSON.stringify({
119
+ data: [{ id: model, name: model, context_length: 8192 }]
120
+ }));
121
+ return;
122
+ }
123
+
138
124
  let body = '';
139
- req.on('data', chunk => body += chunk);
125
+ req.on('data', c => body += c);
140
126
  req.on('end', async () => {
141
127
  try {
142
128
  let reqData = {};
143
129
  try { reqData = JSON.parse(body); } catch {}
144
130
 
145
- // Ollama ga yo'naltirish
146
131
  const ollamaRes = await fetch('http://localhost:11434/api/chat', {
147
132
  method: 'POST',
148
133
  headers: { 'Content-Type': 'application/json' },
149
134
  body: JSON.stringify({
150
135
  model: reqData.model || model,
151
136
  messages: reqData.messages || [],
152
- stream: reqData.stream !== false,
153
- })
137
+ stream: true,
138
+ }),
154
139
  });
155
140
 
156
- res.writeHead(ollamaRes.status, {
157
- 'Content-Type': 'text/event-stream',
158
- 'Access-Control-Allow-Origin': '*',
159
- 'Access-Control-Allow-Headers': '*',
160
- });
141
+ res.writeHead(200, { 'Content-Type': 'text/event-stream' });
161
142
 
162
143
  const reader = ollamaRes.body.getReader();
163
144
  const decoder = new TextDecoder();
164
-
145
+
165
146
  while (true) {
166
147
  const { done, value } = await reader.read();
167
148
  if (done) break;
168
149
  const text = decoder.decode(value);
169
- const lines = text.split('\n').filter(l => l.trim());
170
- for (const line of lines) {
150
+ for (const line of text.split('\n').filter(l => l.trim())) {
171
151
  try {
172
152
  const json = JSON.parse(line);
173
153
  const content = json.message?.content || '';
174
154
  const isDone = json.done || false;
175
- const chunk = {
155
+ res.write('data: ' + JSON.stringify({
176
156
  id: 'chatcmpl-' + Date.now(),
177
157
  object: 'chat.completion.chunk',
178
- choices: [{ delta: { content }, finish_reason: isDone ? 'stop' : null }]
179
- };
180
- res.write('data: ' + JSON.stringify(chunk) + '\n\n');
158
+ choices: [{ delta: { content }, finish_reason: isDone ? 'stop' : null }],
159
+ }) + '\n\n');
181
160
  } catch {}
182
161
  }
183
162
  }
@@ -190,60 +169,166 @@ function startProxy(model) {
190
169
  });
191
170
  });
192
171
 
193
- server.listen(11435, () => {});
172
+ server.listen(PROXY_PORT, () => {
173
+ log('āœ…', `OpenRouter proxy: http://localhost:${PROXY_PORT}`, C.green);
174
+ });
175
+
176
+ return server;
194
177
  }
195
178
 
179
+ // ── NexusAI loyihasini topish ──
196
180
  function findProject() {
197
181
  const cwd = process.cwd();
198
-
199
- // Hozirgi papkada package.json bormi?
200
- if (fs.existsSync(path.join(cwd, 'package.json'))) {
201
- const pkg = JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf8'));
202
- if (pkg.name && pkg.name.toLowerCase().includes('nexus')) return cwd;
203
- // next.js loyihasi bo'lsa ham qabul qilish
204
- if (fs.existsSync(path.join(cwd, 'next.config.js')) ||
205
- fs.existsSync(path.join(cwd, 'next.config.ts'))) return cwd;
182
+
183
+ // Hozirgi papkada next.config bormi?
184
+ const nextConfigs = ['next.config.js', 'next.config.ts', 'next.config.mjs'];
185
+ for (const cfg of nextConfigs) {
186
+ if (fs.existsSync(path.join(cwd, cfg))) return cwd;
206
187
  }
207
188
 
208
189
  // Barcha disklarda qidirish
209
- const drives = ['E', 'D', 'C', 'F'];
210
- const projectNames = ['NexusAi-team', 'nexusai-team', 'NexusAI', 'nexus-team'];
211
-
190
+ const drives = ['E', 'D', 'C', 'F', 'G'];
191
+ const names = ['nexusai-team', 'NexusAi-team', 'NexusAI-team', 'nexus-team', 'nexusai'];
192
+
212
193
  for (const drive of drives) {
213
- for (const name of projectNames) {
194
+ for (const name of names) {
214
195
  const p = `${drive}:\\${name}`;
215
- if (fs.existsSync(p)) return p;
196
+ try {
197
+ if (fs.existsSync(p) && nextConfigs.some(c => fs.existsSync(path.join(p, c)))) {
198
+ return p;
199
+ }
200
+ } catch {}
216
201
  }
217
202
  }
218
203
  return null;
219
204
  }
220
205
 
206
+ // ── .env.local sozlash ──
221
207
  function setupEnv(projectDir) {
222
208
  const envPath = path.join(projectDir, '.env.local');
223
- if (!fs.existsSync(envPath)) {
224
- fs.writeFileSync(envPath, [
225
- 'OPENROUTER_BASE_URL=http://localhost:11435/api/v1',
226
- 'OPENROUTER_API_KEY_1=ollama-local',
227
- 'OPENROUTER_API_KEY_2=ollama-local',
228
- 'OPENROUTER_API_KEY_3=ollama-local',
229
- 'OPENROUTER_API_KEY_4=ollama-local',
230
- 'OPENROUTER_API_KEY_5=ollama-local',
231
- 'OPENROUTER_API_KEY_5=ollama-local',
232
- 'OPENROUTER_API_KEY_6=ollama-local',
233
- 'OPENROUTER_API_KEY_7=ollama-local',
234
- 'OPENROUTER_API_KEY_8=ollama-local',
235
- 'OPENROUTER_API_KEY_9=ollama-local',
236
- 'OPENROUTER_API_KEY_10=ollama-local',
237
- ].join('\n'));
238
- console.log('āœ… .env.local yaratildi');
209
+ const content = [
210
+ 'OPENROUTER_BASE_URL=http://localhost:11435/api/v1',
211
+ 'OPENROUTER_API_KEY_1=ollama-local',
212
+ 'OPENROUTER_API_KEY_2=ollama-local',
213
+ 'OPENROUTER_API_KEY_3=ollama-local',
214
+ ].join('\n');
215
+
216
+ // Agar mavjud bo'lsa OpenRouter kalitlarini saqlab qolamiz
217
+ if (fs.existsSync(envPath)) {
218
+ const existing = fs.readFileSync(envPath, 'utf8');
219
+ if (existing.includes('OLLAMA') || existing.includes('ollama-local')) return;
220
+ // OpenRouter kalitlari bor bo'lsa ularga proxy qo'shamiz
221
+ fs.writeFileSync(envPath, content + '\n' + existing);
222
+ } else {
223
+ fs.writeFileSync(envPath, content);
239
224
  }
225
+ log('āœ…', '.env.local sozlandi', C.green);
226
+ }
227
+
228
+ // ── Next.js serverni ishga tushirish ──
229
+ function startNextJS(projectDir) {
230
+ return new Promise((resolve) => {
231
+ setupEnv(projectDir);
232
+
233
+ // node_modules bormi?
234
+ if (!fs.existsSync(path.join(projectDir, 'node_modules'))) {
235
+ log('šŸ“¦', 'npm install...', C.yellow);
236
+ execSync('npm install', { cwd: projectDir, stdio: 'inherit' });
237
+ }
238
+
239
+ log('🌐', 'NexusAI sayt ishga tushmoqda...', C.cyan);
240
+
241
+ const dev = spawn('npm', ['run', 'dev'], {
242
+ cwd: projectDir,
243
+ shell: true,
244
+ stdio: ['ignore', 'pipe', 'pipe'],
245
+ env: {
246
+ ...process.env,
247
+ PORT: '3000',
248
+ OPENROUTER_BASE_URL: 'http://localhost:11435/api/v1',
249
+ OPENROUTER_API_KEY_1: 'ollama-local',
250
+ },
251
+ });
252
+
253
+ dev.stdout.on('data', (data) => {
254
+ const text = data.toString();
255
+ if (text.includes('Ready') || text.includes('ready') || text.includes('localhost:3000')) {
256
+ log('āœ…', 'Sayt tayyor: http://localhost:3000', C.green);
257
+ setTimeout(resolve, 500);
258
+ }
259
+ });
260
+
261
+ dev.stderr.on('data', () => {});
262
+ dev.on('error', (err) => log('āŒ', err.message, C.red));
263
+
264
+ // Max 60 sekund
265
+ setTimeout(resolve, 60000);
266
+ });
240
267
  }
241
268
 
242
269
  function sleep(ms) {
243
- return new Promise(resolve => setTimeout(resolve, ms));
270
+ return new Promise(r => setTimeout(r, ms));
271
+ }
272
+
273
+ // ── MAIN ──
274
+ async function main() {
275
+ console.log(`\n${C.bold}${C.cyan}
276
+ ā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā•—ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—ā–ˆā–ˆā•— ā–ˆā–ˆā•—ā–ˆā–ˆā•— ā–ˆā–ˆā•—ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā•—
277
+ ā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā•‘ā–ˆā–ˆā•”ā•ā•ā•ā•ā•ā•šā–ˆā–ˆā•—ā–ˆā–ˆā•”ā•ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•”ā•ā•ā•ā•ā•ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•—ā–ˆā–ˆā•‘
278
+ ā–ˆā–ˆā•”ā–ˆā–ˆā•— ā–ˆā–ˆā•‘ā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā•šā–ˆā–ˆā–ˆā•”ā• ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•‘ā–ˆā–ˆā•‘
279
+ ā–ˆā–ˆā•‘ā•šā–ˆā–ˆā•—ā–ˆā–ˆā•‘ā–ˆā–ˆā•”ā•ā•ā• ā–ˆā–ˆā•”ā–ˆā–ˆā•— ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā•šā•ā•ā•ā•ā–ˆā–ˆā•‘ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•‘ā–ˆā–ˆā•‘
280
+ ā–ˆā–ˆā•‘ ā•šā–ˆā–ˆā–ˆā–ˆā•‘ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—ā–ˆā–ˆā•”ā• ā–ˆā–ˆā•—ā•šā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•”ā•ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•‘
281
+ ā•šā•ā• ā•šā•ā•ā•ā•ā•šā•ā•ā•ā•ā•ā•ā•ā•šā•ā• ā•šā•ā• ā•šā•ā•ā•ā•ā•ā• ā•šā•ā•ā•ā•ā•ā•ā•ā•šā•ā• ā•šā•ā•ā•šā•ā•
282
+ ${C.reset}${C.dim} TEAM v3.0 — 15 AI Agents${C.reset}\n`);
283
+
284
+ // 1. Ollama topish
285
+ log('šŸ”', 'Ollama qidirilmoqda...', C.cyan);
286
+ const ollamaPath = findOllama();
287
+
288
+ if (!ollamaPath) {
289
+ log('āŒ', 'Ollama topilmadi!', C.red);
290
+ log('šŸ’”', 'Yuklab oling: https://ollama.com/download', C.yellow);
291
+ process.exit(1);
292
+ }
293
+ log('āœ…', `Ollama: ${ollamaPath}`, C.green);
294
+
295
+ // 2. Ollama ishga tushirish
296
+ startOllama(ollamaPath);
297
+ await sleep(2000);
298
+
299
+ // 3. Model topish
300
+ log('šŸ”', 'AI model qidirilmoqda...', C.cyan);
301
+ const model = await findBestModel();
302
+
303
+ // 4. Proxy
304
+ startProxy(model);
305
+
306
+ // 5. Loyiha topish
307
+ log('šŸ”', 'NexusAI loyiha qidirilmoqda...', C.cyan);
308
+ const projectDir = findProject();
309
+
310
+ if (!projectDir) {
311
+ log('āŒ', 'NexusAI loyiha topilmadi!', C.red);
312
+ log('šŸ’”', 'Loyiha papkasida turib avg-nexus buyrug\'ini bering', C.yellow);
313
+ process.exit(1);
314
+ }
315
+ log('āœ…', `Loyiha: ${projectDir}`, C.green);
316
+
317
+ // 6. Next.js ishga tushirish
318
+ await startNextJS(projectDir);
319
+
320
+ log('', `\n${C.bold}${C.green}šŸŽ‰ NexusAI tayyor!${C.reset}`, '');
321
+ log('🌐', 'http://localhost:3000', C.cyan);
322
+ log('', `${C.dim}To\'xtatish uchun: Ctrl+C${C.reset}\n`, '');
323
+
324
+ // Jarayon tirik qolsin
325
+ process.on('SIGINT', () => {
326
+ log('šŸ‘‹', 'NexusAI to\'xtatildi', C.yellow);
327
+ process.exit(0);
328
+ });
244
329
  }
245
330
 
246
331
  main().catch(err => {
247
- console.error('āŒ Xato:', err.message);
332
+ log('āŒ', `Xato: ${err.message}`, C.red);
248
333
  process.exit(1);
249
- });
334
+ });
package/package.json CHANGED
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "name": "avg-nexus",
3
- "version": "1.0.5",
4
- "description": "Offline proxy for NexusAI Team — runs your site with local Ollama instead of OpenRouter",
5
- "main": "lib/index.js",
3
+ "version": "1.0.7",
4
+ "description": "Offline/Online proxy for NexusAI Team — runs with local Ollama",
5
+ "main": "bin/avg-nexus.js",
6
+ "type": "commonjs",
6
7
  "bin": {
7
- "avg-nexus": "bin/avg-nexus.js"
8
+ "avg-nexus": "./bin/avg-nexus.js"
8
9
  },
9
10
  "scripts": {
10
- "test": "node bin/avg-nexus.js"
11
+ "start": "node bin/avg-nexus.js"
11
12
  },
12
13
  "dependencies": {
13
- "http-proxy": "^1.18.1",
14
14
  "node-fetch": "^2.7.0"
15
15
  },
16
16
  "keywords": [
@@ -18,11 +18,12 @@
18
18
  "openrouter",
19
19
  "proxy",
20
20
  "nexusai",
21
- "offline"
21
+ "offline",
22
+ "ai"
22
23
  ],
24
+ "author": "avg1002",
23
25
  "license": "MIT",
24
26
  "engines": {
25
27
  "node": ">=18.0.0"
26
- },
27
- "type": "commonjs"
28
+ }
28
29
  }