npm-noxyai 1.0.13 → 1.0.15
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/index-noxyai.js +78 -259
- package/package.json +1 -1
package/index-noxyai.js
CHANGED
|
@@ -12,27 +12,14 @@ const CONFIG_FILE = path.join(os.homedir(), '.noxyai.json');
|
|
|
12
12
|
const args = process.argv.slice(2);
|
|
13
13
|
const command = args[0];
|
|
14
14
|
|
|
15
|
-
// --- CONFIG MANAGEMENT ---
|
|
16
15
|
function loadConfig() {
|
|
17
16
|
if (fs.existsSync(CONFIG_FILE)) {
|
|
18
|
-
try {
|
|
19
|
-
const config = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
|
|
20
|
-
if (config.agentMode === undefined) config.agentMode = false; // Default OFF
|
|
21
|
-
return config;
|
|
22
|
-
} catch(e) {}
|
|
17
|
+
try { return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8')); } catch(e){}
|
|
23
18
|
}
|
|
24
|
-
return {
|
|
25
|
-
token: null,
|
|
26
|
-
model: 'auto',
|
|
27
|
-
agentMode: false // Default to Chat mode
|
|
28
|
-
};
|
|
19
|
+
return { token: null, model: 'auto', agentMode: false };
|
|
29
20
|
}
|
|
21
|
+
function saveConfig(config) { fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2)); }
|
|
30
22
|
|
|
31
|
-
function saveConfig(config) {
|
|
32
|
-
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// --- UI HELPERS ---
|
|
36
23
|
function printLogo() {
|
|
37
24
|
console.log('\x1b[36m' + `
|
|
38
25
|
███╗ ██╗ ██████╗ ██╗ ██╗██╗ ██╗ █████╗ ██╗
|
|
@@ -48,100 +35,48 @@ let spinnerInterval;
|
|
|
48
35
|
function startSpinner(text) {
|
|
49
36
|
const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
50
37
|
let i = 0;
|
|
51
|
-
process.stdout.write('\x1b[?25l');
|
|
38
|
+
process.stdout.write('\x1b[?25l');
|
|
52
39
|
spinnerInterval = setInterval(() => {
|
|
53
40
|
process.stdout.write(`\r\x1b[36m${frames[i]} ${text}\x1b[0m`);
|
|
54
41
|
i = (i + 1) % frames.length;
|
|
55
42
|
}, 80);
|
|
56
43
|
}
|
|
57
|
-
|
|
58
44
|
function stopSpinner() {
|
|
59
45
|
clearInterval(spinnerInterval);
|
|
60
46
|
process.stdout.write('\r\x1b[K\x1b[?25h');
|
|
61
47
|
}
|
|
62
48
|
|
|
63
|
-
function logout() {
|
|
64
|
-
const config = loadConfig();
|
|
65
|
-
config.token = null;
|
|
66
|
-
saveConfig(config);
|
|
67
|
-
console.log('✅ Successfully logged out.');
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function openBrowser(url) {
|
|
71
|
-
const platform = os.platform();
|
|
72
|
-
if (platform === 'android') spawn('termux-open-url', [url], { stdio: 'ignore' });
|
|
73
|
-
else if (platform === 'darwin') spawn('open', [url], { stdio: 'ignore' });
|
|
74
|
-
else if (platform === 'win32') spawn('cmd.exe', ['/c', 'start', '""', url], { stdio: 'ignore' });
|
|
75
|
-
else spawn('xdg-open', [url], { stdio: 'ignore' });
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// --- SEARCH FUNCTION ---
|
|
79
|
-
async function searchWeb(query) {
|
|
80
|
-
try {
|
|
81
|
-
const config = loadConfig();
|
|
82
|
-
const res = await fetch(`${BASE_URL}/api/io?action=search&q=${encodeURIComponent(query)}`, {
|
|
83
|
-
headers: { 'Authorization': 'Bearer ' + config.token }
|
|
84
|
-
});
|
|
85
|
-
const data = await res.json();
|
|
86
|
-
return data;
|
|
87
|
-
} catch (err) {
|
|
88
|
-
return { error: err.message };
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// --- CORE FUNCTIONS ---
|
|
93
49
|
async function login() {
|
|
94
50
|
printLogo();
|
|
95
51
|
console.log('Initializing secure login...\n');
|
|
96
52
|
try {
|
|
97
53
|
const initRes = await fetch(BASE_URL + '/api/cli/init', { method: 'POST' });
|
|
98
54
|
const { deviceCode, verificationUrl, interval } = await initRes.json();
|
|
99
|
-
|
|
100
|
-
console.log('=============================================');
|
|
101
|
-
console.log(`URL: ${verificationUrl}`);
|
|
102
|
-
console.log('=============================================\n');
|
|
55
|
+
console.log(`URL: ${verificationUrl}\n`);
|
|
103
56
|
|
|
104
57
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
105
58
|
rl.question('Press ENTER to automatically open the browser...', () => {
|
|
106
|
-
|
|
59
|
+
const platform = os.platform();
|
|
60
|
+
if (platform === 'android') spawn('termux-open-url', [verificationUrl], { stdio: 'ignore' });
|
|
61
|
+
else spawn('xdg-open', [verificationUrl], { stdio: 'ignore' });
|
|
107
62
|
rl.close();
|
|
108
63
|
startSpinner('Waiting for authentication...');
|
|
109
64
|
});
|
|
110
65
|
|
|
111
66
|
const pollInterval = setInterval(async () => {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
stopSpinner();
|
|
124
|
-
const config = loadConfig();
|
|
125
|
-
config.token = data.token;
|
|
126
|
-
saveConfig(config);
|
|
127
|
-
|
|
128
|
-
console.clear();
|
|
129
|
-
printLogo();
|
|
130
|
-
console.log('✅ Login successful! Terminal connected.');
|
|
131
|
-
console.log('Type "noxyai" to start interactive mode.\n');
|
|
132
|
-
process.exit(0);
|
|
133
|
-
} else if (data.error) {
|
|
134
|
-
clearInterval(pollInterval);
|
|
135
|
-
stopSpinner();
|
|
136
|
-
console.error('\n❌ Login failed: ' + data.error);
|
|
137
|
-
process.exit(1);
|
|
138
|
-
}
|
|
139
|
-
} catch (e) {}
|
|
67
|
+
const pollRes = await fetch(BASE_URL + '/api/cli/poll', { method: 'POST', body: JSON.stringify({ deviceCode }) });
|
|
68
|
+
const data = await pollRes.json();
|
|
69
|
+
if (data.status === 'success') {
|
|
70
|
+
clearInterval(pollInterval); stopSpinner();
|
|
71
|
+
const config = loadConfig(); config.token = data.token; saveConfig(config);
|
|
72
|
+
console.clear(); printLogo();
|
|
73
|
+
console.log('✅ Login successful! Type "noxyai" to start.\n'); process.exit(0);
|
|
74
|
+
} else if (data.error) {
|
|
75
|
+
clearInterval(pollInterval); stopSpinner();
|
|
76
|
+
console.error('\n❌ Login failed: ' + data.error); process.exit(1);
|
|
77
|
+
}
|
|
140
78
|
}, interval * 1000);
|
|
141
|
-
|
|
142
|
-
} catch (error) {
|
|
143
|
-
console.error('Failed to connect to NoxyAI server.', error.message);
|
|
144
|
-
}
|
|
79
|
+
} catch (error) { console.error('Failed to connect.', error.message); }
|
|
145
80
|
}
|
|
146
81
|
|
|
147
82
|
function runTerminalCommand(cmd) {
|
|
@@ -149,18 +84,11 @@ function runTerminalCommand(cmd) {
|
|
|
149
84
|
console.log(`\n\x1b[33m⚡ Running:\x1b[0m ${cmd}\n`);
|
|
150
85
|
const isServer = cmd.includes('dev') || cmd.includes('serve') || cmd.includes('host') || cmd.includes('start');
|
|
151
86
|
const child = spawn(cmd, { shell: true, stdio: 'inherit' });
|
|
152
|
-
|
|
153
87
|
if (isServer) {
|
|
154
|
-
console.log(`\
|
|
88
|
+
console.log(`\x1b[36m[i] Server detected. Running in foreground. Press Ctrl+C to stop.\x1b[0m`);
|
|
155
89
|
setTimeout(() => resolve(), 2500);
|
|
156
90
|
}
|
|
157
|
-
|
|
158
|
-
child.on('close', (code) => {
|
|
159
|
-
if (!isServer) {
|
|
160
|
-
if (code !== 0) reject(`Command failed with exit code ${code}`);
|
|
161
|
-
else resolve();
|
|
162
|
-
}
|
|
163
|
-
});
|
|
91
|
+
child.on('close', (code) => { if (!isServer) { if (code !== 0) reject(`Exit code ${code}`); else resolve(); } });
|
|
164
92
|
child.on('error', (error) => reject(error.message));
|
|
165
93
|
});
|
|
166
94
|
}
|
|
@@ -175,140 +103,91 @@ function getLocalContext() {
|
|
|
175
103
|
async function chat(prompt, depth = 0) {
|
|
176
104
|
const config = loadConfig();
|
|
177
105
|
if (!config.token) { console.error('❌ Unauthorized: Run "noxyai login"'); return; }
|
|
178
|
-
if (depth > 6) { console.error('\n❌ Reached max reasoning depth.
|
|
106
|
+
if (depth > 6) { console.error('\n❌ Reached max reasoning depth.'); return; }
|
|
179
107
|
|
|
180
|
-
// Only add file context in Agent Mode
|
|
181
108
|
const enhancedPrompt = (depth === 0 && config.agentMode) ? getLocalContext() + prompt : prompt;
|
|
182
|
-
|
|
183
109
|
startSpinner(depth === 0 ? 'NoxyAI is thinking...' : 'NoxyAI is analyzing results...');
|
|
184
110
|
|
|
185
111
|
try {
|
|
186
112
|
const res = await fetch(BASE_URL + '/api/io', {
|
|
187
113
|
method: 'POST',
|
|
188
114
|
headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + config.token },
|
|
189
|
-
body: JSON.stringify({
|
|
190
|
-
prompt: enhancedPrompt,
|
|
191
|
-
model: config.model,
|
|
192
|
-
agentMode: config.agentMode
|
|
193
|
-
})
|
|
115
|
+
body: JSON.stringify({ prompt: enhancedPrompt, model: config.model, agentMode: config.agentMode })
|
|
194
116
|
});
|
|
195
117
|
|
|
196
118
|
stopSpinner();
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
const errorText = await res.text();
|
|
200
|
-
if (res.status === 402) {
|
|
201
|
-
console.log('\n\x1b[31m' + errorText + '\x1b[0m');
|
|
202
|
-
return;
|
|
203
|
-
}
|
|
204
|
-
return console.error('\n❌ API Error: ' + errorText);
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
const modelDisplay = config.model === 'auto' ? 'Mamba-Codestral 7B' :
|
|
208
|
-
config.model === 'Qwen3' ? 'Qwen3 80B' : 'Mamba-Codestral 7B';
|
|
209
|
-
const agentStatus = config.agentMode ? '🤖 Agent' : '💬 Chat';
|
|
210
|
-
console.log(`\n${agentStatus} \x1b[36mNoxyAI (${modelDisplay}):\x1b[0m\n`);
|
|
119
|
+
if (!res.ok) return console.error('\n❌ API Error: ' + await res.text());
|
|
120
|
+
console.log(`\n🤖 \x1b[36mNoxyAI (${config.model}):\x1b[0m\n`);
|
|
211
121
|
|
|
212
122
|
const reader = res.body.getReader();
|
|
213
123
|
const decoder = new TextDecoder('utf-8');
|
|
214
124
|
let fullResponse = "";
|
|
215
|
-
let usage = null;
|
|
216
|
-
let searchSources = [];
|
|
217
125
|
|
|
218
126
|
while (true) {
|
|
219
127
|
const { done, value } = await reader.read();
|
|
220
128
|
if (done) break;
|
|
221
|
-
|
|
222
129
|
const lines = decoder.decode(value, { stream: true }).split('\n');
|
|
223
130
|
for (const line of lines) {
|
|
224
131
|
if (line.startsWith('data: ')) {
|
|
225
132
|
const data = line.slice(6).trim();
|
|
226
133
|
if (data === '[DONE]') break;
|
|
227
|
-
|
|
228
134
|
try {
|
|
229
135
|
const parsed = JSON.parse(data);
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
if (parsed.
|
|
233
|
-
process.stdout.write('\x1b[32m' + parsed.reasoning + '\x1b[0m');
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
// Normal response
|
|
237
|
-
if (parsed.text) {
|
|
136
|
+
if (parsed.isReasoning) {
|
|
137
|
+
process.stdout.write('\x1b[31m' + parsed.text + '\x1b[0m');
|
|
138
|
+
} else if (parsed.text) {
|
|
238
139
|
fullResponse += parsed.text;
|
|
239
140
|
process.stdout.write(parsed.text);
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
if (parsed.usage) {
|
|
243
|
-
usage = parsed.usage;
|
|
141
|
+
} else if (parsed.cost) {
|
|
142
|
+
console.log(`\n\n\x1b[33m💳 Tokens Used: ${Math.round(parsed.cost/10)} | Credits Deducted: ${parsed.cost}\x1b[0m`);
|
|
244
143
|
}
|
|
245
144
|
} catch (e) {}
|
|
246
145
|
}
|
|
247
146
|
}
|
|
248
147
|
}
|
|
249
148
|
|
|
250
|
-
console.log(
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
if (!config.agentMode) {
|
|
254
|
-
return;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
// Check if response contains action tags before processing
|
|
258
|
-
const hasActionTags = /<read>|<search>|<file path=|<execute>/.test(fullResponse);
|
|
259
|
-
if (!hasActionTags) {
|
|
260
|
-
return;
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
console.log('\x1b[32m[Agent] Processing actions...\x1b[0m');
|
|
149
|
+
if (!config.agentMode) { console.log(); return; }
|
|
150
|
+
|
|
151
|
+
console.log('\n\x1b[32m[Agent] Processing actions...\x1b[0m');
|
|
264
152
|
let agentFeedback = "";
|
|
265
153
|
|
|
266
|
-
|
|
267
|
-
const readRegex = /<read>([\s\S]*?)<\/read>/g;
|
|
268
|
-
let match;
|
|
154
|
+
const readRegex = /<read>([\s\S]*?)<\/read>/g; let match;
|
|
269
155
|
while ((match = readRegex.exec(fullResponse)) !== null) {
|
|
270
156
|
const filePath = match[1].trim();
|
|
271
157
|
try {
|
|
272
158
|
const content = fs.readFileSync(filePath, 'utf8');
|
|
273
159
|
console.log(`\x1b[34m📖 Read file:\x1b[0m ${filePath}`);
|
|
274
160
|
agentFeedback += `\n[FILE: ${filePath}]\n\`\`\`\n${content}\n\`\`\`\n`;
|
|
275
|
-
} catch (err) {
|
|
276
|
-
console.log(`\x1b[31m❌ Failed to read:\x1b[0m ${filePath}`);
|
|
277
|
-
agentFeedback += `\n[ERROR reading ${filePath}: ${err.message}]\n`;
|
|
278
|
-
}
|
|
161
|
+
} catch (err) { agentFeedback += `\n[ERROR reading ${filePath}: ${err.message}]\n`; }
|
|
279
162
|
}
|
|
280
163
|
|
|
281
|
-
//
|
|
164
|
+
// 🚨 UPDATED WEB SEARCH TOOL
|
|
282
165
|
const searchRegex = /<search>([\s\S]*?)<\/search>/g;
|
|
283
166
|
while ((match = searchRegex.exec(fullResponse)) !== null) {
|
|
284
167
|
const query = match[1].trim();
|
|
285
168
|
console.log(`\x1b[35m🔍 Searching Web:\x1b[0m ${query}`);
|
|
286
169
|
try {
|
|
287
|
-
const
|
|
170
|
+
const searchRes = await fetch(BASE_URL + '/api/search', {
|
|
171
|
+
method: 'POST',
|
|
172
|
+
headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + config.token },
|
|
173
|
+
body: JSON.stringify({ query })
|
|
174
|
+
});
|
|
175
|
+
const searchData = await searchRes.json();
|
|
176
|
+
|
|
177
|
+
let snippets = "No results.";
|
|
288
178
|
if (searchData.items) {
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
searchSources.forEach((s, i) => {
|
|
297
|
-
console.log(` \x1b[36m${i+1}. ${s.title}\x1b[0m`);
|
|
298
|
-
console.log(` \x1b[90m${s.link}\x1b[0m`);
|
|
299
|
-
});
|
|
300
|
-
|
|
301
|
-
const snippets = searchData.items.map(i => `- ${i.title}: ${i.snippet}\n Source: ${i.link}`).join('\n');
|
|
302
|
-
agentFeedback += `\n[SEARCH RESULTS FOR "${query}"]\n${snippets}\n`;
|
|
303
|
-
} else {
|
|
304
|
-
agentFeedback += `\n[SEARCH RESULTS FOR "${query}"]\nNo results found.\n`;
|
|
179
|
+
console.log(`\x1b[32m📚 Sources Extracted:\x1b[0m`);
|
|
180
|
+
snippets = searchData.items.map(i => {
|
|
181
|
+
// Print directly to terminal with RED URL
|
|
182
|
+
console.log(` - ${i.title}\n \x1b[31m${i.link}\x1b[0m`);
|
|
183
|
+
// Send the URL context back to the AI
|
|
184
|
+
return `- Title: ${i.title}\n Snippet: ${i.snippet}\n URL: ${i.link}`;
|
|
185
|
+
}).join('\n\n');
|
|
305
186
|
}
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
}
|
|
187
|
+
agentFeedback += `\n[SEARCH RESULTS FOR "${query}"]\n${snippets}\n(Please summarize and cite these URLs.)\n`;
|
|
188
|
+
} catch (err) { agentFeedback += `\n[SEARCH FAILED: ${err.message}]\n`; }
|
|
309
189
|
}
|
|
310
190
|
|
|
311
|
-
// Tool: Write Files
|
|
312
191
|
const fileRegex = /<file path="([^"]+)">([\s\S]*?)<\/file>/g;
|
|
313
192
|
while ((match = fileRegex.exec(fullResponse)) !== null) {
|
|
314
193
|
const filePath = match[1], content = match[2];
|
|
@@ -318,23 +197,17 @@ async function chat(prompt, depth = 0) {
|
|
|
318
197
|
console.log(`\x1b[32m✔ Wrote file:\x1b[0m ${filePath}`);
|
|
319
198
|
}
|
|
320
199
|
|
|
321
|
-
// Tool: Execute Commands
|
|
322
200
|
const execRegex = /<execute>([\s\S]*?)<\/execute>/g;
|
|
323
201
|
const commands = [];
|
|
324
202
|
while ((match = execRegex.exec(fullResponse)) !== null) commands.push(match[1].trim());
|
|
325
|
-
|
|
326
203
|
for (const cmd of commands) {
|
|
327
|
-
try {
|
|
328
|
-
|
|
329
|
-
} catch (err) {
|
|
330
|
-
console.log(`\x1b[31m❌ Command Failed:\x1b[0m ${err}`);
|
|
331
|
-
agentFeedback += `\n[COMMAND ERROR running "${cmd}": ${err}]\n`;
|
|
332
|
-
}
|
|
204
|
+
try { await runTerminalCommand(cmd); }
|
|
205
|
+
catch (err) { console.log(`\x1b[31m❌ Command Failed:\x1b[0m ${err}`); agentFeedback += `\n[COMMAND ERROR running "${cmd}": ${err}]\n`; }
|
|
333
206
|
}
|
|
334
207
|
|
|
335
208
|
if (agentFeedback) {
|
|
336
|
-
console.log(`\
|
|
337
|
-
await chat(`
|
|
209
|
+
console.log(`\x1b[33m🔄 Sending data back to Agent...\x1b[0m`);
|
|
210
|
+
await chat(`Action results:\n${agentFeedback}\nNext step? Output <file> or <execute> if ready, or <read>/<search>.`, depth + 1);
|
|
338
211
|
return;
|
|
339
212
|
}
|
|
340
213
|
|
|
@@ -342,106 +215,52 @@ async function chat(prompt, depth = 0) {
|
|
|
342
215
|
console.log(`\x1b[32m✨ Task Completed!\x1b[0m`);
|
|
343
216
|
console.log('\x1b[32m════════════════════════════════════════\x1b[0m\n');
|
|
344
217
|
|
|
345
|
-
} catch (error) {
|
|
346
|
-
stopSpinner();
|
|
347
|
-
console.error('\n❌ Connection error: ' + error.message);
|
|
348
|
-
}
|
|
218
|
+
} catch (error) { stopSpinner(); console.error('\n❌ Connection error: ' + error.message); }
|
|
349
219
|
}
|
|
350
220
|
|
|
351
221
|
function startInteractiveMode() {
|
|
352
222
|
const config = loadConfig();
|
|
353
223
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
354
224
|
printLogo();
|
|
355
|
-
|
|
356
|
-
const agentStatus = config.agentMode ? '\x1b[32mON\x1b[0m' : '\x1b[31mOFF\x1b[0m';
|
|
357
|
-
|
|
358
225
|
console.log(`\x1b[33mWelcome to NoxyAI Interactive Mode!\x1b[0m`);
|
|
359
|
-
console.log(`
|
|
360
|
-
console.log(`
|
|
361
|
-
console.log(`\nCommands:`);
|
|
362
|
-
console.log(` \x1b[36m/agent\x1b[0m - Toggle Agent Mode (file/code operations)`);
|
|
363
|
-
console.log(` \x1b[36m/model\x1b[0m - Change AI Model`);
|
|
364
|
-
console.log(` \x1b[36m/clear\x1b[0m - Clear screen`);
|
|
365
|
-
console.log(` \x1b[36m/exit\x1b[0m - Exit\n`);
|
|
366
|
-
console.log(`\x1b[90mTip: In Chat mode, AI responds normally. In Agent mode, it can read/write files and run commands.\x1b[0m\n`);
|
|
226
|
+
console.log(`Model: \x1b[32m${config.model}\x1b[0m | Agent Mode: ${config.agentMode ? '\x1b[32mON\x1b[0m' : '\x1b[31mOFF\x1b[0m'}`);
|
|
227
|
+
console.log(`Commands: \x1b[36m/model\x1b[0m, \x1b[36m/agent\x1b[0m, \x1b[36m/clear\x1b[0m, \x1b[36m/exit\x1b[0m\n`);
|
|
367
228
|
|
|
368
229
|
function ask() {
|
|
369
|
-
|
|
370
|
-
rl.question(prompt, async (input) => {
|
|
230
|
+
rl.question('\x1b[36mNoxyAI > \x1b[0m', async (input) => {
|
|
371
231
|
const trimmed = input.trim();
|
|
372
232
|
const lower = trimmed.toLowerCase();
|
|
373
233
|
|
|
374
|
-
if (lower === 'exit' || lower === '/exit')
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
console.clear();
|
|
380
|
-
printLogo();
|
|
381
|
-
ask();
|
|
382
|
-
}
|
|
383
|
-
else if (lower === '/agent' || lower === '/agent on' || lower === '/agent off') {
|
|
384
|
-
config.agentMode = !config.agentMode;
|
|
385
|
-
saveConfig(config);
|
|
386
|
-
const status = config.agentMode ? '\x1b[32mON\x1b[0m' : '\x1b[31mOFF\x1b[0m';
|
|
387
|
-
console.log(`\n\x1b[33m🤖 Agent Mode: ${status}\x1b[0m`);
|
|
388
|
-
if (config.agentMode) {
|
|
389
|
-
console.log('\x1b[36mAgent mode features:\x1b[0m');
|
|
390
|
-
console.log(' • Read & write files');
|
|
391
|
-
console.log(' • Execute terminal commands');
|
|
392
|
-
console.log(' • Web search integration');
|
|
393
|
-
console.log(' • Multi-step autonomous tasks\n');
|
|
394
|
-
} else {
|
|
395
|
-
console.log('\x1b[36mChat mode: Normal AI conversation without file access\x1b[0m\n');
|
|
396
|
-
}
|
|
234
|
+
if (lower === 'exit' || lower === '/exit') process.exit(0);
|
|
235
|
+
else if (lower === '/clear') { console.clear(); printLogo(); ask(); }
|
|
236
|
+
else if (lower === '/agent') {
|
|
237
|
+
const c = loadConfig(); c.agentMode = !c.agentMode; saveConfig(c);
|
|
238
|
+
console.log(`\n\x1b[33mAgent Mode is now ${c.agentMode ? '\x1b[32mON (File/Web access enabled)' : '\x1b[31mOFF (Standard Chat)'}\x1b[0m\n`);
|
|
397
239
|
ask();
|
|
398
240
|
}
|
|
399
241
|
else if (lower === '/model') {
|
|
400
242
|
console.log('\n\x1b[33mSelect an AI Model:\x1b[0m');
|
|
401
|
-
console.log(' \x1b[36m1)\x1b[0m Auto (
|
|
402
|
-
console.log(' \x1b[36m2)\x1b[0m Qwen3 80B
|
|
403
|
-
console.log(' \x1b[36m3)\x1b[0m Mamba
|
|
404
|
-
|
|
243
|
+
console.log(' \x1b[36m1)\x1b[0m Auto (Llama 3.3 70B)');
|
|
244
|
+
console.log(' \x1b[36m2)\x1b[0m Qwen3 Next 80B (Deep Reasoning)');
|
|
245
|
+
console.log(' \x1b[36m3)\x1b[0m Mistral Mamba Codestral 7B');
|
|
405
246
|
rl.question('\nEnter number (1-3): ', (choice) => {
|
|
406
247
|
let selected = 'auto';
|
|
407
248
|
if (choice === '2') selected = 'Qwen3';
|
|
408
|
-
if (choice === '3') selected = 'Mamba
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
saveConfig(config);
|
|
412
|
-
|
|
413
|
-
console.log(`\n\x1b[32m✔ Model changed to: ${selected}\x1b[0m\n`);
|
|
414
|
-
ask();
|
|
249
|
+
if (choice === '3') selected = 'Mamba';
|
|
250
|
+
const c = loadConfig(); c.model = selected; saveConfig(c);
|
|
251
|
+
console.log(`\n\x1b[32m✔ Model changed to: ${selected}\x1b[0m\n`); ask();
|
|
415
252
|
});
|
|
416
253
|
return;
|
|
417
254
|
}
|
|
418
|
-
else if (trimmed) {
|
|
419
|
-
|
|
420
|
-
ask();
|
|
421
|
-
} else {
|
|
422
|
-
ask();
|
|
423
|
-
}
|
|
255
|
+
else if (trimmed) { await chat(trimmed); ask(); }
|
|
256
|
+
else ask();
|
|
424
257
|
});
|
|
425
258
|
}
|
|
426
259
|
ask();
|
|
427
260
|
}
|
|
428
261
|
|
|
429
|
-
// Command handling
|
|
430
262
|
if (command === 'login') login();
|
|
431
|
-
else if (command === 'logout')
|
|
432
|
-
else if (command === 'help' || command === '--help') {
|
|
433
|
-
printLogo();
|
|
434
|
-
console.log(`\n \x1b[32mnoxyai\x1b[0m Start Interactive Mode`);
|
|
435
|
-
console.log(` \x1b[32mnoxyai chat "<prompt>"\x1b[0m Run single prompt`);
|
|
436
|
-
console.log(`\n Interactive Commands:`);
|
|
437
|
-
console.log(` \x1b[36m/agent\x1b[0m Toggle Agent Mode (file/code operations)`);
|
|
438
|
-
console.log(` \x1b[36m/model\x1b[0m Change AI Model\n`);
|
|
439
|
-
}
|
|
440
|
-
else if (command === 'chat') {
|
|
441
|
-
chat(args.slice(1).join(' ')).then(() => process.exit(0));
|
|
442
|
-
}
|
|
263
|
+
else if (command === 'logout') { const c = loadConfig(); c.token = null; saveConfig(c); console.log('Logged out.'); }
|
|
264
|
+
else if (command === 'help' || command === '--help') { printLogo(); console.log(` \x1b[32mnoxyai\x1b[0m Start Interactive Mode\n`); }
|
|
443
265
|
else if (!command) startInteractiveMode();
|
|
444
|
-
else {
|
|
445
|
-
console.error(`❌ Unknown command. Run "noxyai help"`);
|
|
446
|
-
process.exit(1);
|
|
447
|
-
}
|
|
266
|
+
else { console.error(`❌ Unknown command.`); process.exit(1); }
|