navada-edge-cli 3.5.1 → 3.5.3

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/lib/agent.js CHANGED
@@ -454,6 +454,7 @@ function streamFreeTier(endpoint, messages) {
454
454
 
455
455
  res.on('end', () => {
456
456
  if (fullContent) process.stdout.write('\n');
457
+ sessionState._lastStreamed = true;
457
458
  resolve({ content: fullContent, isRateLimit: false, streamed: true });
458
459
  });
459
460
  });
@@ -552,6 +553,7 @@ function streamAnthropic(key, messages, tools, system) {
552
553
 
553
554
  res.on('end', () => {
554
555
  if (contentBlocks.some(b => b.type === 'text')) process.stdout.write('\n');
556
+ sessionState._lastStreamed = true;
555
557
  resolve({ content: contentBlocks, stop_reason: stopReason });
556
558
  });
557
559
  });
@@ -637,6 +639,7 @@ function streamOpenAI(key, messages, model = 'gpt-4o') {
637
639
 
638
640
  res.on('end', () => {
639
641
  if (fullContent) process.stdout.write('\n');
642
+ sessionState._lastStreamed = true;
640
643
  toolCalls = toolCalls.filter(Boolean);
641
644
  resolve({ content: fullContent, tool_calls: toolCalls, finish_reason: finishReason });
642
645
  });
@@ -704,6 +707,7 @@ function streamGemini(key, messages, model = 'gemini-2.0-flash') {
704
707
 
705
708
  res.on('end', () => {
706
709
  if (fullContent) process.stdout.write('\n');
710
+ sessionState._lastStreamed = true;
707
711
  resolve({ content: fullContent });
708
712
  });
709
713
  });
@@ -1072,7 +1076,115 @@ async function executeTool(name, input) {
1072
1076
  }
1073
1077
  }
1074
1078
 
1079
+ // ---------------------------------------------------------------------------
1080
+ // Local action interceptor — executes file/shell actions WITHOUT needing LLM tool use
1081
+ // This ensures free tier users can still create, read, edit, delete files
1082
+ // ---------------------------------------------------------------------------
1083
+ function tryLocalAction(userMessage) {
1084
+ const msg = userMessage.trim();
1085
+ const home = os.homedir();
1086
+ const desktop = path.join(home, 'Desktop');
1087
+
1088
+ // Resolve a location phrase to an absolute path (use ORIGINAL case, not lowered)
1089
+ function resolveLocation(phrase) {
1090
+ const p = phrase.trim().replace(/[""'.,!]/g, '');
1091
+ const low = p.toLowerCase();
1092
+ if (low === 'my desktop' || low === 'the desktop' || low === 'desktop') return desktop;
1093
+ if (low === 'home' || low === 'my home' || low === 'home directory') return home;
1094
+ if (p.startsWith('~')) return p.replace(/^~[/\\]?/, home + path.sep);
1095
+ if (path.isAbsolute(p)) return p;
1096
+ return path.join(process.cwd(), p);
1097
+ }
1098
+
1099
+ // Extract the ORIGINAL-CASE name from the original message using a case-insensitive match
1100
+ // We match on the original message to preserve casing
1101
+ let m;
1102
+
1103
+ // ── Create folder/directory ──
1104
+ // Pattern: "create a folder called NAME on my desktop"
1105
+ m = msg.match(/(?:create|make|new)\s+(?:a\s+)?(?:new\s+)?(?:folder|directory|dir)\s+(?:called|named)\s+[""']?([^""']+?)[""']?\s+(?:on|at|in)\s+(.+?)$/i);
1106
+ if (m) {
1107
+ const name = m[1].trim();
1108
+ const loc = resolveLocation(m[2]);
1109
+ const resolved = path.join(loc, name);
1110
+ try { fs.mkdirSync(resolved, { recursive: true }); return `Created folder: ${resolved}`; }
1111
+ catch (e) { return null; }
1112
+ }
1113
+
1114
+ // Pattern: "create a folder on my desktop called NAME"
1115
+ m = msg.match(/(?:create|make|new)\s+(?:a\s+)?(?:new\s+)?(?:folder|directory|dir)\s+(?:on|at|in)\s+(.+?)\s+(?:called|named)\s+[""']?([^""']+?)[""']?\s*$/i);
1116
+ if (m) {
1117
+ const loc = resolveLocation(m[1]);
1118
+ const name = m[2].trim();
1119
+ const resolved = path.join(loc, name);
1120
+ try { fs.mkdirSync(resolved, { recursive: true }); return `Created folder: ${resolved}`; }
1121
+ catch (e) { return null; }
1122
+ }
1123
+
1124
+ // Pattern: "create a folder called NAME" (no location — use cwd, or desktop if mentioned earlier)
1125
+ m = msg.match(/(?:create|make|new)\s+(?:a\s+)?(?:new\s+)?(?:folder|directory|dir)\s+(?:called|named)\s+[""']?([^""']+?)[""']?\s*$/i);
1126
+ if (m) {
1127
+ const name = m[1].trim();
1128
+ const loc = msg.toLowerCase().includes('desktop') ? desktop : process.cwd();
1129
+ const resolved = path.join(loc, name);
1130
+ try { fs.mkdirSync(resolved, { recursive: true }); return `Created folder: ${resolved}`; }
1131
+ catch (e) { return null; }
1132
+ }
1133
+
1134
+ // Pattern: "create a new folder NAME on my desktop" (no "called/named")
1135
+ m = msg.match(/(?:create|make|new)\s+(?:a\s+)?(?:new\s+)?(?:folder|directory|dir)\s+([A-Za-z0-9_\-. ]+?)\s+(?:on|at|in)\s+(.+?)$/i);
1136
+ if (m) {
1137
+ const name = m[1].trim();
1138
+ const loc = resolveLocation(m[2]);
1139
+ const resolved = path.join(loc, name);
1140
+ try { fs.mkdirSync(resolved, { recursive: true }); return `Created folder: ${resolved}`; }
1141
+ catch (e) { return null; }
1142
+ }
1143
+
1144
+ // ── Create file ──
1145
+ m = msg.match(/(?:create|make|new|touch)\s+(?:a\s+)?(?:new\s+)?(?:file)\s+(?:called|named)\s+[""']?([^""']+?)[""']?\s+(?:on|at|in)\s+(.+?)$/i);
1146
+ if (m) {
1147
+ const resolved = path.join(resolveLocation(m[2]), m[1].trim());
1148
+ return localTools.writeFile.execute(resolved, '');
1149
+ }
1150
+ m = msg.match(/(?:create|make|new|touch)\s+(?:a\s+)?(?:new\s+)?(?:file)\s+(?:called|named)\s+[""']?([^""']+?)[""']?\s*$/i);
1151
+ if (m) {
1152
+ const loc = msg.toLowerCase().includes('desktop') ? desktop : process.cwd();
1153
+ return localTools.writeFile.execute(path.join(loc, m[1].trim()), '');
1154
+ }
1155
+
1156
+ // ── Read file ──
1157
+ m = msg.match(/(?:read|show|display|cat|open)\s+(?:the\s+)?(?:file\s+)?[""']?([^""']+\.\w{1,5})[""']?/i);
1158
+ if (m) {
1159
+ const p = m[1].trim();
1160
+ const filePath = path.isAbsolute(p) ? p : path.join(process.cwd(), p);
1161
+ return localTools.readFile.execute(filePath);
1162
+ }
1163
+
1164
+ // ── Delete file/folder ──
1165
+ m = msg.match(/(?:delete|remove|rm)\s+(?:the\s+)?(?:file|folder|directory)\s+[""']?([^""']+?)[""']?\s*$/i);
1166
+ if (m) {
1167
+ const p = m[1].trim();
1168
+ const filePath = path.isAbsolute(p) ? p : path.join(process.cwd(), p);
1169
+ return localTools.deleteFile.execute(filePath);
1170
+ }
1171
+
1172
+ // ── List files ──
1173
+ m = msg.match(/(?:list|show|ls|dir)\s+(?:the\s+)?(?:files|contents|items)\s+(?:in|on|at|of)\s+(.+)/i);
1174
+ if (m) {
1175
+ return localTools.listFiles.execute(resolveLocation(m[1]));
1176
+ }
1177
+
1178
+ return null;
1179
+ }
1180
+
1075
1181
  async function grokChat(userMessage, conversationHistory = []) {
1182
+ // Try local action first — enables file ops on free tier
1183
+ const localResult = tryLocalAction(userMessage);
1184
+ if (localResult) {
1185
+ return `${localResult}\n\nWhat would you like to do next?`;
1186
+ }
1187
+
1076
1188
  const messages = [
1077
1189
  ...conversationHistory.slice(-20).map(m => ({
1078
1190
  role: m.role,
@@ -1145,7 +1257,6 @@ async function grokChat(userMessage, conversationHistory = []) {
1145
1257
 
1146
1258
  // Extract final text
1147
1259
  const content = response?.choices?.[0]?.message?.content || '';
1148
- if (content) console.log(` ${content}`);
1149
1260
  return content || 'No response.';
1150
1261
  }
1151
1262
 
@@ -31,11 +31,12 @@ module.exports = function(reg) {
31
31
  addToHistory('user', msg);
32
32
  addToHistory('assistant', response);
33
33
 
34
- // Only print if not already streamed
35
- if (!response._streamed) {
34
+ // Only print if not already streamed to stdout
35
+ if (response && !sessionState._lastStreamed) {
36
36
  console.log(ui.header('NAVADA'));
37
37
  console.log(` ${response}`);
38
38
  }
39
+ sessionState._lastStreamed = false;
39
40
 
40
41
  // Track usage
41
42
  reportTelemetry('chat', { messageLength: msg.length });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "navada-edge-cli",
3
- "version": "3.5.1",
3
+ "version": "3.5.3",
4
4
  "description": "Interactive CLI for the NAVADA Edge Network — explore nodes, agents, Cloudflare, AI, Docker, and MCP from your terminal",
5
5
  "main": "lib/cli.js",
6
6
  "bin": {