natureco-cli 5.50.1 → 5.51.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/CHANGELOG.md +24 -0
- package/README.md +4 -0
- package/SELF.md +13 -3
- package/bin/natureco.js +7 -0
- package/package.json +1 -1
- package/src/commands/account.js +100 -0
- package/src/tools/agentic-runner.js +1 -1
- package/src/tools/edit_file.js +8 -0
- package/src/tools/skill_find.js +53 -0
- package/src/tools/workflow.js +27 -39
- package/src/tools/write_file.js +9 -1
- package/src/utils/channel-brain.js +6 -0
- package/src/utils/natureco-account.js +120 -0
- package/src/utils/self-edit-guard.js +84 -0
- package/src/utils/skill-index.js +10 -3
- package/src/utils/tool-runner.js +36 -10
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,30 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to NatureCo CLI will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [5.51.1] - 2026-07-10 — "SECURITY: edit_file onay atlaması + kendi-kaynağını-düzenleme kısıtı"
|
|
6
|
+
|
|
7
|
+
### Security
|
|
8
|
+
- **`edit_file` onay/diff mekanizmasını atlıyordu (kritik).** tool-runner'ın `needsConfirm` kontrolü `write_file` için diff+onay isterken, aynı riski taşıyan hedefli değişiklik aracı `edit_file`'ı kapsamıyordu — SELF.md "kendini onar" protokolü ve Tek Beyin'in kanallara terminal-eşdeğeri araç erişimi ile birleşince, allow-list'teki bir hesaptan (veya prompt injection'dan) gelen mesaj paket kaynak kodunu gözetimsiz değiştirebilirdi. Düzeltmeler:
|
|
9
|
+
1. `edit_file` onay kapsamına alındı (`needsConfirmation` helper'ı çıkarıldı ve test edilebilir şekilde export edildi); onay ekranında `old_string` → `new_string` hedefli diff'i, dosya yolu ve `replace_all` uyarısı gösterilir.
|
|
10
|
+
2. **Kendi-kaynağını-düzenleme varsayılan KAPALI** (`src/utils/self-edit-guard.js`): hedef, paket kurulum kökü (veya herhangi bir `node_modules/natureco-cli`) altındaysa `edit_file`/`write_file` reddedilir; bilinçli açma `NATURECO_ALLOW_SELF_EDIT=1` env ya da config `allowSelfEdit: true` ile. Symlink/junction hilesi kapalı (hedef realpath'e çözülür — `~/.natureco/tools` bağlantısı paket içine açılır!). Koruma ARAÇ seviyesinde (v5.43 dersi: allowlist'e güvenme) — hem tool_calls hem agentic yolu kapsar.
|
|
11
|
+
3. **Kanal kaynaklı çağrılarda koşulsuz red:** `channel-brain` süreci `NATURECO_CHANNEL_ORIGIN=1` işaretler; bayrak/config açık olsa bile mesajlaşma kanalından paket kaynak koduna yazmak HER ZAMAN reddedilir (kanalda interaktif onay gösterilemez). Paket dışı dosyalar (ör. "masaüstüne oyun yap") kanaldan çalışmaya devam eder.
|
|
12
|
+
4. SELF.md onarma protokolüne yetki notu eklendi (okuma/teşhis her zaman serbest; yazma bilinçli bayrak ister; kanaldan asla).
|
|
13
|
+
- 8 regresyon testi (`test/security/edit-file-approval.test.js`, önce kırmızı→sonra yeşil): onay kapsamı, bayraksız red + dosya değişmedi garantisi, bayrakla açılma, kanal-kaynağında bayrağın yok sayılması, channel-brain'in işareti koyması, paket-dışı dosyaların etkilenmemesi. **638 test yeşil**; canlı E2E: ajan kendi tr-text.js'ini değiştirmeye çalıştı → engellendi, dosya değişmedi, kullanıcıya bilinçli açma yolunu anlattı.
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
- **`natureco account login | logout | whoami` — tek NatureCo hesabı (SSO).** developers.natureco.me API-KEY girişinden (`natureco login`, config.json) AYRIDIR; natureco.me Supabase Auth üstünde kişi kimliği: e-posta+şifre veya e-posta OTP; oturum `~/.natureco/auth.json` (0600), token yenileme. `natureco-sdk` `NatureCoAuth` ile aynı protokol → ekosistem geneli (CLI/terminal/portal) tek hesap. Bağımlılıksız (Supabase REST). Yeni: `src/utils/natureco-account.js`, `src/commands/account.js`.
|
|
17
|
+
|
|
18
|
+
## [5.51.0] - 2026-07-10 — "PERF: basit istek 2.698 token — %45 daha ucuz (skill keşfi isteğe bağlı)"
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
- **Gerçek ölçüm: "merhaba" prompt'u 4.883 → 2.698 token** (v5.42 öncesine göre toplam %86 iniş). Bölüm bazlı ölçüm yapıldı ve üç büyük kalem vuruldu:
|
|
22
|
+
- **Skill index (1.468 → ~40 token):** 319 skill'in isim listesi artık sysMsg'e GÖMÜLMÜYOR; tek satır ipucu var. Yeni **`skill_find(query)`** aracı (foldTr'li Türkçe-güvenli arama) ile ajan gerektiğinde arar, `skill_view(name)` ile yükler — progressive disclosure 2. seviye. Eski davranış env ile: `NATURECO_SKILL_INDEX=names|full`.
|
|
23
|
+
- **Araç tanıtımları (~892 → ~540 token)** ve **kurallar (~725 → ~405 token):** her kuralın öğrettiği ders korunarak metinler sıkıştırıldı (cron-daemon dürüstlüğü, kayıt kalitesi, bekleyen işler, "internet erişimim yok deme"...).
|
|
24
|
+
- E2E doğrulama (davranış bozulmadı): skill keşfi (ajan skill_find'ı kendisi çağırıp seo-optimization'ı buldu), dosya yazma (write_file ✓), hafıza recall ("Sen Gencay'sın, ben Hinata" ✓). **630 test yeşil** (+7 yeni: skill_find + yeni indeks varsayılanı).
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
- `src/tools/skill_find.js` — isim+açıklama üzerinde anahtar kelime skorlamalı skill arama (agentic allowlist'e eklendi).
|
|
28
|
+
|
|
5
29
|
## [5.50.1] - 2026-07-10 — "PERF: öz-bilgi bloğu sıkıştırıldı (~%55 daha az token)"
|
|
6
30
|
|
|
7
31
|
### Changed
|
package/README.md
CHANGED
|
@@ -576,3 +576,7 @@ MIT © [NatureCo](https://github.com/natureco-official)
|
|
|
576
576
|
<p align="center">
|
|
577
577
|
Made with 🌿 for developers who live in the terminal.
|
|
578
578
|
</p>
|
|
579
|
+
|
|
580
|
+
---
|
|
581
|
+
|
|
582
|
+
<sub>Part of the **NatureCo** ecosystem — [natureco.me](https://natureco.me) · NatureCo ekosisteminin parçası</sub>
|
package/SELF.md
CHANGED
|
@@ -56,9 +56,11 @@
|
|
|
56
56
|
### 4. Skill sistemi
|
|
57
57
|
- Yerleşikler: paket kökünde `skills/` (her biri `<ad>/SKILL.md`, YAML frontmatter).
|
|
58
58
|
Kullanıcınınkiler: `~/.natureco/skills/`. Proje: `./.natureco/skills/`.
|
|
59
|
-
- İndeks: `src/utils/skill-index.js` (`
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
- İndeks: `src/utils/skill-index.js` (`buildSkillIndex` — 60+ skill'de sysMsg'e yalnız
|
|
60
|
+
TEK SATIR ipucu gider; isim listesi bile gömülmez. `NATURECO_SKILL_INDEX` env:
|
|
61
|
+
off|names|full ile eski davranışlar açılır).
|
|
62
|
+
- Keşif: ajan `skill_find(query)` ile arar (`src/tools/skill_find.js`, foldTr'li),
|
|
63
|
+
`skill_view(name)` ile yükler (progressive disclosure). CLI: `src/commands/skills.js`
|
|
62
64
|
+ `src/utils/skills.js` (list/install/remove; indirme allowlist'i `skills-allowlist.json`).
|
|
63
65
|
- Görünürlük bağlantıları: `~/.natureco/skills-builtin` ve `~/.natureco/tools`
|
|
64
66
|
(junction/symlink, `src/utils/builtin-links.js`).
|
|
@@ -85,6 +87,14 @@
|
|
|
85
87
|
|
|
86
88
|
## Kendini onarma protokolü
|
|
87
89
|
|
|
90
|
+
> **YETKİ GEREKİR:** Kendi kaynak dosyalarına yazmak varsayılan olarak KAPALIDIR
|
|
91
|
+
> (güvenlik: prompt injection / kanal kaynaklı istekler kodu değiştirememeli).
|
|
92
|
+
> Kullanıcı bilinçli olarak açmalı: `NATURECO_ALLOW_SELF_EDIT=1` env değişkeni
|
|
93
|
+
> ya da config'te `allowSelfEdit: true`. Mesajlaşma kanalından (Telegram vb.)
|
|
94
|
+
> gelen isteklerde bu bayrak açık olsa bile kaynak koda yazmak HER ZAMAN reddedilir
|
|
95
|
+
> — kullanıcıya "bu işlem yalnızca terminalden yapılabilir" de. Okuma/teşhis
|
|
96
|
+
> (read_file, grep_search, node --check) her zaman serbesttir.
|
|
97
|
+
|
|
88
98
|
1. Belirtiyi netleştir: hangi komut/özellik, hata mesajı ne, `natureco status` çıktısı.
|
|
89
99
|
2. Bu haritadan ilgili akışı ve dosyayı bul; `read_file` ile oku (önce oku, sonra düzelt).
|
|
90
100
|
3. `grep_search` ile ilgili fonksiyon/hata metnini kaynakta ara (kurulum kökünde).
|
package/bin/natureco.js
CHANGED
|
@@ -12,6 +12,7 @@ try { require('../src/utils/update-check').maybeNotify(packageJson.version); } c
|
|
|
12
12
|
try { require('../src/utils/builtin-links').ensureBuiltinLinks(); } catch { /* asla komutu bozma */ }
|
|
13
13
|
const login = require('../src/commands/login');
|
|
14
14
|
const logout = require('../src/commands/logout');
|
|
15
|
+
const account = require('../src/commands/account');
|
|
15
16
|
const bots = require('../src/commands/bots');
|
|
16
17
|
const chat = require('../src/commands/chat');
|
|
17
18
|
const help = require('../src/commands/help');
|
|
@@ -101,6 +102,7 @@ ${chalk.yellow('⚙️ Kurulum & Ayarlar')}
|
|
|
101
102
|
${chalk.cyan('onboard')} Interaktif onboarding (gateway|auth|workspace|channels|skills|health|status)
|
|
102
103
|
${chalk.cyan('configure')} Interaktif yapılandırma (gateway|auth|channels|plugins|skills)
|
|
103
104
|
${chalk.cyan('login')} API key ile giriş
|
|
105
|
+
${chalk.cyan('account')} NatureCo hesabı / SSO (login|logout|whoami)
|
|
104
106
|
${chalk.cyan('logout')} Çıkış
|
|
105
107
|
${chalk.cyan('config')} Ayarlar (get|set|unset|list|file|schema|validate|backups|restore)
|
|
106
108
|
${chalk.cyan('doctor')} Sistem teşhis (run|list|check)
|
|
@@ -198,6 +200,11 @@ program
|
|
|
198
200
|
.description('Login with your NatureCo API key')
|
|
199
201
|
.action(login);
|
|
200
202
|
|
|
203
|
+
program
|
|
204
|
+
.command('account [action]')
|
|
205
|
+
.description('NatureCo hesabı / SSO (login | logout | whoami)')
|
|
206
|
+
.action(account);
|
|
207
|
+
|
|
201
208
|
program
|
|
202
209
|
.command('setup [action]')
|
|
203
210
|
.description('Run initial setup wizard (wizard|config|workspace|dirs|status)')
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "natureco-cli",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.51.1",
|
|
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"
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
const inquirer = require('../utils/inquirer-wrapper');
|
|
2
|
+
const chalk = require('chalk');
|
|
3
|
+
const acc = require('../utils/natureco-account');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* `natureco account [login|logout|whoami]` — tek NatureCo hesabı (SSO).
|
|
7
|
+
* developers.natureco.me API-KEY girişinden (`natureco login`) AYRIDIR:
|
|
8
|
+
* bu, natureco.me hesabınla kişi kimliği; ekosistem geneli paylaşılır.
|
|
9
|
+
*/
|
|
10
|
+
async function account(action) {
|
|
11
|
+
const sub = (action || 'whoami').toLowerCase();
|
|
12
|
+
if (sub === 'login' || sub === 'giris' || sub === 'giriş') return doLogin();
|
|
13
|
+
if (sub === 'logout' || sub === 'cikis' || sub === 'çıkış') return doLogout();
|
|
14
|
+
return doWhoami();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async function doLogin() {
|
|
18
|
+
console.log('');
|
|
19
|
+
console.log(chalk.green.bold(' (\\_/)'));
|
|
20
|
+
console.log(chalk.green.bold(' (•ᴥ•)'));
|
|
21
|
+
console.log(chalk.green(' />🌿'));
|
|
22
|
+
console.log('');
|
|
23
|
+
console.log(chalk.green.bold(' NatureCo Hesabı — Giriş'));
|
|
24
|
+
console.log(chalk.gray(' natureco.me hesabınla ekosistemin her yerinde tek kimlik.\n'));
|
|
25
|
+
console.log(chalk.gray(' ' + '─'.repeat(48)) + '\n');
|
|
26
|
+
|
|
27
|
+
const { email } = await inquirer.prompt([{
|
|
28
|
+
type: 'input',
|
|
29
|
+
name: 'email',
|
|
30
|
+
message: ' E-posta:',
|
|
31
|
+
validate: (v) => (/.+@.+\..+/.test((v || '').trim()) ? true : 'Geçerli bir e-posta gir'),
|
|
32
|
+
}]);
|
|
33
|
+
|
|
34
|
+
const { method } = await inquirer.prompt([{
|
|
35
|
+
type: 'list',
|
|
36
|
+
name: 'method',
|
|
37
|
+
message: ' Giriş yöntemi:',
|
|
38
|
+
choices: [
|
|
39
|
+
{ name: 'Şifre', value: 'password' },
|
|
40
|
+
{ name: 'E-postama kod gönder (OTP)', value: 'otp' },
|
|
41
|
+
],
|
|
42
|
+
}]);
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
if (method === 'password') {
|
|
46
|
+
const { password } = await inquirer.prompt([{ type: 'password', name: 'password', message: ' Şifre:', mask: '*' }]);
|
|
47
|
+
console.log(chalk.gray('\n Doğrulanıyor...'));
|
|
48
|
+
await acc.loginWithPassword(email.trim(), password);
|
|
49
|
+
} else {
|
|
50
|
+
console.log(chalk.gray('\n Kod gönderiliyor...'));
|
|
51
|
+
await acc.sendOtp(email.trim());
|
|
52
|
+
console.log(chalk.gray(' ') + chalk.cyan(email.trim()) + chalk.gray(' adresine 6 haneli kod gönderildi.'));
|
|
53
|
+
const { token } = await inquirer.prompt([{
|
|
54
|
+
type: 'input', name: 'token', message: ' Koddan gelen 6 hane:',
|
|
55
|
+
validate: (v) => ((v || '').trim().length >= 6 ? true : 'Kodu gir'),
|
|
56
|
+
}]);
|
|
57
|
+
console.log(chalk.gray('\n Doğrulanıyor...'));
|
|
58
|
+
await acc.verifyOtp(email.trim(), token.trim());
|
|
59
|
+
}
|
|
60
|
+
} catch (err) {
|
|
61
|
+
console.log(chalk.red(`\n ❌ ${err.message || 'Giriş başarısız'}\n`));
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const me = await acc.whoami();
|
|
66
|
+
console.log(chalk.green('\n ✓ Giriş başarılı!'));
|
|
67
|
+
if (me && me.email) console.log(chalk.gray(' Hoş geldin, ') + chalk.white(me.email));
|
|
68
|
+
console.log(chalk.gray(' Oturum: ~/.natureco/auth.json'));
|
|
69
|
+
console.log('');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async function doLogout() {
|
|
73
|
+
if (!acc.isLoggedIn()) {
|
|
74
|
+
console.log(chalk.gray('\n Zaten giriş yapılmamış.\n'));
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
const who = acc.currentEmail();
|
|
78
|
+
acc.logout();
|
|
79
|
+
console.log(chalk.green(`\n ✓ Çıkış yapıldı${who ? ' (' + who + ')' : ''}.\n`));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async function doWhoami() {
|
|
83
|
+
if (!acc.isLoggedIn()) {
|
|
84
|
+
console.log(chalk.gray('\n NatureCo hesabına giriş yapılmamış.'));
|
|
85
|
+
console.log(chalk.gray(' Giriş: ') + chalk.cyan('natureco account login') + '\n');
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
console.log(chalk.gray('\n Doğrulanıyor...'));
|
|
89
|
+
const me = await acc.whoami();
|
|
90
|
+
if (!me) {
|
|
91
|
+
console.log(chalk.yellow('\n ⚠ Oturum süresi dolmuş görünüyor. Tekrar giriş yap: ') + chalk.cyan('natureco account login') + '\n');
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
console.log(chalk.green.bold('\n ⬡ NatureCo Hesabı'));
|
|
95
|
+
console.log(chalk.gray(' E-posta: ') + chalk.white(me.email || '-'));
|
|
96
|
+
console.log(chalk.gray(' ID: ') + chalk.gray(me.id || '-'));
|
|
97
|
+
console.log('');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
module.exports = account;
|
|
@@ -22,7 +22,7 @@ const os = require('os');
|
|
|
22
22
|
// digerleri → allowlist/full moda gore). Yani keyfi/yikici komut calismaz.
|
|
23
23
|
// Diger ~85 arac (discord, telegram, cron, browser...) bilerek DISARIDA.
|
|
24
24
|
const DEFAULT_ALLOWED = [
|
|
25
|
-
'write_file', 'read_file', 'edit_file', 'skill_view', 'bash', 'file_search', 'list_dir', 'grep_search',
|
|
25
|
+
'write_file', 'read_file', 'edit_file', 'skill_view', 'skill_find', 'bash', 'file_search', 'list_dir', 'grep_search',
|
|
26
26
|
'memory_write', 'memory_tree', 'cron_create', 'duckduckgo', 'web_search', 'todo_write',
|
|
27
27
|
'git', 'http_request', 'notebook_edit', 'clarify', 'code_execution',
|
|
28
28
|
// orchestration + planlama (guvenli: sub_agent = alt-LLM cagrisi, plan = metin+.md; shell/sistem yok)
|
package/src/tools/edit_file.js
CHANGED
|
@@ -50,6 +50,14 @@ async function editFile({ path: filePath, old_string, new_string, replace_all =
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
const target = _expand(filePath);
|
|
53
|
+
|
|
54
|
+
// GÜVENLİK (v5.51.1): paketin kendi kaynak koduna yazma varsayılan kapalı;
|
|
55
|
+
// kanal kaynaklı çağrılarda koşulsuz red. Bkz. src/utils/self-edit-guard.js
|
|
56
|
+
const guard = require('../utils/self-edit-guard').checkSelfEdit(target);
|
|
57
|
+
if (!guard.allowed) {
|
|
58
|
+
return { success: false, error: guard.error, reason: guard.reason, path: target };
|
|
59
|
+
}
|
|
60
|
+
|
|
53
61
|
if (!fs.existsSync(target)) {
|
|
54
62
|
return {
|
|
55
63
|
success: false,
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* skill_find — istek üzerine skill keşfi (v5.51 token optimizasyonu).
|
|
3
|
+
*
|
|
4
|
+
* Eskiden 319 skill'in İSİM LİSTESİ her istekte sysMsg'e gömülüyordu (~1.450 token).
|
|
5
|
+
* Artık sysMsg'de tek satırlık ipucu var; ajan görev bir uzmanlık istediğinde bu
|
|
6
|
+
* araçla arar (isim+açıklama üzerinde anahtar-kelime skorlaması, foldTr ile
|
|
7
|
+
* Türkçe-güvenli), sonra skill_view(name) ile yükler. Progressive disclosure 2. seviye.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const { _discoverSkills, _shorten } = require('../utils/skill-index');
|
|
11
|
+
const { foldTr } = require('../utils/tr-text');
|
|
12
|
+
|
|
13
|
+
module.exports = {
|
|
14
|
+
name: 'skill_find',
|
|
15
|
+
description: 'Görevle ilgili skill ara (isim + açıklama üzerinde anahtar kelime eşleşmesi). Bulduğun skill\'i skill_view(name) ile yükle.',
|
|
16
|
+
inputSchema: {
|
|
17
|
+
type: 'object',
|
|
18
|
+
properties: {
|
|
19
|
+
query: { type: 'string', description: 'Aranacak konu/anahtar kelimeler (ör. "seo audit", "video üretimi", "api tasarımı")' },
|
|
20
|
+
maxResults: { type: 'number', description: 'En fazla sonuç (varsayılan 8)' },
|
|
21
|
+
},
|
|
22
|
+
required: ['query'],
|
|
23
|
+
},
|
|
24
|
+
async execute({ query, maxResults = 8 }) {
|
|
25
|
+
if (!query || !String(query).trim()) {
|
|
26
|
+
return { success: false, error: 'query gerekli' };
|
|
27
|
+
}
|
|
28
|
+
const words = foldTr(String(query)).split(/\s+/).filter(w => w.length > 1);
|
|
29
|
+
const skills = _discoverSkills();
|
|
30
|
+
|
|
31
|
+
const scored = skills.map(s => {
|
|
32
|
+
const name = foldTr(s.name || '');
|
|
33
|
+
const desc = foldTr(s.description || '');
|
|
34
|
+
let score = 0;
|
|
35
|
+
for (const w of words) {
|
|
36
|
+
if (name.includes(w)) score += 3; // isimde geçmesi en güçlü sinyal
|
|
37
|
+
else if (desc.includes(w)) score += 1;
|
|
38
|
+
}
|
|
39
|
+
return { s, score };
|
|
40
|
+
}).filter(r => r.score > 0)
|
|
41
|
+
.sort((a, b) => b.score - a.score)
|
|
42
|
+
.slice(0, Math.max(1, Math.min(20, maxResults)));
|
|
43
|
+
|
|
44
|
+
if (scored.length === 0) {
|
|
45
|
+
return { success: true, results: [], note: 'Eşleşen skill yok — farklı anahtar kelimelerle dene ya da skill olmadan araçlarla ilerle.' };
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
success: true,
|
|
49
|
+
results: scored.map(r => ({ name: r.s.name, description: _shorten(r.s.description, 100), category: r.s.category })),
|
|
50
|
+
note: 'Kullanmak için: skill_view(name)',
|
|
51
|
+
};
|
|
52
|
+
},
|
|
53
|
+
};
|
package/src/tools/workflow.js
CHANGED
|
@@ -271,46 +271,34 @@ async function workflow(params) {
|
|
|
271
271
|
memCtx ? '\nKullanici bilgisi:\n' + memCtx : '',
|
|
272
272
|
'\n\nOrtam:\n- Isletim sistemi: ' + process.platform + '\n- Kullanici home: ' + os.homedir() + '\n- Masaustu: ' + desktop + '\n- Calisma dizini: ' + process.cwd() + selfInfo,
|
|
273
273
|
'\n\nArac cagirmak icin TAM olarak su formati kullan:\n<minimax:tool_call>\n<invoke name="ARAC_ADI">\n<parameter name="PARAM">DEGER</parameter>\n</invoke>\n</minimax:tool_call>',
|
|
274
|
-
'\n\nKullanabilecegin araclar:',
|
|
275
|
-
'- write_file: dosya olustur/uzerine yaz.
|
|
276
|
-
'- read_file: dosya
|
|
277
|
-
'- edit_file: MEVCUT dosyada metin degistir.
|
|
278
|
-
'-
|
|
279
|
-
'-
|
|
280
|
-
'-
|
|
281
|
-
'-
|
|
282
|
-
'-
|
|
283
|
-
'-
|
|
284
|
-
'-
|
|
285
|
-
'-
|
|
286
|
-
'-
|
|
287
|
-
'-
|
|
288
|
-
'-
|
|
289
|
-
'-
|
|
290
|
-
'-
|
|
291
|
-
'- plan: karmasik/cok-adimli bir gorev icin ONCE plan cikar (SADECE plan metni uretir, HICBIR islem yapmaz; ~/.natureco/plans/ altina kaydeder). parametreler: action ("create" veya "list"), task (create icin), depth ("minimal"/"detailed"). Kullanici "plan yap / once planla / adimlari cikar / yol haritasi" derse bunu kullan.',
|
|
292
|
-
'- notebook_edit: Jupyter (.ipynb) hucre duzenle. parametreler: filePath, ...',
|
|
293
|
-
'- clarify: kullaniciya netlestirme sorusu sor (belirsizlik varsa). parametreler: question, options.',
|
|
294
|
-
'- calendar_add / reminder_add / notes_add / mac_notify: macOS Takvim/Hatirlatici/Notlar/bildirim (kullanici macOS ise; degilse arac "sadece macOS" der).',
|
|
295
|
-
'- image_generation: gorsel uret. parametre: prompt, size. | text_to_speech: metni sese cevir (text). | speech_to_text: sesi metne cevir (audioPath).',
|
|
296
|
-
'- memory_write: HIZLI tek bilgi kaydet (isim, kisa tercih). parametreler: username ("' + memUser + '"), fact, category. YENI oturumda hatirlanir.',
|
|
297
|
-
'- memory_tree: AGAC-HAFIZA — zengin/kategorize kalici bilgi (proje, karar, teknik not, kisisel detay). action: index|read|search|append; username="' + memUser + '"; append icin: root (1-kisisel|2-teknik|3-kararlar) + branch (dal basligi) + content (yaprak). YENI oturumda hatirlanir.',
|
|
274
|
+
'\n\nKullanabilecegin araclar (p: = parametreler):',
|
|
275
|
+
'- write_file: dosya olustur/uzerine yaz. p: path (TAM yol), content (dosyanin TAM icerigi, kisaltma yok).',
|
|
276
|
+
'- read_file: oku (p: path) | list_dir: dizini listele (p: path) | file_search: glob ile dosya bul (p: pattern orn "**/*.js").',
|
|
277
|
+
'- edit_file: MEVCUT dosyada metin degistir. p: path, old_string (birebir mevcut), new_string. Kismi degisiklikte write_file yerine bunu kullan.',
|
|
278
|
+
'- grep_search: dosya ICERIGINDE desen ara (bash grep yerine bunu tercih et). p: pattern, path, includePattern?, maxResults?.',
|
|
279
|
+
'- bash: kabuk komutu (npm/git/node/python/test/mkdir...). p: command. Yikici/tehlikeli komutlar politikayla engellenir.',
|
|
280
|
+
'- skill_find: skill ara (p: query) → skill_view: skill yukle (p: name).',
|
|
281
|
+
'- cron_create: zamanlanmis gorev. p: name, schedule ("0 9 * * *" veya "every day 9am"), command, description.',
|
|
282
|
+
'- duckduckgo_search: internet aramasi, API key GEREKMEZ, her zaman calisir. p: query, maxResults | web_search: Tavily key ayarliysa; degilse duckduckgo kullan.',
|
|
283
|
+
'- todo_write: aktif gorev listesi. p: action (list/add/update/start/done/remove/clear) + alanlar.',
|
|
284
|
+
'- git: git islemi (p: operation/args/message) | http_request: HTTP istegi (p: url, method, headers, body) | code_execution: sandbox kod (p: code, language).',
|
|
285
|
+
'- sub_agent: alt-gorevi bagimsiz alt-agent\'a devret (p: task, context?, maxTokens?). Paralel/odakli isler; coklu cagriyla orchestrate edebilirsin.',
|
|
286
|
+
'- plan: cok-adimli gorevde SADECE plan metni uret, islem yapmaz (p: action create/list, task, depth). "once planla / yol haritasi" istenirse.',
|
|
287
|
+
'- notebook_edit: .ipynb hucre duzenle (p: filePath...) | clarify: netlestirme sorusu sor (p: question, options).',
|
|
288
|
+
'- calendar_add / reminder_add / notes_add / mac_notify: macOS asistan islevleri (macOS degilse arac soyler).',
|
|
289
|
+
'- image_generation (p: prompt, size) | text_to_speech (p: text) | speech_to_text (p: audioPath).',
|
|
290
|
+
'- memory_write: HIZLI tek bilgi kaydet (p: username="' + memUser + '", fact, category) | memory_tree: AGAC-HAFIZA, zengin kalici bilgi (p: action index|read|search|append|remove, username="' + memUser + '", append icin root 1-kisisel|2-teknik|3-kararlar + branch + content). Ikisi de YENI oturumda hatirlanir.',
|
|
298
291
|
'\nKurallar:',
|
|
299
|
-
'-
|
|
300
|
-
'- KAYIT KALITESI
|
|
301
|
-
'-
|
|
302
|
-
'-
|
|
303
|
-
'- Guncel/internet bilgisi
|
|
304
|
-
'-
|
|
305
|
-
'- Zamanlanmis/
|
|
306
|
-
'-
|
|
307
|
-
'-
|
|
308
|
-
'-
|
|
309
|
-
'- Birden fazla dosya gerekiyorsa her biri icin AYRI write_file cagir.',
|
|
310
|
-
'- Kullanici "masaustu"/"desktop" dediyse ve tam yol vermediyse dosyayi buraya yaz: ' + desktop,
|
|
311
|
-
'- Goreceli yol yerine TAM yol kullan.',
|
|
312
|
-
'- Arac sonuclari <tool_results> icinde geri gelir; gorev bitince ARAC CAGIRMADAN tek cumlelik ozet yaz.',
|
|
313
|
-
'- Basit sohbet/selamlasma ise arac cagirma, dogrudan kisa yanit ver.',
|
|
292
|
+
'- Kalici bilgi paylasilirsa ya da "hatirla/not al/kaydet" denirse HEMEN kaydet (username="' + memUser + '"): kisa → memory_write; zengin → memory_tree(append, dogru root/branch). Trivial/gecici seyleri kaydetme.',
|
|
293
|
+
'- KAYIT KALITESI: fact\'i soylenen ANLAM ve SPESIFIK DEGERLERLE yaz — kod/isim/sayi/tarih AYNEN ("kod adi ONYX-7"yi "onyx" yapma); etiketi dogru koy (proje kodu ≠ kullanici adi). Yanlis ozet = yeni oturumda yanlis hatirlama.',
|
|
294
|
+
'- Kisiye ozel soru (gecmis/proje/tercih) → ONCE memory_tree(search/read) ile ilgili dali oku. Bilgi tek yerde yasar; credential ASLA duz metin.',
|
|
295
|
+
'- Yarim kalan is / "sonra yapalim" → memory_tree(append, root:"3-kararlar", branch:"Bekleyen İşler") — yeni oturumda otomatik hatirlatilir; tamamlaninca memory_tree(remove) ile kaldir.',
|
|
296
|
+
'- Guncel/internet bilgisi → ONCE duckduckgo_search ("internet erisimim yok" DEME).',
|
|
297
|
+
'- Gorev listesi/todo → todo_write (memory_tree DEGIL).',
|
|
298
|
+
'- Zamanlanmis/tekrarlayan is → SADECE cron_create (bash ile crontab/schtasks DUZENLEME — "natureco cron list"te gorunmez). Sonrasinda kullaniciya MUTLAKA soyle: fiilen calismasi icin "natureco daemon start" (veya install) gerekir; bunu soylemeden kesin basari iddia etme.',
|
|
299
|
+
'- Dosya degistirmeden ONCE read_file ile oku, edit_file ile hedefli degistir. Yerini bilmiyorsan file_search/grep_search ile kesfet.',
|
|
300
|
+
'- Kod yazinca/degistirince bash ile calistir/test et, hata varsa duzelt. Coklu dosya = her biri icin AYRI write_file. Hep TAM yol; "masaustu" = ' + desktop + '.',
|
|
301
|
+
'- Arac sonuclari <tool_results> icinde doner; is bitince arac cagirmadan tek cumlelik ozet yaz. Basit sohbette arac cagirma, dogrudan yanitla.',
|
|
314
302
|
execFull ? '- Kullanici uygulama ac / tarayici kontrol / muzik cal / ekran / GUI istediyse yukaridaki computer-use araclarini KULLAN — "yapamam/engellendi" DEME, dogrudan ilgili araci cagir.' : '',
|
|
315
303
|
fullToolsBlock,
|
|
316
304
|
treeDigest ? ('\n\nBILDIGIN KALICI HAFIZA (bu kullaniciya ait, onceki oturumlardan hatirladiklarin; kullaniciya ozel bir sey sorulursa ONCE BUNU KULLAN — dosya arama, uydurma):\n' + treeDigest) : '',
|
package/src/tools/write_file.js
CHANGED
|
@@ -27,8 +27,16 @@ module.exports = {
|
|
|
27
27
|
rawPath = path.join(require('os').homedir(), rawPath.slice(1));
|
|
28
28
|
}
|
|
29
29
|
const filePath = path.resolve(rawPath);
|
|
30
|
+
|
|
31
|
+
// GÜVENLİK (v5.51.1): paketin kendi kaynak koduna yazma varsayılan kapalı;
|
|
32
|
+
// kanal kaynaklı çağrılarda koşulsuz red. Bkz. src/utils/self-edit-guard.js
|
|
33
|
+
const guard = require('../utils/self-edit-guard').checkSelfEdit(filePath);
|
|
34
|
+
if (!guard.allowed) {
|
|
35
|
+
return { success: false, error: guard.error, reason: guard.reason, path: filePath };
|
|
36
|
+
}
|
|
37
|
+
|
|
30
38
|
const dir = path.dirname(filePath);
|
|
31
|
-
|
|
39
|
+
|
|
32
40
|
// Create directory if it doesn't exist
|
|
33
41
|
if (!fs.existsSync(dir)) {
|
|
34
42
|
fs.mkdirSync(dir, { recursive: true });
|
|
@@ -89,6 +89,12 @@ function chunkText(text, maxLen) {
|
|
|
89
89
|
* @returns {Promise<string>} bot yaniti ('' donerse gonderilecek bir sey yok)
|
|
90
90
|
*/
|
|
91
91
|
async function runBrain({ channel, chatKey, text }, deps = {}) {
|
|
92
|
+
// GÜVENLİK (v5.51.1): bu süreçteki araç çağrılarının KANAL kaynaklı olduğunu
|
|
93
|
+
// işaretle — self-edit-guard bunu görünce paket kaynak koduna yazmayı, allow
|
|
94
|
+
// bayrağı açık olsa bile KOŞULSUZ reddeder (kanalda interaktif onay yok).
|
|
95
|
+
// Bilerek geri alınmaz: gateway sürecindeki her çalıştırma kanal kaynaklıdır.
|
|
96
|
+
process.env.NATURECO_CHANNEL_ORIGIN = '1';
|
|
97
|
+
|
|
92
98
|
const workflow = deps.workflow || require('../tools/workflow');
|
|
93
99
|
const getConfig = deps.getConfig || require('./config').getConfig;
|
|
94
100
|
const cfg = getConfig();
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NatureCo Hesabı (SSO) — tek NatureCo hesabı, ekosistem geneli.
|
|
3
|
+
* natureco.me Supabase Auth üstünde, bağımlılıksız (Supabase REST + global fetch).
|
|
4
|
+
* Bu, developers.natureco.me API-KEY girişinden (config.json) AYRIDIR:
|
|
5
|
+
* - API key → bot/otomasyon API'si (config.json)
|
|
6
|
+
* - Hesap → kişi kimliği / SSO (auth.json) ← bu dosya
|
|
7
|
+
* natureco-sdk'daki NatureCoAuth ile aynı protokol (paylaşılan auth.json).
|
|
8
|
+
*/
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const os = require('os');
|
|
13
|
+
const path = require('path');
|
|
14
|
+
|
|
15
|
+
// natureco.me kimlik projesi — anon key PUBLIC (client'lara gömülür, gizli değil)
|
|
16
|
+
const SUPABASE_URL = process.env.NATURECO_SUPABASE_URL || 'https://mxnlehflfkesasclcldy.supabase.co';
|
|
17
|
+
const SUPABASE_ANON = process.env.NATURECO_SUPABASE_ANON || 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Im14bmxlaGZsZmtlc2FzY2xjbGR5Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzY2NDA5MzEsImV4cCI6MjA5MjIxNjkzMX0.93aPOg6bVmgFaJvsM5jVZwiX2TTuFIyAzhP6BlhBkGU';
|
|
18
|
+
const AUTH_BASE = `${SUPABASE_URL}/auth/v1`;
|
|
19
|
+
|
|
20
|
+
function authFile() {
|
|
21
|
+
return path.join(os.homedir(), '.natureco', 'auth.json');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function loadSession() {
|
|
25
|
+
try { return JSON.parse(fs.readFileSync(authFile(), 'utf8')); } catch (_) { return null; }
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function saveSession(session) {
|
|
29
|
+
const file = authFile();
|
|
30
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
31
|
+
fs.writeFileSync(file, JSON.stringify(session, null, 2));
|
|
32
|
+
try { fs.chmodSync(file, 0o600); } catch (_) {}
|
|
33
|
+
return session;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function clearSession() {
|
|
37
|
+
try { fs.unlinkSync(authFile()); } catch (_) {}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function _post(pathname, body, accessToken) {
|
|
41
|
+
const headers = { apikey: SUPABASE_ANON, 'Content-Type': 'application/json' };
|
|
42
|
+
if (accessToken) headers.Authorization = `Bearer ${accessToken}`;
|
|
43
|
+
const res = await fetch(`${AUTH_BASE}${pathname}`, { method: 'POST', headers, body: JSON.stringify(body) });
|
|
44
|
+
const data = await res.json().catch(() => ({}));
|
|
45
|
+
if (!res.ok) {
|
|
46
|
+
const msg = data.error_description || data.msg || data.error || `Auth hatası (${res.status})`;
|
|
47
|
+
const err = new Error(msg); err.statusCode = res.status; throw err;
|
|
48
|
+
}
|
|
49
|
+
return data;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function _shape(s) {
|
|
53
|
+
return {
|
|
54
|
+
access_token: s.access_token,
|
|
55
|
+
refresh_token: s.refresh_token,
|
|
56
|
+
token_type: s.token_type || 'bearer',
|
|
57
|
+
expires_at: s.expires_at || (s.expires_in ? Math.floor(Date.now() / 1000) + s.expires_in : null),
|
|
58
|
+
user: s.user ? { id: s.user.id, email: s.user.email } : null,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** E-posta + şifre ile giriş */
|
|
63
|
+
async function loginWithPassword(email, password) {
|
|
64
|
+
return saveSession(_shape(await _post('/token?grant_type=password', { email, password })));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Şifresiz: e-postaya OTP kodu gönder (mevcut hesap; kayıt açmaz) */
|
|
68
|
+
async function sendOtp(email) {
|
|
69
|
+
await _post('/otp', { email, create_user: false });
|
|
70
|
+
return { sent: true, email };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** OTP kodunu doğrula → oturum */
|
|
74
|
+
async function verifyOtp(email, token) {
|
|
75
|
+
return saveSession(_shape(await _post('/verify', { type: 'email', email, token })));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Access token yenile */
|
|
79
|
+
async function refresh() {
|
|
80
|
+
const s = loadSession();
|
|
81
|
+
if (!s || !s.refresh_token) { const e = new Error('Oturum yok'); e.statusCode = 401; throw e; }
|
|
82
|
+
return saveSession(_shape(await _post('/token?grant_type=refresh_token', { refresh_token: s.refresh_token })));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Geçerli (gerekirse yenilenmiş) access token, yoksa null */
|
|
86
|
+
async function getAccessToken() {
|
|
87
|
+
let s = loadSession();
|
|
88
|
+
if (!s) return null;
|
|
89
|
+
if (s.expires_at && Date.now() / 1000 > s.expires_at - 60) {
|
|
90
|
+
try { s = await refresh(); } catch (_) { return null; }
|
|
91
|
+
}
|
|
92
|
+
return s ? s.access_token : null;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Giriş yapan kullanıcı ({ id, email, ... }) veya null */
|
|
96
|
+
async function whoami() {
|
|
97
|
+
const token = await getAccessToken();
|
|
98
|
+
if (!token) return null;
|
|
99
|
+
const res = await fetch(`${AUTH_BASE}/user`, { headers: { apikey: SUPABASE_ANON, Authorization: `Bearer ${token}` } });
|
|
100
|
+
if (!res.ok) return null;
|
|
101
|
+
return res.json();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function isLoggedIn() {
|
|
105
|
+
const s = loadSession();
|
|
106
|
+
return !!(s && s.access_token);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function currentEmail() {
|
|
110
|
+
const s = loadSession();
|
|
111
|
+
return s && s.user ? s.user.email : null;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function logout() { clearSession(); }
|
|
115
|
+
|
|
116
|
+
module.exports = {
|
|
117
|
+
loginWithPassword, sendOtp, verifyOtp, refresh,
|
|
118
|
+
getAccessToken, whoami, isLoggedIn, currentEmail, logout,
|
|
119
|
+
SUPABASE_URL,
|
|
120
|
+
};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* self-edit-guard — paketin KENDİ kaynak koduna yazmayı varsayılan olarak kapatır.
|
|
3
|
+
*
|
|
4
|
+
* GÜVENLİK (v5.51.1): SELF.md "kendini onar" protokolü ajana kendi kaynağını
|
|
5
|
+
* düzenlemeyi öğretir; Tek Beyin ise güvenilir mesajlaşma kanallarına terminal
|
|
6
|
+
* eşdeğeri araç erişimi verir. İkisi birleşince, allow-list'teki bir hesaptan
|
|
7
|
+
* (ya da ajanın okuduğu içeriğe gizlenmiş prompt injection'dan) gelen bir mesaj,
|
|
8
|
+
* kurulu paketin kodunu gözetimsiz değiştirebilirdi. Kural:
|
|
9
|
+
*
|
|
10
|
+
* - Hedef paket kurulum kökü altındaysa (veya herhangi bir node_modules/
|
|
11
|
+
* natureco-cli altındaysa) yazma VARSAYILAN OLARAK REDDEDİLİR.
|
|
12
|
+
* - Bilinçli açma: NATURECO_ALLOW_SELF_EDIT=1 env ya da config allowSelfEdit:true.
|
|
13
|
+
* - KANAL KAYNAKLI çağrılarda (NATURECO_CHANNEL_ORIGIN=1; channel-brain koyar)
|
|
14
|
+
* bayrak AÇIK OLSA BİLE koşulsuz red — kanalda interaktif onay gösterilemez.
|
|
15
|
+
*
|
|
16
|
+
* Symlink/junction hilesi kapalıdır: hedef, var olan en derin atası üzerinden
|
|
17
|
+
* realpath'e çözülür (~/.natureco/tools bağlantısı paketin içine açılır!).
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
const fs = require('fs');
|
|
21
|
+
const path = require('path');
|
|
22
|
+
|
|
23
|
+
function packageRoot() {
|
|
24
|
+
try { return fs.realpathSync(path.join(__dirname, '..', '..')); }
|
|
25
|
+
catch { return path.resolve(__dirname, '..', '..'); }
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Windows'ta yol karşılaştırması harf-duyarsız olmalı
|
|
29
|
+
function _norm(p) {
|
|
30
|
+
return process.platform === 'win32' ? String(p).toLowerCase() : String(p);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Hedef henüz yoksa bile symlink çözümü yapabilmek için var olan en derin
|
|
34
|
+
// atayı realpath'le, kalan kuyruğu üstüne ekle.
|
|
35
|
+
function realTarget(p) {
|
|
36
|
+
let cur = path.resolve(p);
|
|
37
|
+
const tail = [];
|
|
38
|
+
while (!fs.existsSync(cur)) {
|
|
39
|
+
const parent = path.dirname(cur);
|
|
40
|
+
if (parent === cur) break;
|
|
41
|
+
tail.unshift(path.basename(cur));
|
|
42
|
+
cur = parent;
|
|
43
|
+
}
|
|
44
|
+
try { cur = fs.realpathSync(cur); } catch { /* erişilemedi — çözümsüz devam */ }
|
|
45
|
+
return tail.length ? path.join(cur, ...tail) : cur;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function isSelfPath(targetPath) {
|
|
49
|
+
if (!targetPath) return false;
|
|
50
|
+
const real = _norm(realTarget(targetPath));
|
|
51
|
+
const root = _norm(packageRoot());
|
|
52
|
+
if (real === root || real.startsWith(root + path.sep)) return true;
|
|
53
|
+
// Başka bir konumdaki natureco-cli kurulumu da "kendi kaynağı" sayılır
|
|
54
|
+
return /[\\/]node_modules[\\/]natureco-cli([\\/]|$)/i.test(real);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* @returns {{allowed: boolean, error?: string, reason?: string}}
|
|
59
|
+
*/
|
|
60
|
+
function checkSelfEdit(targetPath) {
|
|
61
|
+
if (!isSelfPath(targetPath)) return { allowed: true };
|
|
62
|
+
|
|
63
|
+
if (process.env.NATURECO_CHANNEL_ORIGIN === '1') {
|
|
64
|
+
return {
|
|
65
|
+
allowed: false,
|
|
66
|
+
reason: 'self-edit-channel',
|
|
67
|
+
error: 'Güvenlik: natureco-cli kaynak kodu mesajlaşma kanalı üzerinden HİÇBİR KOŞULDA düzenlenemez (interaktif onay gösterilemiyor). Bu işlem yalnızca terminalden, NATURECO_ALLOW_SELF_EDIT=1 ile yapılabilir.',
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (process.env.NATURECO_ALLOW_SELF_EDIT === '1') return { allowed: true };
|
|
72
|
+
try {
|
|
73
|
+
const { getConfig } = require('./config');
|
|
74
|
+
if (getConfig().allowSelfEdit === true) return { allowed: true };
|
|
75
|
+
} catch { /* config okunamadı — güvenli tarafta kal (red) */ }
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
allowed: false,
|
|
79
|
+
reason: 'self-edit-disabled',
|
|
80
|
+
error: 'Güvenlik: hedef, natureco-cli kurulumunun kendi kaynak kodu altında. Kendini-onarma varsayılan olarak kapalıdır; kullanıcı bilinçli olarak açmalı: NATURECO_ALLOW_SELF_EDIT=1 (veya config allowSelfEdit:true). Ayrıntı: paket kökündeki SELF.md.',
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
module.exports = { checkSelfEdit, isSelfPath, _internal: { packageRoot, realTarget } };
|
package/src/utils/skill-index.js
CHANGED
|
@@ -78,10 +78,17 @@ function buildSkillIndex() {
|
|
|
78
78
|
if (mode === 'off') return '';
|
|
79
79
|
const skills = _discoverSkills();
|
|
80
80
|
if (skills.length === 0) return '';
|
|
81
|
-
// Cok skill (>60) → KOMPAKT isim-listesi (kategorili, virgullu): en dusuk token.
|
|
82
|
-
// Az skill → isim + kisa aciklama. Kullanici 'full'/'names' ile ezebilir.
|
|
83
81
|
const many = skills.length > 60;
|
|
84
|
-
|
|
82
|
+
// v5.51: cok skill'de VARSAYILAN artik tek satirlik IPUCU (progressive disclosure
|
|
83
|
+
// 2. seviye): 319 ismin listesi bile ~1.450 token tutuyordu. Ajan gorev bir
|
|
84
|
+
// uzmanlik istediginde skill_find(query) ile arar, skill_view(name) ile yukler.
|
|
85
|
+
// Eski davranislar env ile durur: names (isim listesi), full (isim+aciklama).
|
|
86
|
+
if (many && mode !== 'full' && mode !== 'names') {
|
|
87
|
+
return `## Skills: ${skills.length} adet hazir skill var (SEO, tasarim, video, guvenlik, kod...). ` +
|
|
88
|
+
'Gorev bir uzmanlik/sablon istiyorsa ONCE skill_find(query) ile ara, buldugunu skill_view(name) ile yukle.';
|
|
89
|
+
}
|
|
90
|
+
// Az skill → isim + kisa aciklama; 'names' → sadece isimler.
|
|
91
|
+
const compact = mode === 'names';
|
|
85
92
|
const byCategory = {};
|
|
86
93
|
for (const s of skills) {
|
|
87
94
|
if (!byCategory[s.category]) byCategory[s.category] = [];
|
package/src/utils/tool-runner.js
CHANGED
|
@@ -84,6 +84,21 @@ function getToolDefinitions() {
|
|
|
84
84
|
}));
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
// ── Onay gerektiren araçlar ───────────────────────────────────────────────────
|
|
88
|
+
// v5.43: shell_command da bash gibi onay tetikler (aksi halde onay/güvenlik
|
|
89
|
+
// akışını atlayan ikinci bir arbitrary-shell yolu olurdu).
|
|
90
|
+
// v5.51.1 GÜVENLİK: edit_file eklendi — write_file diff+onay isterken, aynı riski
|
|
91
|
+
// taşıyan hedefli değişiklik (edit_file) onaysız geçiyordu (SELF.md kendini-onarma
|
|
92
|
+
// + Tek Beyin kanal erişimiyle birleşince kritik).
|
|
93
|
+
function needsConfirmation(toolName, params) {
|
|
94
|
+
const p = params || {};
|
|
95
|
+
return (
|
|
96
|
+
toolName === 'write_file' ||
|
|
97
|
+
toolName === 'edit_file' ||
|
|
98
|
+
((toolName === 'bash' || toolName === 'shell_command') && /\b(rm|mv|cp|chmod|chown|dd|mkfs|truncate)\b/.test(p.command || ''))
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
87
102
|
// ── Execute a single tool ─────────────────────────────────────────────────────
|
|
88
103
|
async function executeTool(toolName, params, opts = {}) {
|
|
89
104
|
const safeParams = params ?? {};
|
|
@@ -97,28 +112,38 @@ async function executeTool(toolName, params, opts = {}) {
|
|
|
97
112
|
|
|
98
113
|
const label = `${toolName}${safeParams.path ? ' — ' + safeParams.path : safeParams.command ? ' — ' + safeParams.command : ''}`;
|
|
99
114
|
|
|
100
|
-
// ── Onay mekanizması (
|
|
115
|
+
// ── Onay mekanizması (dosya değiştiren araçlar ve tehlikeli bash) ──────────
|
|
101
116
|
if (agentMode) {
|
|
102
|
-
|
|
103
|
-
// akışını atlayan ikinci bir arbitrary-shell yolu olurdu).
|
|
104
|
-
const needsConfirm =
|
|
105
|
-
toolName === 'write_file' ||
|
|
106
|
-
((toolName === 'bash' || toolName === 'shell_command') && /\b(rm|mv|cp|chmod|chown|dd|mkfs|truncate)\b/.test(safeParams.command || ''));
|
|
107
|
-
|
|
108
|
-
if (needsConfirm) {
|
|
117
|
+
if (needsConfirmation(toolName, safeParams)) {
|
|
109
118
|
if (toolName === 'write_file') {
|
|
110
119
|
// Diff göster
|
|
111
120
|
let oldContent = '';
|
|
112
121
|
try { oldContent = fs.readFileSync(path.resolve(safeParams.path), 'utf-8'); } catch {}
|
|
113
122
|
showDiff(oldContent, safeParams.content || '', safeParams.path);
|
|
123
|
+
} else if (toolName === 'edit_file') {
|
|
124
|
+
// v5.51.1 GÜVENLİK: edit_file onay kapsamına alındı — hedefli old→new
|
|
125
|
+
// diff'i göster (write_file'daki diff'in edit_file muadili).
|
|
126
|
+
console.log(chalk.gray(`\n 📄 ${safeParams.path}`));
|
|
127
|
+
for (const line of String(safeParams.old_string || '').split('\n')) {
|
|
128
|
+
console.log(chalk.red(' - ' + line));
|
|
129
|
+
}
|
|
130
|
+
for (const line of String(safeParams.new_string || '').split('\n')) {
|
|
131
|
+
console.log(chalk.green(' + ' + line));
|
|
132
|
+
}
|
|
133
|
+
if (safeParams.replace_all) console.log(chalk.yellow(' (replace_all: TÜM eşleşmeler değişecek)'));
|
|
134
|
+
console.log();
|
|
114
135
|
} else {
|
|
115
136
|
console.log(chalk.yellow(`\n 🖥️ Komut: ${chalk.white(safeParams.command)}\n`));
|
|
116
137
|
}
|
|
117
138
|
|
|
139
|
+
const confirmMsg =
|
|
140
|
+
toolName === 'write_file' ? `✏️ ${safeParams.path} dosyası değiştirilecek` :
|
|
141
|
+
toolName === 'edit_file' ? `✏️ ${safeParams.path} dosyasında değişiklik yapılacak` :
|
|
142
|
+
'⚠️ Bu komut çalıştırılacak';
|
|
118
143
|
const { confirm } = await inquirer.prompt([{
|
|
119
144
|
type: 'confirm',
|
|
120
145
|
name: 'confirm',
|
|
121
|
-
message: chalk.yellow(` ${
|
|
146
|
+
message: chalk.yellow(` ${confirmMsg}. Onaylıyor musun?`),
|
|
122
147
|
default: true,
|
|
123
148
|
}]);
|
|
124
149
|
|
|
@@ -137,7 +162,7 @@ async function executeTool(toolName, params, opts = {}) {
|
|
|
137
162
|
|
|
138
163
|
// İstatistik güncelle
|
|
139
164
|
if (result.success !== false) {
|
|
140
|
-
if (toolName === 'write_file') filesChanged++;
|
|
165
|
+
if (toolName === 'write_file' || toolName === 'edit_file') filesChanged++;
|
|
141
166
|
if (toolName === 'bash') commandsRun++;
|
|
142
167
|
}
|
|
143
168
|
|
|
@@ -185,4 +210,5 @@ module.exports = {
|
|
|
185
210
|
executeToolCalls,
|
|
186
211
|
getSessionStats,
|
|
187
212
|
resetSessionStats,
|
|
213
|
+
needsConfirmation,
|
|
188
214
|
};
|