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.
Files changed (2) hide show
  1. package/index-noxyai.js +78 -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
  }
@@ -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. Stopping.'); return; }
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
- 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
+ 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('\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');
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
- // Tool: Read Files
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
- // Tool: Search Web with sources
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 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
+ const searchData = await searchRes.json();
176
+
177
+ let snippets = "No results.";
288
178
  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`;
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
- } catch (err) {
307
- agentFeedback += `\n[SEARCH FAILED: ${err.message}]\n`;
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
- 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
- }
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(`\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);
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(`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`);
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
- const prompt = config.agentMode ? '\x1b[36mNoxyAI [Agent] > \x1b[0m' : '\x1b[36mNoxyAI [Chat] > \x1b[0m';
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
- 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
- }
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 (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
-
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-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();
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
- await chat(trimmed);
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') 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); }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "npm-noxyai",
3
- "version": "1.0.13",
3
+ "version": "1.0.15",
4
4
  "description": "CLI for NoxyAI",
5
5
  "main": "index-noxyai.js",
6
6
  "bin": {