natureco-cli 1.1.6 → 2.0.1
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/package.json +1 -1
- package/src/commands/dashboard.js +2 -2
- package/src/tools/list_dir.js +115 -0
- package/src/utils/api.js +198 -80
package/package.json
CHANGED
|
@@ -211,7 +211,7 @@ body::before{
|
|
|
211
211
|
<div class="header-bot-name" id="header-bot-name">Nature Bot</div>
|
|
212
212
|
<div class="header-bot-model" id="header-bot-model">NatureCo</div>
|
|
213
213
|
</div>
|
|
214
|
-
<div class="version-badge" id="version-badge">
|
|
214
|
+
<div class="version-badge" id="version-badge">v2.0.1</div>
|
|
215
215
|
</div>
|
|
216
216
|
<div class="messages" id="messages"></div>
|
|
217
217
|
<div class="input-area">
|
|
@@ -341,7 +341,7 @@ function dashboard(action) {
|
|
|
341
341
|
apiKey: cfg.apiKey,
|
|
342
342
|
defaultBot: cfg.defaultBot,
|
|
343
343
|
defaultBotId: cfg.defaultBotId,
|
|
344
|
-
version: '
|
|
344
|
+
version: 'v2.0.1',
|
|
345
345
|
bots: cfg.bots || [],
|
|
346
346
|
telegramToken: cfg.telegramToken || null,
|
|
347
347
|
whatsappConnected: cfg.whatsappConnected || false,
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* List Directory Tool
|
|
6
|
+
* Lists files and directories in a given path
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
module.exports = {
|
|
10
|
+
name: 'list_dir',
|
|
11
|
+
description: 'List files and directories in a given path. Returns file names, sizes, and types.',
|
|
12
|
+
inputSchema: {
|
|
13
|
+
type: 'object',
|
|
14
|
+
properties: {
|
|
15
|
+
path: {
|
|
16
|
+
type: 'string',
|
|
17
|
+
description: 'Directory path to list (relative or absolute). Use "." for current directory.'
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
required: ['path']
|
|
21
|
+
},
|
|
22
|
+
|
|
23
|
+
async execute(params) {
|
|
24
|
+
try {
|
|
25
|
+
const dirPath = params.path || '.';
|
|
26
|
+
const absolutePath = path.resolve(dirPath);
|
|
27
|
+
|
|
28
|
+
// Check if directory exists
|
|
29
|
+
if (!fs.existsSync(absolutePath)) {
|
|
30
|
+
return {
|
|
31
|
+
success: false,
|
|
32
|
+
error: `Directory not found: ${dirPath}`
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Check if it's a directory
|
|
37
|
+
const stats = fs.statSync(absolutePath);
|
|
38
|
+
if (!stats.isDirectory()) {
|
|
39
|
+
return {
|
|
40
|
+
success: false,
|
|
41
|
+
error: `Not a directory: ${dirPath}`
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Read directory
|
|
46
|
+
const entries = fs.readdirSync(absolutePath, { withFileTypes: true });
|
|
47
|
+
|
|
48
|
+
// Format entries
|
|
49
|
+
const items = entries.map(entry => {
|
|
50
|
+
const itemPath = path.join(absolutePath, entry.name);
|
|
51
|
+
let size = 0;
|
|
52
|
+
let type = 'unknown';
|
|
53
|
+
|
|
54
|
+
try {
|
|
55
|
+
const itemStats = fs.statSync(itemPath);
|
|
56
|
+
size = itemStats.size;
|
|
57
|
+
|
|
58
|
+
if (entry.isDirectory()) {
|
|
59
|
+
type = 'directory';
|
|
60
|
+
} else if (entry.isFile()) {
|
|
61
|
+
type = 'file';
|
|
62
|
+
} else if (entry.isSymbolicLink()) {
|
|
63
|
+
type = 'symlink';
|
|
64
|
+
}
|
|
65
|
+
} catch (err) {
|
|
66
|
+
// Ignore stat errors
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
name: entry.name,
|
|
71
|
+
type: type,
|
|
72
|
+
size: size
|
|
73
|
+
};
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// Sort: directories first, then files
|
|
77
|
+
items.sort((a, b) => {
|
|
78
|
+
if (a.type === 'directory' && b.type !== 'directory') return -1;
|
|
79
|
+
if (a.type !== 'directory' && b.type === 'directory') return 1;
|
|
80
|
+
return a.name.localeCompare(b.name);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// Format output
|
|
84
|
+
let output = `Directory: ${absolutePath}\n`;
|
|
85
|
+
output += `Total items: ${items.length}\n\n`;
|
|
86
|
+
|
|
87
|
+
items.forEach(item => {
|
|
88
|
+
const typeIcon = item.type === 'directory' ? '📁' : '📄';
|
|
89
|
+
const sizeStr = item.type === 'file' ? ` (${formatSize(item.size)})` : '';
|
|
90
|
+
output += `${typeIcon} ${item.name}${sizeStr}\n`;
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
success: true,
|
|
95
|
+
output: output,
|
|
96
|
+
items: items,
|
|
97
|
+
path: absolutePath
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
} catch (error) {
|
|
101
|
+
return {
|
|
102
|
+
success: false,
|
|
103
|
+
error: error.message
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
function formatSize(bytes) {
|
|
110
|
+
if (bytes === 0) return '0 B';
|
|
111
|
+
const k = 1024;
|
|
112
|
+
const sizes = ['B', 'KB', 'MB', 'GB'];
|
|
113
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
114
|
+
return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i];
|
|
115
|
+
}
|
package/src/utils/api.js
CHANGED
|
@@ -1,108 +1,226 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
...options.headers,
|
|
10
|
-
},
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
if (!response.ok) {
|
|
14
|
-
const text = await response.text();
|
|
15
|
-
throw new Error(`API Error: ${response.status} - ${text}`);
|
|
16
|
-
}
|
|
1
|
+
// NatureCo CLI v2.0.0 - Direct Groq Integration with Local Tool Execution
|
|
2
|
+
// Bypass NatureCo backend, connect directly to Groq API
|
|
3
|
+
|
|
4
|
+
const { getConfig } = require('./config');
|
|
5
|
+
const { getToolDefinitions, executeToolCalls } = require('./tool-runner');
|
|
6
|
+
|
|
7
|
+
// Conversation history for multi-turn chat
|
|
8
|
+
const conversationHistory = new Map();
|
|
17
9
|
|
|
18
|
-
|
|
10
|
+
/**
|
|
11
|
+
* Get Groq API key from config
|
|
12
|
+
*/
|
|
13
|
+
function getGroqApiKey() {
|
|
14
|
+
const config = getConfig();
|
|
15
|
+
return config.groqApiKey || null;
|
|
19
16
|
}
|
|
20
17
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Format tool definitions for Groq API
|
|
20
|
+
*/
|
|
21
|
+
function formatToolsForGroq() {
|
|
22
|
+
const tools = getToolDefinitions();
|
|
23
|
+
|
|
24
|
+
return tools.map(tool => ({
|
|
25
|
+
type: 'function',
|
|
26
|
+
function: {
|
|
27
|
+
name: tool.name,
|
|
28
|
+
description: tool.description,
|
|
29
|
+
parameters: tool.inputSchema
|
|
30
|
+
}
|
|
31
|
+
}));
|
|
25
32
|
}
|
|
26
33
|
|
|
27
|
-
|
|
28
|
-
|
|
34
|
+
/**
|
|
35
|
+
* Send message to Groq with tool support
|
|
36
|
+
*/
|
|
37
|
+
async function sendMessageToGroq(apiKey, message, conversationId = null, systemPrompt = null) {
|
|
29
38
|
const config = getConfig();
|
|
39
|
+
const groqApiKey = config.groqApiKey;
|
|
30
40
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
41
|
+
if (!groqApiKey) {
|
|
42
|
+
throw new Error('Groq API key not found. Set it with: natureco config set groqApiKey gsk_xxx');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Get model from config (default: llama-3.1-8b-instant)
|
|
46
|
+
const model = config.groqModel || 'llama-3.1-8b-instant';
|
|
47
|
+
|
|
48
|
+
// Get or create conversation history
|
|
49
|
+
const convId = conversationId || `conv_${Date.now()}`;
|
|
50
|
+
if (!conversationHistory.has(convId)) {
|
|
51
|
+
conversationHistory.set(convId, []);
|
|
52
|
+
}
|
|
53
|
+
const history = conversationHistory.get(convId);
|
|
54
|
+
|
|
55
|
+
// Add system prompt if provided
|
|
56
|
+
const messages = [];
|
|
57
|
+
if (systemPrompt) {
|
|
58
|
+
messages.push({ role: 'system', content: systemPrompt });
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Add conversation history
|
|
62
|
+
messages.push(...history);
|
|
63
|
+
|
|
64
|
+
// Add user message
|
|
65
|
+
messages.push({ role: 'user', content: message });
|
|
66
|
+
|
|
67
|
+
// Get tool definitions
|
|
68
|
+
const tools = formatToolsForGroq();
|
|
69
|
+
|
|
70
|
+
console.log('\n[Groq] Sending request...');
|
|
71
|
+
console.log('[Groq] Model:', model);
|
|
72
|
+
console.log('[Groq] Messages:', messages.length);
|
|
73
|
+
console.log('[Groq] Tools:', tools.length);
|
|
74
|
+
|
|
75
|
+
// Tool execution loop (max 10 iterations)
|
|
76
|
+
let iteration = 0;
|
|
77
|
+
const maxIterations = 10;
|
|
78
|
+
let finalResponse = null;
|
|
79
|
+
|
|
80
|
+
while (iteration < maxIterations) {
|
|
81
|
+
iteration++;
|
|
82
|
+
console.log(`\n[Groq] Iteration ${iteration}/${maxIterations}`);
|
|
83
|
+
|
|
84
|
+
// Call Groq API
|
|
85
|
+
const response = await fetch('https://api.groq.com/openai/v1/chat/completions', {
|
|
86
|
+
method: 'POST',
|
|
87
|
+
headers: {
|
|
88
|
+
'Authorization': `Bearer ${groqApiKey}`,
|
|
89
|
+
'Content-Type': 'application/json',
|
|
90
|
+
},
|
|
91
|
+
body: JSON.stringify({
|
|
92
|
+
model: model,
|
|
93
|
+
messages: messages,
|
|
94
|
+
tools: tools,
|
|
95
|
+
tool_choice: 'auto',
|
|
96
|
+
temperature: 0.7,
|
|
97
|
+
max_tokens: 2000,
|
|
98
|
+
}),
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
if (!response.ok) {
|
|
102
|
+
const errorText = await response.text();
|
|
103
|
+
console.error('[Groq] Error:', errorText);
|
|
104
|
+
throw new Error(`Groq API error: ${response.status} - ${errorText}`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const data = await response.json();
|
|
108
|
+
const assistantMessage = data.choices[0].message;
|
|
109
|
+
|
|
110
|
+
console.log('[Groq] Response type:', assistantMessage.tool_calls ? 'tool_calls' : 'text');
|
|
111
|
+
|
|
112
|
+
// Add assistant message to history
|
|
113
|
+
messages.push(assistantMessage);
|
|
114
|
+
|
|
115
|
+
// Check for tool calls
|
|
116
|
+
if (assistantMessage.tool_calls && assistantMessage.tool_calls.length > 0) {
|
|
117
|
+
console.log(`[Groq] Tool calls: ${assistantMessage.tool_calls.length}`);
|
|
118
|
+
|
|
119
|
+
// Execute tools locally
|
|
120
|
+
const toolCalls = assistantMessage.tool_calls.map(tc => ({
|
|
121
|
+
id: tc.id,
|
|
122
|
+
name: tc.function.name,
|
|
123
|
+
input: JSON.parse(tc.function.arguments)
|
|
124
|
+
}));
|
|
36
125
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
126
|
+
const toolResults = await executeToolCalls(toolCalls);
|
|
127
|
+
|
|
128
|
+
// Add tool results to messages
|
|
129
|
+
for (const result of toolResults) {
|
|
130
|
+
const toolCall = assistantMessage.tool_calls.find(tc => tc.id === result.id);
|
|
131
|
+
|
|
132
|
+
messages.push({
|
|
133
|
+
role: 'tool',
|
|
134
|
+
tool_call_id: result.id,
|
|
135
|
+
name: result.name,
|
|
136
|
+
content: result.result.success
|
|
137
|
+
? (result.result.output || JSON.stringify(result.result))
|
|
138
|
+
: `Error: ${result.result.error}`
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Continue loop to get final response
|
|
143
|
+
continue;
|
|
45
144
|
}
|
|
145
|
+
|
|
146
|
+
// No tool calls, we have final response
|
|
147
|
+
finalResponse = assistantMessage.content;
|
|
148
|
+
break;
|
|
46
149
|
}
|
|
47
150
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
const data = await request('/api/agent/message', {
|
|
52
|
-
method: 'POST',
|
|
53
|
-
headers: {
|
|
54
|
-
Authorization: `Bearer ${apiKey}`,
|
|
55
|
-
'X-User-ID': 'cli-user'
|
|
56
|
-
},
|
|
57
|
-
body: JSON.stringify(body),
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
// Debug: API response'u logla
|
|
61
|
-
console.log('[DEBUG] API Response:', JSON.stringify(data, null, 2), '\n');
|
|
62
|
-
|
|
63
|
-
// Backend response formatını CLI formatına çevir
|
|
64
|
-
// Backend: { response: "...", message_id: "...", bot_id: "..." }
|
|
65
|
-
// CLI beklediği: { reply: "...", conversation_id: "..." }
|
|
66
|
-
if (data.response && !data.reply) {
|
|
67
|
-
data.reply = data.response;
|
|
151
|
+
if (iteration >= maxIterations) {
|
|
152
|
+
console.log('\n[Groq] Max iterations reached');
|
|
153
|
+
finalResponse = finalResponse || 'Max tool execution iterations reached.';
|
|
68
154
|
}
|
|
69
|
-
|
|
70
|
-
|
|
155
|
+
|
|
156
|
+
// Save to conversation history (only user and final assistant message)
|
|
157
|
+
history.push({ role: 'user', content: message });
|
|
158
|
+
history.push({ role: 'assistant', content: finalResponse });
|
|
159
|
+
|
|
160
|
+
// Keep history limited to last 20 messages
|
|
161
|
+
if (history.length > 20) {
|
|
162
|
+
conversationHistory.set(convId, history.slice(-20));
|
|
71
163
|
}
|
|
72
164
|
|
|
73
|
-
return
|
|
165
|
+
return {
|
|
166
|
+
reply: finalResponse,
|
|
167
|
+
conversation_id: convId,
|
|
168
|
+
message_id: `msg_${Date.now()}`,
|
|
169
|
+
success: true
|
|
170
|
+
};
|
|
74
171
|
}
|
|
75
172
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
await new Promise(r => setTimeout(r, delay));
|
|
85
|
-
}
|
|
173
|
+
/**
|
|
174
|
+
* Clear conversation history
|
|
175
|
+
*/
|
|
176
|
+
function clearConversation(conversationId) {
|
|
177
|
+
if (conversationId) {
|
|
178
|
+
conversationHistory.delete(conversationId);
|
|
179
|
+
} else {
|
|
180
|
+
conversationHistory.clear();
|
|
86
181
|
}
|
|
87
|
-
return { reply: 'API şu an meşgul, lütfen tekrar deneyin.' };
|
|
88
182
|
}
|
|
89
183
|
|
|
90
|
-
|
|
91
|
-
|
|
184
|
+
/**
|
|
185
|
+
* Legacy function for compatibility
|
|
186
|
+
*/
|
|
187
|
+
async function sendMessage(apiKey, botId, message, conversationId = null, skillPrompts = '') {
|
|
188
|
+
// System prompt for terminal assistant
|
|
189
|
+
const systemPrompt = "You are a terminal assistant. When users ask for file listing, command execution, or directory viewing, you MUST use the available tools (bash, read_file, write_file, list_dir). Never say 'run this command' - execute it yourself using tools and show the result.";
|
|
190
|
+
|
|
191
|
+
return sendMessageToGroq(apiKey, message, conversationId, systemPrompt);
|
|
92
192
|
}
|
|
93
193
|
|
|
194
|
+
/**
|
|
195
|
+
* Validate API key (not used in v2.0.0, kept for compatibility)
|
|
196
|
+
*/
|
|
94
197
|
async function validateApiKey(apiKey) {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
198
|
+
const groqApiKey = getGroqApiKey();
|
|
199
|
+
return groqApiKey !== null;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Get bots (not used in v2.0.0, kept for compatibility)
|
|
204
|
+
*/
|
|
205
|
+
async function getBots(apiKey) {
|
|
206
|
+
return {
|
|
207
|
+
bots: [
|
|
208
|
+
{
|
|
209
|
+
id: 'groq-direct',
|
|
210
|
+
name: 'Groq Direct',
|
|
211
|
+
ai_provider: 'groq',
|
|
212
|
+
model: 'llama-3.3-70b-versatile'
|
|
213
|
+
}
|
|
214
|
+
]
|
|
215
|
+
};
|
|
101
216
|
}
|
|
102
217
|
|
|
103
218
|
module.exports = {
|
|
104
|
-
getBots,
|
|
105
219
|
sendMessage,
|
|
220
|
+
sendMessageToGroq,
|
|
106
221
|
validateApiKey,
|
|
107
|
-
|
|
222
|
+
getBots,
|
|
223
|
+
clearConversation,
|
|
224
|
+
getGroqApiKey,
|
|
225
|
+
_sendMessage: sendMessage, // Alias for compatibility
|
|
108
226
|
};
|