natureco-cli 2.18.1 → 2.19.0
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/chat.js +1 -2
- package/src/commands/code.js +276 -100
- package/src/utils/api.js +26 -13
package/package.json
CHANGED
package/src/commands/chat.js
CHANGED
|
@@ -312,11 +312,10 @@ async function chat(botName, options = {}) {
|
|
|
312
312
|
|
|
313
313
|
if (response.conversation_id) conversationId = response.conversation_id;
|
|
314
314
|
|
|
315
|
-
// Tool loop —
|
|
315
|
+
// Tool loop — api.js zaten tool loop yapıyor, bu loop çalışmamalı
|
|
316
316
|
let iter = 0;
|
|
317
317
|
while (iter < 5) {
|
|
318
318
|
const toolCalls = extractToolCalls(response);
|
|
319
|
-
console.log('[DEBUG] Tool calls:', JSON.stringify(toolCalls));
|
|
320
319
|
if (!toolCalls?.length) break;
|
|
321
320
|
console.log(chalk.yellow(`🔧 ${toolCalls.length} tool çalıştırılıyor...`));
|
|
322
321
|
const toolResults = await executeToolCalls(toolCalls);
|
package/src/commands/code.js
CHANGED
|
@@ -5,14 +5,14 @@ const readline = require('readline');
|
|
|
5
5
|
const inquirer = require('inquirer');
|
|
6
6
|
const chalk = require('chalk');
|
|
7
7
|
const { getApiKey, getConfig } = require('../utils/config');
|
|
8
|
-
const { getBots,
|
|
8
|
+
const { getBots, getProviderConfig, startMcpServers } = require('../utils/api');
|
|
9
9
|
const { getMemoryPrompt, loadMemory } = require('../utils/memory');
|
|
10
10
|
const { getAgentsPrompt } = require('../utils/agents');
|
|
11
11
|
const { createSession, addMessageToSession } = require('../utils/sessions');
|
|
12
12
|
const { addToHistory } = require('../utils/history');
|
|
13
|
-
const { getToolDefinitions,
|
|
14
|
-
const { extractToolCalls } = require('../utils/tool-adapter');
|
|
13
|
+
const { getToolDefinitions, executeTool } = require('../utils/tool-runner');
|
|
15
14
|
|
|
15
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
16
16
|
const sep = () => chalk.gray('─'.repeat(process.stdout.columns || 120));
|
|
17
17
|
|
|
18
18
|
function centerText(text) {
|
|
@@ -23,11 +23,141 @@ function centerText(text) {
|
|
|
23
23
|
}).join('\n');
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
const
|
|
27
|
-
Her adımı açıkça belirt. Değişiklik yapmadan önce plan sun.
|
|
28
|
-
Türkçe konuş.
|
|
29
|
-
Mevcut çalışma dizini: ${process.cwd()}`;
|
|
26
|
+
const SPINNER_FRAMES = ['⠋','⠙','⠹','⠸','⠼','⠴','⠦','⠧','⠇','⠏'];
|
|
30
27
|
|
|
28
|
+
function startSpinner(label) {
|
|
29
|
+
let i = 0;
|
|
30
|
+
return setInterval(() => {
|
|
31
|
+
process.stdout.write(`\r ${chalk.cyan(SPINNER_FRAMES[i++ % SPINNER_FRAMES.length])} ${chalk.gray(label)}`);
|
|
32
|
+
}, 80);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function stopSpinner(timer, label, success = true) {
|
|
36
|
+
clearInterval(timer);
|
|
37
|
+
process.stdout.write(`\r ${success ? chalk.green('✓') : chalk.red('✗')} ${chalk.cyan(label)}\n`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// ── Streaming fetch ───────────────────────────────────────────────────────────
|
|
41
|
+
async function streamMessage(providerConfig, messages, tools) {
|
|
42
|
+
const endpoint = `${providerConfig.url}/chat/completions`;
|
|
43
|
+
const body = {
|
|
44
|
+
model: providerConfig.model,
|
|
45
|
+
messages,
|
|
46
|
+
tools,
|
|
47
|
+
tool_choice: 'auto',
|
|
48
|
+
temperature: 0.7,
|
|
49
|
+
max_tokens: 4000,
|
|
50
|
+
stream: true,
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const response = await fetch(endpoint, {
|
|
54
|
+
method: 'POST',
|
|
55
|
+
headers: {
|
|
56
|
+
'Authorization': `Bearer ${providerConfig.apiKey}`,
|
|
57
|
+
'Content-Type': 'application/json',
|
|
58
|
+
},
|
|
59
|
+
body: JSON.stringify(body),
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
if (!response.ok) {
|
|
63
|
+
const err = await response.text();
|
|
64
|
+
throw new Error(`API error: ${response.status} — ${err}`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const reader = response.body.getReader();
|
|
68
|
+
const decoder = new TextDecoder();
|
|
69
|
+
let fullText = '';
|
|
70
|
+
let toolCallsBuffer = {}; // id -> { name, arguments }
|
|
71
|
+
|
|
72
|
+
process.stdout.write('\n');
|
|
73
|
+
|
|
74
|
+
while (true) {
|
|
75
|
+
const { done, value } = await reader.read();
|
|
76
|
+
if (done) break;
|
|
77
|
+
|
|
78
|
+
const chunk = decoder.decode(value);
|
|
79
|
+
const lines = chunk.split('\n').filter(l => l.startsWith('data: '));
|
|
80
|
+
|
|
81
|
+
for (const line of lines) {
|
|
82
|
+
const data = line.slice(6).trim();
|
|
83
|
+
if (data === '[DONE]') continue;
|
|
84
|
+
try {
|
|
85
|
+
const json = JSON.parse(data);
|
|
86
|
+
const delta = json.choices?.[0]?.delta;
|
|
87
|
+
if (!delta) continue;
|
|
88
|
+
|
|
89
|
+
// Text content
|
|
90
|
+
if (delta.content) {
|
|
91
|
+
process.stdout.write(delta.content);
|
|
92
|
+
fullText += delta.content;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Tool call deltas
|
|
96
|
+
if (delta.tool_calls) {
|
|
97
|
+
for (const tc of delta.tool_calls) {
|
|
98
|
+
const idx = tc.index ?? 0;
|
|
99
|
+
if (!toolCallsBuffer[idx]) {
|
|
100
|
+
toolCallsBuffer[idx] = { id: tc.id || '', name: '', arguments: '' };
|
|
101
|
+
}
|
|
102
|
+
if (tc.id) toolCallsBuffer[idx].id = tc.id;
|
|
103
|
+
if (tc.function?.name) toolCallsBuffer[idx].name += tc.function.name;
|
|
104
|
+
if (tc.function?.arguments) toolCallsBuffer[idx].arguments += tc.function.arguments;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
} catch {}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
process.stdout.write('\n');
|
|
112
|
+
|
|
113
|
+
const toolCalls = Object.values(toolCallsBuffer)
|
|
114
|
+
.filter(tc => tc.name)
|
|
115
|
+
.map(tc => ({
|
|
116
|
+
id: tc.id,
|
|
117
|
+
name: tc.name,
|
|
118
|
+
input: (() => { try { return JSON.parse(tc.arguments); } catch { return {}; } })(),
|
|
119
|
+
}));
|
|
120
|
+
|
|
121
|
+
return { text: fullText, toolCalls };
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// ── Tool execution with spinner + confirm ─────────────────────────────────────
|
|
125
|
+
const DANGEROUS = ['rm ', 'rmdir', 'delete', 'DROP', 'truncate'];
|
|
126
|
+
|
|
127
|
+
async function runToolCall(toolCall, stats) {
|
|
128
|
+
const inputPreview = JSON.stringify(toolCall.input).slice(0, 60);
|
|
129
|
+
const label = `${toolCall.name} ${chalk.gray(inputPreview)}`;
|
|
130
|
+
|
|
131
|
+
const needsConfirm =
|
|
132
|
+
toolCall.name === 'write_file' ||
|
|
133
|
+
(toolCall.name === 'bash' && DANGEROUS.some(d => toolCall.input.command?.includes(d)));
|
|
134
|
+
|
|
135
|
+
if (needsConfirm) {
|
|
136
|
+
console.log(chalk.yellow(`\n ⚠️ ${toolCall.name}: ${inputPreview}`));
|
|
137
|
+
const { ok } = await inquirer.prompt([{
|
|
138
|
+
type: 'confirm', name: 'ok',
|
|
139
|
+
message: ' Devam edilsin mi?', default: true,
|
|
140
|
+
}]);
|
|
141
|
+
if (!ok) {
|
|
142
|
+
console.log(chalk.gray(' İptal edildi.\n'));
|
|
143
|
+
return { success: false, output: 'Kullanıcı iptal etti.' };
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const spinner = startSpinner(`${toolCall.name} ${inputPreview}`);
|
|
148
|
+
const result = await executeTool(toolCall.name, toolCall.input);
|
|
149
|
+
stopSpinner(spinner, toolCall.name, result.success !== false);
|
|
150
|
+
|
|
151
|
+
if (result.success !== false) {
|
|
152
|
+
if (toolCall.name === 'write_file') stats.filesChanged++;
|
|
153
|
+
if (toolCall.name === 'bash') stats.commandsRun++;
|
|
154
|
+
stats.toolCallCount++;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return result;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// ── Main ──────────────────────────────────────────────────────────────────────
|
|
31
161
|
async function code(targetFile, options = {}) {
|
|
32
162
|
const apiKey = getApiKey();
|
|
33
163
|
const config = getConfig();
|
|
@@ -36,27 +166,25 @@ async function code(targetFile, options = {}) {
|
|
|
36
166
|
// ── Bot seçimi ──────────────────────────────────────────────────────────────
|
|
37
167
|
let botList;
|
|
38
168
|
try {
|
|
39
|
-
botList = await
|
|
169
|
+
botList = await getBots(apiKey || config.providerApiKey || '');
|
|
40
170
|
} catch (err) {
|
|
41
171
|
console.log(chalk.red(`\n❌ Error: ${err.message}\n`));
|
|
42
172
|
process.exit(1);
|
|
43
173
|
}
|
|
44
174
|
|
|
45
175
|
if (!botList?.bots?.length) {
|
|
46
|
-
console.log(chalk.gray('No bots found
|
|
176
|
+
console.log(chalk.gray('No bots found.\n'));
|
|
47
177
|
process.exit(1);
|
|
48
178
|
}
|
|
49
179
|
|
|
50
180
|
let bot;
|
|
51
181
|
if (config.botName) {
|
|
52
|
-
bot = botList.bots.find(b => b.name
|
|
182
|
+
bot = botList.bots.find(b => b.name?.toLowerCase() === config.botName.toLowerCase());
|
|
53
183
|
}
|
|
54
184
|
if (!bot) {
|
|
55
185
|
process.stdin.resume();
|
|
56
186
|
const { selectedBot } = await inquirer.prompt([{
|
|
57
|
-
type: 'list',
|
|
58
|
-
name: 'selectedBot',
|
|
59
|
-
message: 'Bot seçin:',
|
|
187
|
+
type: 'list', name: 'selectedBot', message: 'Bot seçin:',
|
|
60
188
|
choices: botList.bots.map(b => ({ name: b.name, value: b.id })),
|
|
61
189
|
}]);
|
|
62
190
|
bot = botList.bots.find(b => b.id === selectedBot);
|
|
@@ -65,13 +193,35 @@ async function code(targetFile, options = {}) {
|
|
|
65
193
|
const mem = loadMemory(bot.id);
|
|
66
194
|
const displayBotName = mem.botName || bot.name || 'NatureCo';
|
|
67
195
|
const userName = mem.name || config.userName || 'User';
|
|
68
|
-
const
|
|
69
|
-
const shortModel = providerModel.split('/').pop().split('-').slice(0, 3).join('-');
|
|
196
|
+
const providerConfig = getProviderConfig();
|
|
70
197
|
|
|
198
|
+
if (!providerConfig) {
|
|
199
|
+
console.log(chalk.red('\n❌ Provider yapılandırılmamış. natureco config set providerUrl ...\n'));
|
|
200
|
+
process.exit(1);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const shortModel = providerConfig.model.split('/').pop().split('-').slice(0, 3).join('-');
|
|
204
|
+
|
|
205
|
+
// ── Proje context — dizini otomatik tara ────────────────────────────────────
|
|
206
|
+
let projectContext = '';
|
|
207
|
+
try {
|
|
208
|
+
const dirResult = await executeTool('list_dir', { path: process.cwd() });
|
|
209
|
+
if (dirResult.success !== false) {
|
|
210
|
+
projectContext = `\nMevcut proje dizini: ${process.cwd()}\nDosyalar:\n${(dirResult.output || '').slice(0, 800)}`;
|
|
211
|
+
}
|
|
212
|
+
} catch {}
|
|
213
|
+
|
|
214
|
+
// ── Sistem prompt ────────────────────────────────────────────────────────────
|
|
71
215
|
const agentsPrompt = getAgentsPrompt();
|
|
72
216
|
const memoryPrompt = getMemoryPrompt(bot.id);
|
|
73
217
|
|
|
74
|
-
let systemPrompt =
|
|
218
|
+
let systemPrompt = `Sen NatureCo Code Agent'sın — güçlü bir AI coding asistanı.
|
|
219
|
+
Kullanıcının projesinde gerçekten çalış: dosyaları oku, değiştir, komutları çalıştır.
|
|
220
|
+
Her adımı Türkçe açıkla. Değişiklik yapmadan önce ne yapacağını söyle.
|
|
221
|
+
Hataları yakala ve düzelt. İş bitince özet sun.
|
|
222
|
+
Mevcut dizin: ${process.cwd()}
|
|
223
|
+
Kullanıcı: ${userName}${projectContext}`;
|
|
224
|
+
|
|
75
225
|
if (agentsPrompt) systemPrompt += `\n\n## Project Instructions\n${agentsPrompt}`;
|
|
76
226
|
if (memoryPrompt) systemPrompt += '\n\n' + memoryPrompt;
|
|
77
227
|
|
|
@@ -79,144 +229,170 @@ async function code(targetFile, options = {}) {
|
|
|
79
229
|
if (targetFile) {
|
|
80
230
|
try {
|
|
81
231
|
const content = fs.readFileSync(path.resolve(targetFile), 'utf-8');
|
|
82
|
-
systemPrompt += `\n\n## Odak Dosyası: ${targetFile}\n\`\`\`\n${content}\n\`\`\``;
|
|
232
|
+
systemPrompt += `\n\n## Odak Dosyası: ${targetFile}\n\`\`\`\n${content.slice(0, 2000)}\n\`\`\``;
|
|
83
233
|
} catch {
|
|
84
|
-
console.log(chalk.yellow(` ⚠️ ${targetFile} okunamad
|
|
234
|
+
console.log(chalk.yellow(` ⚠️ ${targetFile} okunamadı\n`));
|
|
85
235
|
}
|
|
86
236
|
}
|
|
87
237
|
|
|
238
|
+
// ── Session ──────────────────────────────────────────────────────────────────
|
|
88
239
|
const session = createSession(bot.id, bot.name);
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
240
|
+
const conversationMessages = [{ role: 'system', content: systemPrompt }];
|
|
241
|
+
const stats = { filesChanged: 0, commandsRun: 0, toolCallCount: 0, messageCount: 0 };
|
|
242
|
+
|
|
243
|
+
// ── MCP sunucularını başlat ──────────────────────────────────────────────────
|
|
244
|
+
await startMcpServers().catch(() => {});
|
|
245
|
+
|
|
246
|
+
// ── Tool definitions ─────────────────────────────────────────────────────────
|
|
247
|
+
const localTools = getToolDefinitions();
|
|
248
|
+
const tools = localTools.map(t => ({
|
|
249
|
+
type: 'function',
|
|
250
|
+
function: {
|
|
251
|
+
name: t.name,
|
|
252
|
+
description: t.description,
|
|
253
|
+
parameters: t.inputSchema || { type: 'object', properties: {} },
|
|
254
|
+
},
|
|
255
|
+
}));
|
|
92
256
|
|
|
93
257
|
// ── Header ──────────────────────────────────────────────────────────────────
|
|
94
258
|
console.clear();
|
|
95
|
-
|
|
259
|
+
const codeLogo = [
|
|
260
|
+
'███╗ ██╗ █████╗ ████████╗██╗ ██╗██████╗ ███████╗ ██████╗ ██████╗',
|
|
261
|
+
'████╗ ██║██╔══██╗╚══██╔══╝██║ ██║██╔══██╗██╔════╝██╔════╝ ██╔═══██╗',
|
|
262
|
+
'██╔██╗ ██║███████║ ██║ ██║ ██║██████╔╝█████╗ ██║ ██║ ██║',
|
|
263
|
+
'██║╚██╗██║██╔══██║ ██║ ██║ ██║██╔══██╗██╔══╝ ██║ ██║ ██║',
|
|
264
|
+
'██║ ╚████║██║ ██║ ██║ ╚██████╔╝██║ ██║███████╗╚██████╗ ╚██████╔╝',
|
|
265
|
+
'╚═╝ ╚═══╝╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═════╝',
|
|
266
|
+
'',
|
|
267
|
+
'█▀▀ █▀█ █▀▄ █▀▀',
|
|
268
|
+
'█▄▄ █▄█ █▄▀ ██▄',
|
|
269
|
+
].join('\n');
|
|
270
|
+
console.log(centerText(chalk.green(codeLogo)));
|
|
271
|
+
console.log();
|
|
272
|
+
console.log(centerText(chalk.cyan(`(\\_/) ${displayBotName} hazır`) + chalk.gray(` · v${version}`)));
|
|
96
273
|
console.log(sep());
|
|
97
|
-
if (targetFile) {
|
|
98
|
-
|
|
99
|
-
console.log(sep());
|
|
100
|
-
}
|
|
101
|
-
console.log(centerText(chalk.gray(`${shortModel} · /help için yardım · Ctrl+C çıkış`)));
|
|
274
|
+
if (targetFile) console.log(centerText(chalk.gray(`📄 ${targetFile}`)));
|
|
275
|
+
console.log(centerText(chalk.gray(`${shortModel} · /help · Ctrl+C çıkış`)));
|
|
102
276
|
console.log(sep());
|
|
103
277
|
console.log();
|
|
104
278
|
|
|
105
|
-
// ── Yükleme animasyonu ──────────────────────────────────────────────────────
|
|
106
|
-
const loadingFrames = ['●○○', '○●○', '○○●'];
|
|
107
|
-
let loadingFrame = 0;
|
|
108
|
-
let loadingTimer = null;
|
|
109
|
-
|
|
110
|
-
function startLoading() {
|
|
111
|
-
loadingFrame = 0;
|
|
112
|
-
process.stdout.write(chalk.gray(' ' + loadingFrames[0]));
|
|
113
|
-
loadingTimer = setInterval(() => {
|
|
114
|
-
process.stdout.write('\r' + chalk.gray(' ' + loadingFrames[loadingFrame]));
|
|
115
|
-
loadingFrame = (loadingFrame + 1) % loadingFrames.length;
|
|
116
|
-
}, 300);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
function stopLoading() {
|
|
120
|
-
if (loadingTimer) { clearInterval(loadingTimer); loadingTimer = null; }
|
|
121
|
-
process.stdout.write('\r\x1b[2K');
|
|
122
|
-
}
|
|
123
|
-
|
|
124
279
|
// ── Session özeti ─────────────────────────────────────────────────────────
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
console.log(chalk.
|
|
128
|
-
console.log(chalk.green(
|
|
129
|
-
console.log(chalk.green(
|
|
130
|
-
console.log(chalk.cyan(
|
|
131
|
-
console.log();
|
|
280
|
+
function showSummary() {
|
|
281
|
+
console.log(chalk.gray('\n─── Agent Session Özeti ───'));
|
|
282
|
+
console.log(` ${chalk.green('✓')} ${stats.filesChanged} dosya değiştirildi`);
|
|
283
|
+
console.log(` ${chalk.green('✓')} ${stats.commandsRun} komut çalıştırıldı`);
|
|
284
|
+
console.log(` ${chalk.green('✓')} ${stats.toolCallCount} tool çağrısı yapıldı`);
|
|
285
|
+
console.log(` ${chalk.cyan('◉')} ${stats.messageCount} mesaj\n`);
|
|
132
286
|
}
|
|
133
287
|
|
|
134
|
-
// ── Mesaj
|
|
288
|
+
// ── Mesaj gönder + tool loop ──────────────────────────────────────────────
|
|
135
289
|
async function handleMessage(userMessage) {
|
|
136
290
|
userMessage = userMessage.trim();
|
|
137
291
|
if (!userMessage) return;
|
|
138
292
|
|
|
293
|
+
// Slash komutları
|
|
139
294
|
if (userMessage.startsWith('/')) {
|
|
140
295
|
const [cmd] = userMessage.slice(1).split(' ');
|
|
141
296
|
switch (cmd.toLowerCase()) {
|
|
142
297
|
case 'help':
|
|
143
|
-
console.log(chalk.yellow('Code Komutları:'));
|
|
298
|
+
console.log(chalk.yellow('Code Agent Komutları:'));
|
|
144
299
|
[
|
|
145
|
-
['/clear',
|
|
300
|
+
['/clear', 'Ekranı temizle'],
|
|
146
301
|
['/summary', 'Session özetini göster'],
|
|
147
|
-
['/
|
|
148
|
-
|
|
149
|
-
console.log(
|
|
302
|
+
['/done', 'Bitir ve özet göster'],
|
|
303
|
+
['/help', 'Bu yardım'],
|
|
304
|
+
].forEach(([c, d]) => console.log(' ' + chalk.cyan(c.padEnd(12)) + chalk.gray(d)));
|
|
305
|
+
console.log(chalk.gray(' Ctrl+C'.padEnd(14) + 'Çıkış'));
|
|
150
306
|
console.log();
|
|
151
307
|
return;
|
|
152
308
|
case 'clear':
|
|
153
309
|
console.clear();
|
|
154
310
|
return;
|
|
155
311
|
case 'summary':
|
|
156
|
-
|
|
312
|
+
case 'done':
|
|
313
|
+
showSummary();
|
|
314
|
+
if (cmd === 'done') process.exit(0);
|
|
157
315
|
return;
|
|
158
316
|
default:
|
|
159
|
-
console.log(chalk.red(`Bilinmeyen komut: /${cmd}`));
|
|
160
|
-
console.log();
|
|
317
|
+
console.log(chalk.red(`Bilinmeyen komut: /${cmd}\n`));
|
|
161
318
|
return;
|
|
162
319
|
}
|
|
163
320
|
}
|
|
164
321
|
|
|
165
322
|
if (userMessage === 'exit' || userMessage === 'quit') {
|
|
166
|
-
|
|
323
|
+
showSummary();
|
|
167
324
|
process.exit(0);
|
|
168
325
|
}
|
|
169
326
|
|
|
170
|
-
|
|
327
|
+
stats.messageCount++;
|
|
171
328
|
console.log(chalk.white('You ') + userMessage);
|
|
172
|
-
startLoading();
|
|
173
329
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
let
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
console.log(chalk.
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
`Tool: ${tr.name}\nResult: ${tr.result.success ? (tr.result.output || JSON.stringify(tr.result)) : tr.result.error}`
|
|
191
|
-
).join('\n\n');
|
|
192
|
-
startLoading();
|
|
193
|
-
response = await _sendMessage(apiKey || config.providerApiKey, bot.id, toolMsg, conversationId, systemPrompt, toolDefinitions);
|
|
194
|
-
stopLoading();
|
|
195
|
-
if (response.conversation_id) conversationId = response.conversation_id;
|
|
196
|
-
iter++;
|
|
330
|
+
// Mesajı history'e ekle
|
|
331
|
+
conversationMessages.push({ role: 'user', content: userMessage });
|
|
332
|
+
|
|
333
|
+
// ── Streaming + tool loop (max 20 iter) ──────────────────────────────────
|
|
334
|
+
let iter = 0;
|
|
335
|
+
while (iter < 20) {
|
|
336
|
+
iter++;
|
|
337
|
+
|
|
338
|
+
let streamResult;
|
|
339
|
+
try {
|
|
340
|
+
process.stdout.write(chalk.cyan(`${displayBotName} `));
|
|
341
|
+
streamResult = await streamMessage(providerConfig, conversationMessages, tools);
|
|
342
|
+
} catch (err) {
|
|
343
|
+
console.log(chalk.red(`\nError: ${err.message}\n`));
|
|
344
|
+
conversationMessages.pop(); // başarısız user mesajını geri al
|
|
345
|
+
return;
|
|
197
346
|
}
|
|
198
347
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
348
|
+
// Assistant mesajını history'e ekle
|
|
349
|
+
const assistantMsg = { role: 'assistant', content: streamResult.text || '' };
|
|
350
|
+
if (streamResult.toolCalls?.length) {
|
|
351
|
+
assistantMsg.tool_calls = streamResult.toolCalls.map(tc => ({
|
|
352
|
+
id: tc.id || `call_${Date.now()}_${Math.random().toString(36).slice(2)}`,
|
|
353
|
+
type: 'function',
|
|
354
|
+
function: { name: tc.name, arguments: JSON.stringify(tc.input) },
|
|
355
|
+
}));
|
|
356
|
+
}
|
|
357
|
+
conversationMessages.push(assistantMsg);
|
|
358
|
+
|
|
359
|
+
// Tool call yoksa bitti
|
|
360
|
+
if (!streamResult.toolCalls?.length) break;
|
|
361
|
+
|
|
362
|
+
// Tool call'ları çalıştır
|
|
363
|
+
console.log(chalk.yellow(`\n🔧 ${streamResult.toolCalls.length} tool çalıştırılıyor...\n`));
|
|
364
|
+
|
|
365
|
+
for (const toolCall of streamResult.toolCalls) {
|
|
366
|
+
const result = await runToolCall(toolCall, stats);
|
|
367
|
+
const resultStr = result.success !== false
|
|
368
|
+
? (result.output || JSON.stringify(result))
|
|
369
|
+
: `Hata: ${result.error}`;
|
|
370
|
+
|
|
371
|
+
conversationMessages.push({
|
|
372
|
+
role: 'tool',
|
|
373
|
+
tool_call_id: assistantMsg.tool_calls?.find(tc => tc.function.name === toolCall.name)?.id || toolCall.id,
|
|
374
|
+
name: toolCall.name,
|
|
375
|
+
content: resultStr.slice(0, 3000),
|
|
376
|
+
});
|
|
377
|
+
}
|
|
205
378
|
|
|
206
|
-
} catch (err) {
|
|
207
|
-
stopLoading();
|
|
208
|
-
const errMsg = err.message.split('"message":"')[1]?.split('"')[0] || err.message;
|
|
209
|
-
console.log(chalk.red(`Error: ${errMsg}`));
|
|
210
379
|
console.log();
|
|
211
380
|
}
|
|
381
|
+
|
|
382
|
+
// Session'a kaydet
|
|
383
|
+
const lastAssistant = [...conversationMessages].reverse().find(m => m.role === 'assistant');
|
|
384
|
+
if (lastAssistant?.content) {
|
|
385
|
+
addToHistory(bot.id, userMessage, lastAssistant.content, null);
|
|
386
|
+
addMessageToSession(bot.id, session.id, userMessage, lastAssistant.content);
|
|
387
|
+
}
|
|
212
388
|
}
|
|
213
389
|
|
|
214
390
|
// ── Input loop ───────────────────────────────────────────────────────────────
|
|
215
391
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout, terminal: true });
|
|
216
392
|
|
|
217
|
-
process.on('SIGINT',
|
|
393
|
+
process.on('SIGINT', () => {
|
|
218
394
|
rl.close();
|
|
219
|
-
|
|
395
|
+
showSummary();
|
|
220
396
|
console.log(chalk.gray('👋 Goodbye!\n'));
|
|
221
397
|
process.exit(0);
|
|
222
398
|
});
|
package/src/utils/api.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
const fs = require('fs');
|
|
5
5
|
const os = require('os');
|
|
6
6
|
const path = require('path');
|
|
7
|
+
const chalk = require('chalk');
|
|
7
8
|
const { getConfig } = require('./config');
|
|
8
9
|
const { getToolDefinitions, executeToolCalls } = require('./tool-runner');
|
|
9
10
|
const { MCPClient } = require('./mcp-client');
|
|
@@ -428,8 +429,7 @@ function formatToolsForAnthropic() {
|
|
|
428
429
|
*/
|
|
429
430
|
async function sendMessageOpenAICompatible(providerConfig, messages, tools) {
|
|
430
431
|
const endpoint = `${providerConfig.url}/chat/completions`;
|
|
431
|
-
|
|
432
|
-
// Log request size before sending
|
|
432
|
+
|
|
433
433
|
const requestBody = {
|
|
434
434
|
model: providerConfig.model,
|
|
435
435
|
messages: messages,
|
|
@@ -438,8 +438,12 @@ async function sendMessageOpenAICompatible(providerConfig, messages, tools) {
|
|
|
438
438
|
temperature: 0.7,
|
|
439
439
|
max_tokens: 2000,
|
|
440
440
|
};
|
|
441
|
-
|
|
442
|
-
|
|
441
|
+
|
|
442
|
+
// NatureCo backend proxy — tool call'ları Groq'a iletmek için explicitly set
|
|
443
|
+
if (providerConfig.url.includes('api.natureco.me')) {
|
|
444
|
+
requestBody.tool_choice = 'auto';
|
|
445
|
+
requestBody.tools = tools;
|
|
446
|
+
}
|
|
443
447
|
|
|
444
448
|
const response = await fetch(endpoint, {
|
|
445
449
|
method: 'POST',
|
|
@@ -447,7 +451,7 @@ async function sendMessageOpenAICompatible(providerConfig, messages, tools) {
|
|
|
447
451
|
'Authorization': `Bearer ${providerConfig.apiKey}`,
|
|
448
452
|
'Content-Type': 'application/json',
|
|
449
453
|
},
|
|
450
|
-
body:
|
|
454
|
+
body: JSON.stringify(requestBody),
|
|
451
455
|
});
|
|
452
456
|
|
|
453
457
|
if (!response.ok) {
|
|
@@ -588,27 +592,36 @@ async function sendMessageToProvider(apiKey, message, conversationId = null, sys
|
|
|
588
592
|
}));
|
|
589
593
|
|
|
590
594
|
const toolResults = [];
|
|
595
|
+
const SPINNER_FRAMES = ['⠋','⠙','⠹','⠸','⠼','⠴','⠦','⠧','⠇','⠏'];
|
|
591
596
|
|
|
592
597
|
for (const toolCall of toolCalls) {
|
|
598
|
+
// Spinner başlat
|
|
599
|
+
let frameIdx = 0;
|
|
600
|
+
const inputPreview = JSON.stringify(toolCall.input).slice(0, 50);
|
|
601
|
+
const spinner = setInterval(() => {
|
|
602
|
+
process.stdout.write(`\r ${chalk.cyan(SPINNER_FRAMES[frameIdx++ % SPINNER_FRAMES.length])} ${chalk.gray(toolCall.name + ' — ' + inputPreview)}`);
|
|
603
|
+
}, 80);
|
|
604
|
+
|
|
593
605
|
// Check if this is an MCP tool
|
|
594
606
|
const mcpTools = getMcpTools();
|
|
595
607
|
const isMcpTool = mcpTools.find(t => t.name === toolCall.name);
|
|
608
|
+
let result;
|
|
596
609
|
|
|
597
610
|
if (isMcpTool) {
|
|
598
|
-
// Execute MCP tool
|
|
599
611
|
debugLog(`[MCP] Executing tool: ${toolCall.name}`);
|
|
600
|
-
|
|
601
|
-
toolResults.push({
|
|
602
|
-
id: toolCall.id,
|
|
603
|
-
name: toolCall.name,
|
|
604
|
-
result: result
|
|
605
|
-
});
|
|
612
|
+
result = await executeMcpTool(toolCall.name, toolCall.input);
|
|
613
|
+
toolResults.push({ id: toolCall.id, name: toolCall.name, result });
|
|
606
614
|
} else {
|
|
607
|
-
// Execute local tool
|
|
608
615
|
debugLog(`[Local] Executing tool: ${toolCall.name}`);
|
|
609
616
|
const localResults = await executeToolCalls([toolCall]);
|
|
610
617
|
toolResults.push(...localResults);
|
|
618
|
+
result = localResults[0]?.result;
|
|
611
619
|
}
|
|
620
|
+
|
|
621
|
+
// Spinner durdur, sonucu göster
|
|
622
|
+
clearInterval(spinner);
|
|
623
|
+
const success = result?.success !== false;
|
|
624
|
+
process.stdout.write(`\r ${success ? chalk.green('✓') : chalk.red('✗')} ${chalk.cyan(toolCall.name)} ${chalk.gray('— ' + inputPreview)}\n`);
|
|
612
625
|
}
|
|
613
626
|
|
|
614
627
|
// Add tool results to messages (base64 encoded for safety)
|