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