natureco-cli 5.29.0 → 5.31.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/CHANGELOG.md +19 -0
- package/package.json +1 -1
- package/src/commands/repl.js +9 -3
- package/src/tools/agentic-runner.js +18 -10
- package/src/tools/workflow.js +43 -3
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to NatureCo CLI will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [5.31.0] - 2026-07-04 — "CHAT/CODE ARAYÜZ ZENGİNLEŞTİRME (araç görünürlüğü + düşünme + input alanı)"
|
|
6
|
+
|
|
7
|
+
### ✨ Yeni (UX)
|
|
8
|
+
- **Araç görünürlugu**: agentic akista her arac ekranda gorunur — "🔧 <etiket> · <ozet> ✓/✗" (write_file/edit_file/bash/browser/mac_app_open...). Onceden streaming tool XML'ini gizlerken arac aktivitesi de gorunmuyordu; artik gorunur.
|
|
9
|
+
- **Düsünme gostergesi**: model yanit uretirken "💭 düşünüyor…", ilk token gelince temizlenir.
|
|
10
|
+
- **Gorunur input alani**: REPL prompt'u "💬 Sen ▸" + her girdiden once ince ayirici cizgi (cikti/girdi net ayrilir; readline tek-satir → satir duzenleme bozulmaz).
|
|
11
|
+
- **Gorunur acma yonlendirmesi (full mod)**: "kendi tarayicimda ac / dinlemek istiyorum" → headless `browser` yerine gorunur `open`/`start`/`mac_app_open` kullanilir.
|
|
12
|
+
|
|
13
|
+
Doğrulama: streaming UI E2E (💭 + 🔧 + ✓ gorunuyor); REPL regresyon; **492 test yeşil**.
|
|
14
|
+
|
|
15
|
+
## [5.30.0] - 2026-07-04 — "TAM KONTROL MODU (sahip opt-in: tüm araç+skill + computer-use)"
|
|
16
|
+
|
|
17
|
+
### ✨ Yeni
|
|
18
|
+
- **Tam kontrol modu** (`natureco config set agentExec full` veya `NATURECO_AGENT_EXEC=full`): sahibi opt-in yapinca ajan **TUM araclara + skill'lere** erisir ve **her shell komutunu** calistirabilir (yikici komutlar — rm -rf — tam modda bile bloklu kalir). Computer-use araclari: `mac_app_open` (WhatsApp/Chrome/Spotify ac), `browser` (Playwright otomasyonu), `computer_use` (GUI: tikla/yaz/ekran goruntusu), `social_open` (muzik/video), `macos_screenshot`. Full modda sistem prompt'u bu araclari modele tanitir ("yapamam deme, ilgili araci cagir").
|
|
19
|
+
- Safe modda (varsayilan) `open`/`start`/`xdg-open` (uygulama/URL ac) da izinli — asistan temel acma islerini yapar.
|
|
20
|
+
- **Iki kademeli guvenlik**: Safe (varsayilan, milyonlar) = dosya araclari + guvenli shell. Full (sahip opt-in) = her sey. Katmanli: yikici-komut guard her modda aktif.
|
|
21
|
+
|
|
22
|
+
Doğrulama: full modda `browser` araci E2E (example.com → "Example Domain"); **492 test yeşil**, ESLint temiz.
|
|
23
|
+
|
|
5
24
|
## [5.29.0] - 2026-07-04 — "ask KUTU-ÇIKIŞI DÜZELTMESİ + çekirdek komut denetimi"
|
|
6
25
|
|
|
7
26
|
### 🐛 Düzeltme
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "natureco-cli",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.31.0",
|
|
4
4
|
"description": "OpenClaw'dan daha güvenli, daha hızlı, daha ucuz AI agent CLI. Multi-agent, self-evolving skills, audit log, maliyet optimizasyonu ve NatureCo platform-native.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"natureco": "bin/natureco.js"
|
package/src/commands/repl.js
CHANGED
|
@@ -1178,14 +1178,20 @@ async function startRepl(args) {
|
|
|
1178
1178
|
const rl = readline.createInterface({
|
|
1179
1179
|
input: createPasteSafeInput(process.stdin),
|
|
1180
1180
|
output: createOutputFilter(process.stdout),
|
|
1181
|
-
prompt: tui.styled('
|
|
1181
|
+
prompt: tui.styled(' 💬 Sen ▸ ', { color: tui.PALETTE.primary, bold: true }),
|
|
1182
1182
|
terminal: true,
|
|
1183
1183
|
});
|
|
1184
1184
|
// Pipe/script kullanımında EOF, yanıt hâlâ üretilirken gelir —
|
|
1185
1185
|
// aktif işlem varken kapanışı bekletmek için sayaç + kapalı-rl koruması
|
|
1186
1186
|
let _busy = 0;
|
|
1187
1187
|
let _rlClosed = false;
|
|
1188
|
-
|
|
1188
|
+
// Görünür input alanı: her prompttan önce ince ayırıcı çizgi (readline prompt'u tek-satır
|
|
1189
|
+
// kalir → satir duzenleme/gecmis bozulmaz). Cikti ile girdi arasini net ayirir.
|
|
1190
|
+
const safePrompt = () => {
|
|
1191
|
+
if (_rlClosed) return;
|
|
1192
|
+
try { process.stdout.write(tui.styled('\n ' + '─'.repeat(54) + '\n', { color: tui.PALETTE.muted })); } catch {}
|
|
1193
|
+
rl.prompt();
|
|
1194
|
+
};
|
|
1189
1195
|
safePrompt();
|
|
1190
1196
|
|
|
1191
1197
|
const cleanup = async (exitCode = 0) => {
|
|
@@ -1465,7 +1471,7 @@ async function startRepl(args) {
|
|
|
1465
1471
|
|
|
1466
1472
|
// Çok satırlı (paste) mesajları gönderildikten sonra ekranda göster
|
|
1467
1473
|
if (line.indexOf('\n') !== -1) {
|
|
1468
|
-
process.stdout.write(tui.styled('
|
|
1474
|
+
process.stdout.write(tui.styled(' 💬 Sen ▸ ', { color: tui.PALETTE.primary, bold: true }));
|
|
1469
1475
|
process.stdout.write(line + '\n');
|
|
1470
1476
|
}
|
|
1471
1477
|
|
|
@@ -62,13 +62,18 @@ const AGENT_EXEC_ALLOW = new Set([
|
|
|
62
62
|
'ls', 'dir', 'cat', 'type', 'head', 'tail', 'pwd', 'echo', 'printf', 'grep', 'findstr',
|
|
63
63
|
'find', 'wc', 'which', 'where', 'whereis', 'tree', 'stat', 'file', 'sort', 'uniq', 'diff',
|
|
64
64
|
'mkdir', 'touch', 'cp', 'copy', 'mv', 'move', 'git', 'jq', 'sed', 'awk', 'date', 'env', 'whoami',
|
|
65
|
+
'open', 'start', 'xdg-open', 'explorer', // uygulama/URL/dosya ac (dusuk risk, asistan icin gerekli)
|
|
65
66
|
]);
|
|
66
67
|
const AGENT_EXEC_BLOCK_PATTERNS = [
|
|
67
68
|
/\bnpm\s+publish\b/i, /\byarn\s+publish\b/i, /\bpnpm\s+publish\b/i,
|
|
68
69
|
/\bgit\s+push\b/i, /\bgit\s+remote\s+(add|set-url)\b/i,
|
|
69
70
|
/\bnpm\s+(un)?deprecate\b/i, /\bnpm\s+owner\b/i,
|
|
70
71
|
];
|
|
71
|
-
|
|
72
|
+
// Full modda ("agentExec: full") acilan computer-use araclari: tarayici otomasyonu,
|
|
73
|
+
// uygulama ac/kapat, GUI (tikla/yaz/ekran goruntusu). Safe modda (varsayilan) KAPALI.
|
|
74
|
+
const FULL_TOOLS = ['browser', 'browser_use', 'mac_app_open', 'mac_app_quit', 'social_open', 'computer_use', 'macos_screenshot'];
|
|
75
|
+
function agentExecAllowed(command, opts) {
|
|
76
|
+
if (opts && opts.full) return true;
|
|
72
77
|
if (String(process.env.NATURECO_AGENT_EXEC || '').toLowerCase() === 'full') return true;
|
|
73
78
|
const cmd = String(command || '').trim();
|
|
74
79
|
if (!cmd) return false;
|
|
@@ -262,9 +267,10 @@ async function executeCall(call, opts = {}) {
|
|
|
262
267
|
return { records, feedback: feedbacks.join('\n') };
|
|
263
268
|
}
|
|
264
269
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
270
|
+
// Full modda (sahibin opt-in'i) TUM araclar acik; safe modda sadece allowlist.
|
|
271
|
+
if (!opts.execFull && !allowed.has(norm)) {
|
|
272
|
+
records.push({ tool: rawTool, status: 'error', error: 'Guvenli modda kapali arac: ' + rawTool });
|
|
273
|
+
return { records, feedback: `${rawTool}: guvenli modda kapali. Tum arac+skill'ler icin tam mod: "natureco config set agentExec full" (veya NATURECO_AGENT_EXEC=full). Guvenli modda acik: ${[...allowed].join(', ')}` };
|
|
268
274
|
}
|
|
269
275
|
|
|
270
276
|
const args = { ...(call.args || {}) };
|
|
@@ -285,10 +291,10 @@ async function executeCall(call, opts = {}) {
|
|
|
285
291
|
records.push({ tool: 'bash', status: 'error', args: { command: cmd }, error: 'Yikici/tehlikeli komut ajan modunda engellendi' });
|
|
286
292
|
return { records, feedback: `bash: "${cmd.slice(0, 80)}" yikici/tehlikeli goruldugu icin ajan tarafindan CALISTIRILMADI. Gerekirse kullanici komutu kendisi calistirabilir.` };
|
|
287
293
|
}
|
|
288
|
-
const execOk = opts.agentExecAllowed || agentExecAllowed;
|
|
294
|
+
const execOk = opts.agentExecAllowed || ((c) => agentExecAllowed(c, { full: opts.execFull }));
|
|
289
295
|
if (!execOk(cmd)) {
|
|
290
|
-
records.push({ tool: 'bash', status: 'error', args: { command: cmd }, error: '
|
|
291
|
-
return { records, feedback: `bash: "${cmd.slice(0, 80)}"
|
|
296
|
+
records.push({ tool: 'bash', status: 'error', args: { command: cmd }, error: 'Guvenli mod exec politikasi disinda' });
|
|
297
|
+
return { records, feedback: `bash: "${cmd.slice(0, 80)}" guvenli mod politikasi disinda (ag/yayin/sistem). Tam kontrol icin: "natureco config set agentExec full" (veya NATURECO_AGENT_EXEC=full). Yikici komutlar (rm -rf) tam modda bile bloklu.` };
|
|
292
298
|
}
|
|
293
299
|
}
|
|
294
300
|
|
|
@@ -320,12 +326,12 @@ async function executeCall(call, opts = {}) {
|
|
|
320
326
|
* callModel(messages) => Promise<{ content, toolCalls }>
|
|
321
327
|
* Donus: { records, reply, iterations }
|
|
322
328
|
*/
|
|
323
|
-
async function runAgentic({ callModel, systemPrompt, historyMessages, task, toolsDir, loadTool, allowed, maxIterations = 15 }) {
|
|
329
|
+
async function runAgentic({ callModel, systemPrompt, historyMessages, task, toolsDir, loadTool, allowed, execFull, onEvent, maxIterations = 15 }) {
|
|
324
330
|
const messages = [{ role: 'system', content: systemPrompt }];
|
|
325
331
|
for (const mm of historyMessages || []) messages.push({ role: mm.role, content: mm.content || '' });
|
|
326
332
|
messages.push({ role: 'user', content: task });
|
|
327
333
|
|
|
328
|
-
const allowedSet = allowed ? new Set(allowed) : new Set(DEFAULT_ALLOWED);
|
|
334
|
+
const allowedSet = allowed ? new Set(allowed) : new Set([...DEFAULT_ALLOWED, ...(execFull ? FULL_TOOLS : [])]);
|
|
329
335
|
const allRecords = [];
|
|
330
336
|
let finalReply = '';
|
|
331
337
|
let iterations = 0;
|
|
@@ -343,7 +349,9 @@ async function runAgentic({ callModel, systemPrompt, historyMessages, task, tool
|
|
|
343
349
|
messages.push({ role: 'assistant', content: content || '' });
|
|
344
350
|
const feedbacks = [];
|
|
345
351
|
for (const call of calls) {
|
|
346
|
-
|
|
352
|
+
if (onEvent) { try { onEvent({ phase: 'start', tool: call.tool, args: call.args }); } catch {} }
|
|
353
|
+
const { records, feedback } = await executeCall(call, { toolsDir, loadTool, allowed: allowedSet, execFull });
|
|
354
|
+
if (onEvent) { try { onEvent({ phase: 'end', tool: call.tool, args: call.args, records }); } catch {} }
|
|
347
355
|
allRecords.push(...records);
|
|
348
356
|
feedbacks.push(feedback);
|
|
349
357
|
}
|
package/src/tools/workflow.js
CHANGED
|
@@ -222,6 +222,23 @@ async function workflow(params) {
|
|
|
222
222
|
const { runAgentic } = require('./agentic-runner');
|
|
223
223
|
const memCtx = memoryContext();
|
|
224
224
|
const desktop = path.join(os.homedir(), 'Desktop');
|
|
225
|
+
// Tam mod (sahibin opt-in'i): tum arac+skill'ler acilir, keyfi shell (yikici haric).
|
|
226
|
+
const execFull = cfg.agentExec === 'full' || cfg.computerUse === true || String(process.env.NATURECO_AGENT_EXEC || '').toLowerCase() === 'full';
|
|
227
|
+
let fullToolsBlock = '';
|
|
228
|
+
if (execFull) {
|
|
229
|
+
const allNames = tools.filter(t => t !== 'workflow');
|
|
230
|
+
fullToolsBlock = [
|
|
231
|
+
'\n\nTAM MOD ACIK — su araclara da ERISIMIN VAR; o an ne gerekiyorsa dogrudan cagir:',
|
|
232
|
+
'- mac_app_open: macOS uygulamasi ac. parametre: appName (orn. "WhatsApp", "Google Chrome", "Spotify")',
|
|
233
|
+
'- mac_app_quit: macOS uygulamasi kapat. parametre: appName',
|
|
234
|
+
'- browser: HEADLESS tarayici otomasyonu (icerik cek/screenshot — kullaniciya GORUNMEZ). parametreler: action, url, script',
|
|
235
|
+
'- computer_use: GUI otomasyonu. parametreler: action ("screenshot"/"click"/"type"/"keypress"/"scroll"), x, y, text, key',
|
|
236
|
+
'- social_open: muzik/video/sosyal ac. parametreler: query, platform (spotify/youtube...)',
|
|
237
|
+
'- macos_screenshot: ekran goruntusu al',
|
|
238
|
+
'\nGORUNUR ACMA (onemli): Kullanici "kendi tarayicimda ac / gorunur ac / dinlemek/izlemek istiyorum" derse `browser` (headless, gorunmez) DEGIL, GORUNUR ac: macOS bash ile `open "https://..."` ya da `open -a "Google Chrome" "https://..."`; Windows `start "" "https://..."`. Uygulama icin `open -a WhatsApp` / mac_app_open. Muzik/video icin dogrudan YouTube/Spotify URL\'sini `open` ile ac.',
|
|
239
|
+
'\nTum arac listesi (isimle cagir; parametre yanlissa <tool_results> duzeltir): ' + allNames.join(', '),
|
|
240
|
+
].join('\n');
|
|
241
|
+
}
|
|
225
242
|
const sysMsg = [
|
|
226
243
|
'Sen NatureCo adli, arac kullanabilen bir yapay zeka ajanisin. Kullanicinin istegini SADECE anlatarak degil, ARACLARI cagirarak fiilen gerceklestir.',
|
|
227
244
|
memCtx ? '\nKullanici bilgisi:\n' + memCtx : '',
|
|
@@ -244,6 +261,8 @@ async function workflow(params) {
|
|
|
244
261
|
'- Goreceli yol yerine TAM yol kullan.',
|
|
245
262
|
'- Arac sonuclari <tool_results> icinde geri gelir; gorev bitince ARAC CAGIRMADAN tek cumlelik ozet yaz.',
|
|
246
263
|
'- Basit sohbet/selamlasma ise arac cagirma, dogrudan kisa yanit ver.',
|
|
264
|
+
execFull ? '- Kullanici uygulama ac / tarayici kontrol / muzik cal / ekran / GUI istediyse yukaridaki computer-use araclarini KULLAN — "yapamam/engellendi" DEME, dogrudan ilgili araci cagir.' : '',
|
|
265
|
+
fullToolsBlock,
|
|
247
266
|
skillsIndexBlock ? '\n\n' + skillsIndexBlock : '',
|
|
248
267
|
].filter(Boolean).join('\n');
|
|
249
268
|
|
|
@@ -260,11 +279,14 @@ async function workflow(params) {
|
|
|
260
279
|
|
|
261
280
|
async function callModel(msgs) {
|
|
262
281
|
if (streamOn) {
|
|
263
|
-
|
|
282
|
+
let cleared = false;
|
|
283
|
+
const clearThinking = () => { if (!cleared) { process.stdout.write('\r\x1b[K'); cleared = true; } };
|
|
284
|
+
process.stdout.write('\x1b[2m 💭 düşünüyor…\x1b[0m');
|
|
285
|
+
const sani = makeSanitizeStream(botName, t => { clearThinking(); process.stdout.write(t); });
|
|
264
286
|
const filter = makeStreamFilter(t => sani.push(t), null);
|
|
265
287
|
const body = { model, stream: true, messages: msgs, temperature: 0.3, max_tokens: 16000 };
|
|
266
288
|
const out = await apiCallStream(providerUrl, providerApiKey, body, d => filter.push(d));
|
|
267
|
-
filter.end(); sani.end();
|
|
289
|
+
filter.end(); sani.end(); clearThinking();
|
|
268
290
|
return { content: out.content || '', toolCalls: out.toolCalls || [] };
|
|
269
291
|
}
|
|
270
292
|
const body = { model, stream: false, messages: msgs, temperature: 0.3, max_tokens: 16000 };
|
|
@@ -273,10 +295,28 @@ async function workflow(params) {
|
|
|
273
295
|
return { content: msg.content || '', toolCalls: msg.tool_calls || [] };
|
|
274
296
|
}
|
|
275
297
|
|
|
298
|
+
// Araç aktivitesi gösterimi (TTY streaming): her araç icin "🔧 label · hint ✓/✗"
|
|
299
|
+
const TOOL_LABEL = { write_file: 'dosya yaz', read_file: 'oku', edit_file: 'düzenle', bash: 'komut', file_search: 'ara', list_dir: 'listele', skill_view: 'skill', browser: 'tarayıcı', browser_use: 'tarayıcı', mac_app_open: 'uygulama aç', mac_app_quit: 'uygulama kapat', computer_use: 'GUI', social_open: 'medya aç', macos_screenshot: 'ekran görüntüsü' };
|
|
300
|
+
function briefHint(args) {
|
|
301
|
+
if (!args || typeof args !== 'object') return '';
|
|
302
|
+
const v = args.appName || args.query || args.name || args.url || args.command || args.pattern || args.path || args.action;
|
|
303
|
+
return v ? String(v).replace(/\s+/g, ' ').slice(0, 46) : '';
|
|
304
|
+
}
|
|
305
|
+
const onEvent = streamOn ? (ev) => {
|
|
306
|
+
if (ev.phase === 'start') {
|
|
307
|
+
const label = TOOL_LABEL[ev.tool] || ev.tool;
|
|
308
|
+
const hint = briefHint(ev.args);
|
|
309
|
+
process.stdout.write('\n\x1b[2m 🔧 ' + label + (hint ? ' · ' + hint : '') + '\x1b[0m');
|
|
310
|
+
} else {
|
|
311
|
+
const rec = (ev.records || [])[0] || {};
|
|
312
|
+
process.stdout.write(rec.status === 'done' ? ' \x1b[32m✓\x1b[0m' : ' \x1b[31m✗\x1b[0m');
|
|
313
|
+
}
|
|
314
|
+
} : null;
|
|
315
|
+
|
|
276
316
|
try {
|
|
277
317
|
const { records, reply } = await runAgentic({
|
|
278
318
|
callModel, systemPrompt: sysMsg, historyMessages, task,
|
|
279
|
-
toolsDir: __dirname, maxIterations: 15,
|
|
319
|
+
toolsDir: __dirname, execFull, onEvent, maxIterations: 15,
|
|
280
320
|
});
|
|
281
321
|
const fileWrites = records.filter(r => r.tool === 'write_file' && r.status === 'done');
|
|
282
322
|
let finalReply = reply || '';
|