npm-noxyai 1.0.14 → 1.0.16

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/index-noxyai.js +89 -259
  2. 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
- openBrowser(verificationUrl);
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
- try {
113
- const pollRes = await fetch(BASE_URL + '/api/cli/poll', {
114
- method: 'POST',
115
- headers: { 'Content-Type': 'application/json' },
116
- body: JSON.stringify({ deviceCode })
117
- });
118
-
119
- const data = await pollRes.json();
120
-
121
- if (data.status === 'success') {
122
- clearInterval(pollInterval);
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(`\n\x1b[36m[i] Server detected. Running in foreground. Press Ctrl+C to stop.\x1b[0m`);
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
  }
@@ -168,147 +96,109 @@ function runTerminalCommand(cmd) {
168
96
  function getLocalContext() {
169
97
  try {
170
98
  const files = fs.readdirSync(process.cwd()).filter(f => !f.startsWith('node_modules') && !f.startsWith('.git')).slice(0, 30);
171
- return `\n[SYSTEM: You are in ${process.cwd()}. Files here: ${files.join(', ')}]\n`;
99
+ return `\n[SYSTEM CONTEXT: You are in ${process.cwd()}. Files here: ${files.join(', ')}]\n`;
172
100
  } catch (e) { return ""; }
173
101
  }
174
102
 
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. Stopping.'); return; }
106
+ if (depth > 5) { console.error('\n❌ Reached max reasoning depth. Stopping to prevent loop.'); 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
- if (!res.ok) {
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
- // Reasoning in GREEN
232
- if (parsed.reasoning) {
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
+ // 🚨 EXPLICIT TOKEN BILLING READOUT
143
+ console.log(`\n\n\x1b[33m💳 Tokens Used: ${parsed.tokens} | Credits Deducted: ${parsed.cost}\x1b[0m`);
244
144
  }
245
145
  } catch (e) {}
246
146
  }
247
147
  }
248
148
  }
249
149
 
250
- console.log('\n');
251
-
252
- // ONLY process agent actions if agent mode is ON AND there are action tags
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');
150
+ if (!config.agentMode) { console.log(); return; }
151
+
152
+ console.log('\n\x1b[32m[Agent] Processing actions...\x1b[0m');
264
153
  let agentFeedback = "";
265
154
 
266
- // Tool: Read Files
267
- const readRegex = /<read>([\s\S]*?)<\/read>/g;
268
- let match;
155
+ const readRegex = /<read>([\s\S]*?)<\/read>/g; let match;
269
156
  while ((match = readRegex.exec(fullResponse)) !== null) {
270
157
  const filePath = match[1].trim();
271
158
  try {
272
159
  const content = fs.readFileSync(filePath, 'utf8');
273
160
  console.log(`\x1b[34m📖 Read file:\x1b[0m ${filePath}`);
274
161
  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
- }
162
+ } catch (err) { agentFeedback += `\n[ERROR reading ${filePath}: ${err.message}]\n`; }
279
163
  }
280
164
 
281
- // Tool: Search Web with sources
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 searchData = await searchWeb(query);
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
+
176
+ // 🚨 SAFE JSON PARSING TO PREVENT HTML CRASHES
177
+ const responseText = await searchRes.text();
178
+ let searchData;
179
+ try {
180
+ searchData = JSON.parse(responseText);
181
+ } catch (parseError) {
182
+ throw new Error("Search backend returned an invalid response (Likely a 500 error or missing Vercel environment variables).");
183
+ }
184
+
185
+ if (!searchRes.ok) throw new Error(searchData.error || "Search failed");
186
+
187
+ let snippets = "No results found.";
288
188
  if (searchData.items) {
289
- searchSources = searchData.items.slice(0, 5).map(i => ({
290
- title: i.title,
291
- link: i.link,
292
- snippet: i.snippet
293
- }));
294
-
295
- console.log(`\x1b[35m📚 Sources found:\x1b[0m`);
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`;
189
+ console.log(`\x1b[32m📚 Sources Extracted:\x1b[0m`);
190
+ snippets = searchData.items.map(i => {
191
+ console.log(` - ${i.title}\n \x1b[31m${i.link}\x1b[0m`);
192
+ return `- Title: ${i.title}\n Snippet: ${i.snippet}\n URL: ${i.link}`;
193
+ }).join('\n\n');
305
194
  }
306
- } catch (err) {
307
- agentFeedback += `\n[SEARCH FAILED: ${err.message}]\n`;
195
+ agentFeedback += `\n[SEARCH RESULTS FOR "${query}"]\n${snippets}\n`;
196
+ } catch (err) {
197
+ console.log(`\x1b[31m❌ Search Failed:\x1b[0m ${err.message}`);
198
+ agentFeedback += `\n[SEARCH FAILED: ${err.message}. Tell the user the search tool is currently unavailable.]\n`;
308
199
  }
309
200
  }
310
201
 
311
- // Tool: Write Files
312
202
  const fileRegex = /<file path="([^"]+)">([\s\S]*?)<\/file>/g;
313
203
  while ((match = fileRegex.exec(fullResponse)) !== null) {
314
204
  const filePath = match[1], content = match[2];
@@ -318,23 +208,17 @@ async function chat(prompt, depth = 0) {
318
208
  console.log(`\x1b[32m✔ Wrote file:\x1b[0m ${filePath}`);
319
209
  }
320
210
 
321
- // Tool: Execute Commands
322
211
  const execRegex = /<execute>([\s\S]*?)<\/execute>/g;
323
212
  const commands = [];
324
213
  while ((match = execRegex.exec(fullResponse)) !== null) commands.push(match[1].trim());
325
-
326
214
  for (const cmd of commands) {
327
- try {
328
- await runTerminalCommand(cmd);
329
- } catch (err) {
330
- console.log(`\x1b[31m❌ Command Failed:\x1b[0m ${err}`);
331
- agentFeedback += `\n[COMMAND ERROR running "${cmd}": ${err}]\n`;
332
- }
215
+ try { await runTerminalCommand(cmd); }
216
+ catch (err) { console.log(`\x1b[31m❌ Command Failed:\x1b[0m ${err}`); agentFeedback += `\n[COMMAND ERROR running "${cmd}": ${err}]\n`; }
333
217
  }
334
218
 
335
219
  if (agentFeedback) {
336
- console.log(`\n\x1b[33m🔄 Sending data back to Agent...\x1b[0m`);
337
- await chat(`Here are the results of your actions:\n${agentFeedback}\nWhat is the next step? Output <file> or <execute> if ready, or <read>/<search> if you need more info.`, depth + 1);
220
+ console.log(`\x1b[33m🔄 Sending data back to Agent...\x1b[0m`);
221
+ await chat(`Action results:\n${agentFeedback}\nNext step? Output <file> or <execute> if ready, or <read>/<search>.`, depth + 1);
338
222
  return;
339
223
  }
340
224
 
@@ -342,106 +226,52 @@ async function chat(prompt, depth = 0) {
342
226
  console.log(`\x1b[32m✨ Task Completed!\x1b[0m`);
343
227
  console.log('\x1b[32m════════════════════════════════════════\x1b[0m\n');
344
228
 
345
- } catch (error) {
346
- stopSpinner();
347
- console.error('\n❌ Connection error: ' + error.message);
348
- }
229
+ } catch (error) { stopSpinner(); console.error('\n❌ Connection error: ' + error.message); }
349
230
  }
350
231
 
351
232
  function startInteractiveMode() {
352
233
  const config = loadConfig();
353
234
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
354
235
  printLogo();
355
-
356
- const agentStatus = config.agentMode ? '\x1b[32mON\x1b[0m' : '\x1b[31mOFF\x1b[0m';
357
-
358
236
  console.log(`\x1b[33mWelcome to NoxyAI Interactive Mode!\x1b[0m`);
359
- console.log(`Current Model: \x1b[32m${config.model === 'auto' ? 'Mamba-Codestral 7B' : config.model}\x1b[0m`);
360
- console.log(`Agent Mode: ${agentStatus}`);
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`);
237
+ console.log(`Model: \x1b[32m${config.model}\x1b[0m | Agent Mode: ${config.agentMode ? '\x1b[32mON\x1b[0m' : '\x1b[31mOFF\x1b[0m'}`);
238
+ 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
239
 
368
240
  function ask() {
369
- const prompt = config.agentMode ? '\x1b[36mNoxyAI [Agent] > \x1b[0m' : '\x1b[36mNoxyAI [Chat] > \x1b[0m';
370
- rl.question(prompt, async (input) => {
241
+ rl.question('\x1b[36mNoxyAI > \x1b[0m', async (input) => {
371
242
  const trimmed = input.trim();
372
243
  const lower = trimmed.toLowerCase();
373
244
 
374
- if (lower === 'exit' || lower === '/exit') {
375
- console.log('\n\x1b[36mGoodbye! 👋\x1b[0m\n');
376
- process.exit(0);
377
- }
378
- else if (lower === '/clear') {
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
- }
245
+ if (lower === 'exit' || lower === '/exit') process.exit(0);
246
+ else if (lower === '/clear') { console.clear(); printLogo(); ask(); }
247
+ else if (lower === '/agent') {
248
+ const c = loadConfig(); c.agentMode = !c.agentMode; saveConfig(c);
249
+ console.log(`\n\x1b[33mAgent Mode is now ${c.agentMode ? '\x1b[32mON (File/Web access enabled)' : '\x1b[31mOFF (Standard Chat)'}\x1b[0m\n`);
397
250
  ask();
398
251
  }
399
252
  else if (lower === '/model') {
400
253
  console.log('\n\x1b[33mSelect an AI Model:\x1b[0m');
401
- console.log(' \x1b[36m1)\x1b[0m Auto (Mamba-Codestral 7B)');
402
- console.log(' \x1b[36m2)\x1b[0m Qwen3 80B \x1b[32m[Reasoning in Green]\x1b[0m');
403
- console.log(' \x1b[36m3)\x1b[0m Mamba-Codestral 7B');
404
-
254
+ console.log(' \x1b[36m1)\x1b[0m Auto (Llama 3.3 70B)');
255
+ console.log(' \x1b[36m2)\x1b[0m Qwen3 Next 80B (Deep Reasoning)');
256
+ console.log(' \x1b[36m3)\x1b[0m Mistral Mamba Codestral 7B');
405
257
  rl.question('\nEnter number (1-3): ', (choice) => {
406
258
  let selected = 'auto';
407
259
  if (choice === '2') selected = 'Qwen3';
408
- if (choice === '3') selected = 'Mamba-Codestral';
409
-
410
- config.model = selected;
411
- saveConfig(config);
412
-
413
- console.log(`\n\x1b[32m✔ Model changed to: ${selected}\x1b[0m\n`);
414
- ask();
260
+ if (choice === '3') selected = 'Mamba';
261
+ const c = loadConfig(); c.model = selected; saveConfig(c);
262
+ console.log(`\n\x1b[32m✔ Model changed to: ${selected}\x1b[0m\n`); ask();
415
263
  });
416
264
  return;
417
265
  }
418
- else if (trimmed) {
419
- await chat(trimmed);
420
- ask();
421
- } else {
422
- ask();
423
- }
266
+ else if (trimmed) { await chat(trimmed); ask(); }
267
+ else ask();
424
268
  });
425
269
  }
426
270
  ask();
427
271
  }
428
272
 
429
- // Command handling
430
273
  if (command === 'login') login();
431
- else if (command === 'logout') 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
- }
274
+ else if (command === 'logout') { const c = loadConfig(); c.token = null; saveConfig(c); console.log('Logged out.'); }
275
+ else if (command === 'help' || command === '--help') { printLogo(); console.log(` \x1b[32mnoxyai\x1b[0m Start Interactive Mode\n`); }
443
276
  else if (!command) startInteractiveMode();
444
- else {
445
- console.error(`❌ Unknown command. Run "noxyai help"`);
446
- process.exit(1);
447
- }
277
+ else { console.error(`❌ Unknown command.`); process.exit(1); }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "npm-noxyai",
3
- "version": "1.0.14",
3
+ "version": "1.0.16",
4
4
  "description": "CLI for NoxyAI",
5
5
  "main": "index-noxyai.js",
6
6
  "bin": {