vantuz 3.3.5 → 3.3.7

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/README.md CHANGED
@@ -1,44 +1,80 @@
1
- # 🐙 Vantuz AI
1
+ # 🐙 Vantuz OS
2
2
 
3
- **Yapay Zeka Destekli E-Ticaret Yönetim Platformu**
3
+ **Yeni Nesil E-Ticaret İşletim Sistemi**
4
4
 
5
- 7 Türk pazaryerini tek platformdan yönetin: Trendyol, Hepsiburada, N11, Amazon, Çiçeksepeti, PTTavm, Pazarama
5
+ Vantuz, sıradan bir entegrasyon aracı değil; **öğrenen, gören ve sizin adınıza düşünen** bir yapay zeka işletim sistemidir.
6
6
 
7
- ## Kurulum
7
+ ## 🚀 Temel Yetenekler
8
8
 
9
- ```bash
10
- npm install vantuz
11
- ```
9
+ ### 🧠 Kalıcı Hafıza & Kimlik (Brain)
10
+ Vantuz artık unutmuyor. Sizin stratejilerinizi öğrenir ve uygular.
11
+ - **Persistent Memory:** Konuşulanları, alınan kararları ve verileri diske kaydeder. Restart atsanız bile hatırlar.
12
+ - **Marka Ruhu (`BRAND.md`):** "Kar marjım %15'in altına düşmesin", "Müşteriye senli benli konuşma" gibi kurallarınızı anayasası kabul eder.
13
+
14
+ ### 👁️ Vision Intelligence
15
+ Sadece metin değil, görsellerle de çalışır.
16
+ - **Ürün Analizi:** Fotoğraftan ürün özelliklerini, materyalini ve tahmini fiyatını çıkarır.
17
+ - **Hasar Kontrolü:** İade gelen ürünün fotoğrafına bakıp "Hasarlı" veya "Yeniden Satılabilir" kararı verir.
18
+ - **Depo Gözü:** Depo kameralarına bağlanıp stok sayımı yapabilir.
19
+
20
+ ### 🛡️ Critical Lane (Güvenli Şerit)
21
+ Hata yapma lüksü olmayan işlemler için özel koruma.
22
+ - **Serial Queue:** Aynı anda 100 fiyat güncelleme emri gelse bile, Vantuz bunları tek tek, sakince ve hatasız işler.
23
+ - **Dry-Run:** Kritik komutlar önce simülasyon modunda çalıştırılır, onaylanırsa gerçeğe dönüşür.
12
24
 
13
- ## Hızlı Başlangıç
25
+ ### 🔌 Genişletilebilir Uydu Modülleri (Nodes)
26
+ Vantuz sadece bilgisayarınızda değil, deponuzda da yaşar.
27
+ - **Warehouse Node:** Depo terminaline kurulan ufak bir modülle barkod okuma ve kamera entegrasyonu sağlar.
28
+ - **Webhooks:** Trendyol/Hepsiburada'dan gelen "Yeni Sipariş" bildirimini anında yakalar.
29
+
30
+ ---
31
+
32
+ ## 📦 Kurulum
14
33
 
15
34
  ```bash
16
- # Yapılandırma
17
- npx vantuz config
35
+ # Vantuz'u global olarak kurun
36
+ npm install -g vantuz
18
37
 
19
- # Başlat
20
- npx vantuz tui
38
+ # Yapılandırma sihirbazını başlatın
39
+ vantuz config
21
40
  ```
22
41
 
23
- ## Özellikler
42
+ ## 🎮 Kullanım
24
43
 
25
- - 🏪 **7 Pazaryeri** - Tek komutla tüm platformları yönet
26
- - 🤖 **Akıllı Fiyatlama** - Rakip takibi ve otomatik fiyat optimizasyonu
27
- - 📊 **Hızlı Raporlar** - Anlık satış ve stok özeti
28
- - ⏰ **Otomasyon** - Zamanlanmış görevler ve uyarılar
29
- - 🧠 **Türkçe NL** - Doğal dil komut desteği
44
+ ### Terminal Arayüzü (TUI)
45
+ Vantuz ile sohbet ederek şirketinizi yönetin.
30
46
 
31
- ## Komutlar
47
+ ```bash
48
+ vantuz tui
49
+ ```
50
+
51
+ **Örnek Komutlar:**
52
+ - "Trendyol'daki tüm kılıfların fiyatını %5 artır ama kar marjını koru."
53
+ - "Şu ürünün fotoğrafını analiz et ve Amazon Almanca açıklamasını yaz."
54
+ - "Son 1 saatte gelen iadelerin hasar durumunu raporla."
55
+
56
+ ### Depo Modülü Kurulumu (Warehouse Node)
32
57
 
33
58
  ```bash
34
- npx vantuz tui # Sohbet modu
35
- npx vantuz config # Ayarlar
36
- npx vantuz status # Durum kontrolü
59
+ # Depo bilgisayarında:
60
+ node nodes/warehouse.js
37
61
  ```
38
62
 
39
- ## Lisans
63
+ ---
40
64
 
41
- Ticari yazılım. Kullanım için lisans anahtarı gereklidir.
65
+ ## 🏗️ Mimari
66
+
67
+ - **Core:** Node.js + Express
68
+ - **Memory:** JSON File Persistence + Vector Search
69
+ - **AI:** OpenAI GPT-4o / Google Gemini
70
+ - **Queue:** Serial Promise Queue
71
+ - **Gateway:** WebSocket Bridge
42
72
 
43
- 📧 nuricanavsar2000@gmail.com
44
- 🌐 https://nuricanavsar.com
73
+ ---
74
+
75
+ ## 📄 Lisans
76
+
77
+ Ticari yazılım. Kullanım için lisans anahtarı gereklidir.
78
+ Yapımcı: **Nuri Can Avşar**
79
+ İletişim: nuricanavsar2000@gmail.com
80
+ Web: https://nuricanavsar.com
package/config.js CHANGED
@@ -249,6 +249,7 @@ class Configurator {
249
249
  await this.step3_Channels();
250
250
  await this.step4_Gateway();
251
251
  await this.step_EIAConfig();
252
+ await this.step_RiskAcceptance(); // Add risk acceptance step
252
253
  await this.step5_Save();
253
254
  await this.showSuccess();
254
255
  } catch (error) {
@@ -384,7 +385,8 @@ class Configurator {
384
385
  choice: String(index + 1),
385
386
  label: `${icon} ${label}`,
386
387
  description: description,
387
- envKey: providerInfo.envKey // Store the actual env key
388
+ envKey: providerInfo.envKey, // Store the actual env key
389
+ key: key // Store the provider key (e.g., 'gemini', 'groq')
388
390
  };
389
391
  });
390
392
 
@@ -432,13 +434,15 @@ class Configurator {
432
434
 
433
435
  if (key && key.trim()) {
434
436
  this.envVars[selectedOption.envKey] = key.trim();
437
+ this.envVars.AI_PROVIDER = selectedOption.key; // Save the provider key (e.g., 'groq')
438
+
435
439
  // Clear other AI keys if one is selected
436
440
  providerOptions.forEach(option => {
437
441
  if (option.envKey !== selectedOption.envKey && this.envVars[option.envKey]) {
438
442
  delete this.envVars[option.envKey];
439
443
  }
440
444
  });
441
- console.log(this.successMessage('API anahtarı kaydedildi'));
445
+ console.log(this.successMessage('API anahtarı ve sağlayıcı seçimi kaydedildi'));
442
446
  } else {
443
447
  if (this.envVars[selectedOption.envKey]) {
444
448
  delete this.envVars[selectedOption.envKey];
@@ -595,18 +599,61 @@ class Configurator {
595
599
  console.log('');
596
600
 
597
601
  if (!info.configFound || !info.hasToken || !info.connected) {
598
- console.log(this.warningMessage('Gateway tam olarak yapılandırılmamış veya çalışmıyor gibi görünüyor.'));
599
- console.log(c('brightWhite', 'Lütfen Gateway\'i başlatmak ve tam olarak yapılandırmak için `start.bat` dosyasını çalıştırın.\n'));
600
- console.log(c('dim', ' `start.bat` komutu gerekli dosyaları oluşturacak ve Gateway\'i başlatacaktır.'));
601
- console.log(c('dim', ' Daha sonra durumu tekrar kontrol etmek için: ') + c('brightCyan', 'vantuz gateway status'));
602
+ console.log(this.warningMessage('Gateway tam olarak yapılandırılmamış veya çalışmıyor.'));
603
+
604
+ const startChoice = await this.prompt('Sistemi otomatik başlatmak ister misiniz? (E/h)', 'E');
605
+ if (startChoice.toLowerCase() !== 'h') {
606
+ console.log(c('dim', 'Sistem başlatılıyor (Gateway + Server)...'));
607
+ const result = await gateway.startFullStack();
608
+
609
+ if (result.success) {
610
+ console.log(this.successMessage('Başlatma komutları gönderildi.'));
611
+ console.log(c('dim', 'Servislerin açılması 10-15 saniye sürebilir.'));
612
+ await sleep(5000); // Wait a bit before re-checking
613
+
614
+ // Re-check status
615
+ const newInfo = (await gateway.health()).success;
616
+ if (newInfo) console.log(this.successMessage('Gateway bağlantısı sağlandı!'));
617
+ else console.log(c('yellow', 'Gateway henüz hazır değil, arka planda açılıyor...'));
618
+ } else {
619
+ console.log(this.errorMessage('Başlatma sırasında hata oluştu.'));
620
+ if (result.gateway?.error) console.log(c('red', `Gateway Hatası: ${result.gateway.error}`));
621
+ if (result.server?.error) console.log(c('red', `Server Hatası: ${result.server.error}`));
622
+ }
623
+ } else {
624
+ console.log(c('dim', 'Manuel başlatmak için `start.bat` kullanabilirsiniz.'));
625
+ }
602
626
  } else {
603
- console.log(this.successMessage('Gateway başarılı bir şekilde yapılandırılmış ve çalışıyor görünüyor.'));
604
- console.log(c('brightWhite', 'Durumunu kontrol etmek için dilediğiniz zaman `vantuz gateway status` komutunu kullanabilirsiniz.\n'));
627
+ console.log(this.successMessage('Gateway başarılı bir şekilde yapılandırılmış ve çalışıyor.'));
628
+ console.log(c('dim', 'Durumu kontrol etmek için: vantuz gateway status'));
605
629
  }
606
630
  await this.prompt(c('dim', '▶ Devam etmek için Enter\'a basın...'));
607
631
  }
608
632
 
609
633
  // KAYDET
634
+ async step_RiskAcceptance() {
635
+ if (this.envVars.RISK_ACCEPTED === 'true') return;
636
+
637
+ this.printHeader('RİSK KABULÜ', '⚠️');
638
+ console.log(c('brightWhite', 'Vantuz AI, sizin adınıza fiyat ve stok güncellemeleri yapabilir.\n'));
639
+ console.log(c('yellow', 'Bazı işlemler geri alınamaz olabilir. Otonom kararlar için riskleri kabul ediyor musunuz?'));
640
+ console.log(c('dim', '(Kabul ederseniz, yazma işlemlerinde sürekli onay sormayacaktır.)\n'));
641
+
642
+ console.log(this.menuItem('E', 'Evet, kabul ediyorum', 'Otonom mod'));
643
+ console.log(this.menuItem('H', 'Hayır, her işlemde sor', 'Güvenli mod'));
644
+ console.log('');
645
+
646
+ const choice = await this.prompt(c('brightYellow', '❯ Seçiminiz (E/H)'));
647
+ if (choice.toLowerCase() === 'e') {
648
+ this.envVars.RISK_ACCEPTED = 'true';
649
+ console.log(this.successMessage('Risk kabul edildi. Otonom mod aktif.'));
650
+ } else {
651
+ this.envVars.RISK_ACCEPTED = 'false';
652
+ console.log(this.infoMessage('Güvenli mod aktif. Kritik işlemlerde onay istenecektir.'));
653
+ }
654
+ await sleep(1000);
655
+ }
656
+
610
657
  async step5_Save() {
611
658
  this.printHeader('AYARLAR KAYDEDİLİYOR', '💾');
612
659
 
@@ -0,0 +1,190 @@
1
+ // core/agent-loop.js
2
+ // The Heartbeat of Vantuz OS V2 — Autonomous Agent Loop
3
+ // Cron-driven cycle that orchestrates all intelligence modules.
4
+
5
+ import { log } from './ai-provider.js';
6
+ import { getLearning } from './learning.js';
7
+ import { getSelfHealer } from './self-healer.js';
8
+ import { CronJob } from 'cron';
9
+
10
+ // ═══════════════════════════════════════════════════════════════════════════
11
+ // AGENT LOOP
12
+ // ═══════════════════════════════════════════════════════════════════════════
13
+
14
+ class AgentLoop {
15
+ constructor() {
16
+ this.modules = new Map(); // name -> { fn, interval, enabled }
17
+ this.cronJobs = new Map(); // name -> CronJob instance
18
+ this.running = false;
19
+ this.lastRun = {}; // name -> timestamp
20
+ this.results = {}; // name -> last result
21
+ this.healer = getSelfHealer();
22
+ this.learning = getLearning();
23
+
24
+ log('INFO', '🫀 AgentLoop initialized');
25
+ }
26
+
27
+ /**
28
+ * Register a module to run in the loop.
29
+ * @param {string} name - Module name (e.g., 'warroom', 'oracle').
30
+ * @param {function} fn - Async function to execute.
31
+ * @param {string} cronExpr - Cron expression (default: every 30 min).
32
+ * @param {boolean} enabled - Start enabled? (default: true).
33
+ */
34
+ register(name, fn, cronExpr = '*/30 * * * *', enabled = true) {
35
+ this.modules.set(name, { fn, cronExpr, enabled });
36
+ log('INFO', `AgentLoop: registered "${name}" (${cronExpr}, ${enabled ? 'enabled' : 'disabled'})`);
37
+ }
38
+
39
+ /**
40
+ * Start the loop — creates cron jobs for all registered modules.
41
+ */
42
+ start() {
43
+ if (this.running) {
44
+ log('WARN', 'AgentLoop already running');
45
+ return;
46
+ }
47
+
48
+ for (const [name, mod] of this.modules) {
49
+ if (!mod.enabled) continue;
50
+
51
+ const job = new CronJob(mod.cronExpr, async () => {
52
+ await this._executeModule(name);
53
+ }, null, false, 'Europe/Istanbul');
54
+
55
+ this.cronJobs.set(name, job);
56
+ job.start();
57
+ }
58
+
59
+ this.running = true;
60
+ log('INFO', `🫀 AgentLoop STARTED — ${this.cronJobs.size} modules active`);
61
+ }
62
+
63
+ /**
64
+ * Stop the loop.
65
+ */
66
+ stop() {
67
+ for (const [name, job] of this.cronJobs) {
68
+ job.stop();
69
+ }
70
+ this.cronJobs.clear();
71
+ this.running = false;
72
+ log('INFO', '🫀 AgentLoop STOPPED');
73
+ }
74
+
75
+ /**
76
+ * Enable/disable a module.
77
+ */
78
+ setEnabled(name, enabled) {
79
+ const mod = this.modules.get(name);
80
+ if (mod) {
81
+ mod.enabled = enabled;
82
+ const job = this.cronJobs.get(name);
83
+ if (job) {
84
+ enabled ? job.start() : job.stop();
85
+ }
86
+ log('INFO', `AgentLoop: "${name}" ${enabled ? 'enabled' : 'disabled'}`);
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Manually trigger a module immediately.
92
+ */
93
+ async trigger(name) {
94
+ if (!this.modules.has(name)) {
95
+ return { error: `Module "${name}" not found` };
96
+ }
97
+ return await this._executeModule(name);
98
+ }
99
+
100
+ /**
101
+ * Run all modules once (manual full cycle).
102
+ */
103
+ async runFullCycle() {
104
+ log('INFO', '🫀 Full cycle triggered manually');
105
+ const results = {};
106
+ for (const [name, mod] of this.modules) {
107
+ if (mod.enabled) {
108
+ results[name] = await this._executeModule(name);
109
+ }
110
+ }
111
+ return results;
112
+ }
113
+
114
+ getStatus() {
115
+ const moduleStatus = {};
116
+ for (const [name, mod] of this.modules) {
117
+ moduleStatus[name] = {
118
+ enabled: mod.enabled,
119
+ cronExpr: mod.cronExpr,
120
+ lastRun: this.lastRun[name] || null,
121
+ lastResult: this.results[name] ? 'OK' : null
122
+ };
123
+ }
124
+
125
+ return {
126
+ running: this.running,
127
+ autonomous: this.learning.isAutonomous(),
128
+ netScore: this.learning.getNetScore(),
129
+ activeModules: [...this.modules.entries()].filter(([_, m]) => m.enabled).length,
130
+ totalModules: this.modules.size,
131
+ modules: moduleStatus
132
+ };
133
+ }
134
+
135
+ // ─────────────────────────────────────────────────────────────────────
136
+
137
+ async _executeModule(name) {
138
+ const mod = this.modules.get(name);
139
+ if (!mod) return null;
140
+
141
+ // Autonomy check
142
+ if (!this.learning.isAutonomous()) {
143
+ log('WARN', `AgentLoop: "${name}" skipped — otonom mod kapalı (skor: ${this.learning.getNetScore()})`);
144
+ return { skipped: true, reason: 'Autonomous mode disabled' };
145
+ }
146
+
147
+ log('INFO', `AgentLoop: executing "${name}"`);
148
+ const startTime = Date.now();
149
+
150
+ try {
151
+ // Execute with self-healing wrapper
152
+ const result = await this.healer.withHealing(
153
+ `agent-loop/${name}`,
154
+ () => mod.fn(),
155
+ `agent-loop-${name}`,
156
+ { module: name, timestamp: new Date().toISOString() }
157
+ );
158
+
159
+ const duration = Date.now() - startTime;
160
+ this.lastRun[name] = new Date().toISOString();
161
+ this.results[name] = result;
162
+
163
+ // Positive learning
164
+ this.learning.record('successful_price_update', `${name} başarılı (${duration}ms)`, name);
165
+
166
+ log('INFO', `AgentLoop: "${name}" completed in ${duration}ms`);
167
+ return result;
168
+
169
+ } catch (e) {
170
+ const duration = Date.now() - startTime;
171
+
172
+ // Negative learning
173
+ this.learning.record('listing_error', `${name} hata: ${e.message}`, name);
174
+
175
+ log('ERROR', `AgentLoop: "${name}" failed after ${duration}ms`, { error: e.message });
176
+ return { error: e.message };
177
+ }
178
+ }
179
+ }
180
+
181
+ let loopInstance = null;
182
+
183
+ export function getAgentLoop() {
184
+ if (!loopInstance) {
185
+ loopInstance = new AgentLoop();
186
+ }
187
+ return loopInstance;
188
+ }
189
+
190
+ export default AgentLoop;
@@ -134,6 +134,38 @@ export function clearLogs() {
134
134
  }
135
135
  }
136
136
 
137
+ // ═══════════════════════════════════════════════════════════════════════════
138
+ // WORKSPACE IDENTITY LOADER
139
+ // ═══════════════════════════════════════════════════════════════════════════
140
+
141
+ const WORKSPACE_DIR = path.join(process.cwd(), 'workspace');
142
+
143
+ /**
144
+ * Loads workspace identity files (BRAND.md, SOUL.md, AGENTS.md)
145
+ * These files define the AI's personality, brand rules, and capabilities.
146
+ */
147
+ function loadWorkspaceIdentity() {
148
+ const files = ['BRAND.md', 'SOUL.md', 'AGENTS.md'];
149
+ let identity = '';
150
+
151
+ for (const file of files) {
152
+ const filePath = path.join(WORKSPACE_DIR, file);
153
+ try {
154
+ if (fs.existsSync(filePath)) {
155
+ const content = fs.readFileSync(filePath, 'utf-8').trim();
156
+ if (content) {
157
+ identity += `\n\n--- ${file} ---\n${content}`;
158
+ log('INFO', `Workspace identity loaded: ${file}`, { chars: content.length });
159
+ }
160
+ }
161
+ } catch (e) {
162
+ log('WARN', `Workspace file read error: ${file}`, { error: e.message });
163
+ }
164
+ }
165
+
166
+ return identity;
167
+ }
168
+
137
169
  // ═══════════════════════════════════════════════════════════════════════════
138
170
  // SYSTEM PROMPT
139
171
  // ═══════════════════════════════════════════════════════════════════════════
@@ -158,17 +190,18 @@ const VANTUZ_SYSTEM_PROMPT = `Sen Vantuz AI, e-ticaret operasyonlarını yönete
158
190
  ## Yeteneklerin
159
191
  - Stok kontrolü ve güncelleme
160
192
  - Fiyat analizi ve güncelleme
161
- - Sipariş yönetimi
193
+ - Sipariş yönetimi (Listeleme, Durum sorgulama)
162
194
  - Rakip analizi
163
195
  - Satış raporları
196
+ - Zamanlanmış görevler (Cron Jobs) oluşturma/yönetme
164
197
 
165
198
  ## Önemli Kurallar
166
199
  1. **ASLA VE ASLA** elindeki "MEVCUT SİSTEM DURUMU" verisinde olmayan bir sayıyı uydurma.
167
- 2. Eğer bağlamda sipariş sayısı 0 görünüyorsa veya hiç veri yoksa, "Şu an sipariş verisine ulaşamıyorum" de.
168
- 3. "25 sipariş var" gibi rastgele sayılar verme.
169
- 4. Kar marjının altına fiyat düşürme önerme.
170
- 5. Stokta olmayan ürünü satışa açma.
171
- 6. Kritik işlemlerden önce onay iste.
200
+ 2. Eğer mesajda "cron" veya "zamanla" geçiyorsa, cron formatında zamanlanmış görev oluşturmayı teklif et.
201
+ 3. Sipariş veya ürün listeleme gibi OKUMA (Read) işlemleri için ASLA onay isteme. Doğrudan listele.
202
+ 4. Sadece Fiyat/Stok güncelleme veya Silme gibi YAZMA (Write) işlemleri için risk uyarısı ver.
203
+ 5. Kullanıcı "Risk Kabul Edildi" modundaysa (RISK_ACCEPTED=true), onay istemeden işlemi yap.
204
+ 6. Kar marjının altına fiyat düşürme önerme.
172
205
 
173
206
  ## Yanıt Formatı
174
207
  - Kısa ve öz ol
@@ -205,7 +238,8 @@ export async function chat(message, config, env) {
205
238
 
206
239
  // Context bilgisi ekle
207
240
  const contextInfo = config.systemContext || '';
208
- const fullSystemPrompt = VANTUZ_SYSTEM_PROMPT + contextInfo;
241
+ const workspaceIdentity = loadWorkspaceIdentity();
242
+ const fullSystemPrompt = VANTUZ_SYSTEM_PROMPT + contextInfo + (workspaceIdentity ? `\n\n## MARKA KİMLİĞİ VE STRATEJİ\nAşağıdaki kurallara MUTLAKA uy:${workspaceIdentity}` : '');
209
243
 
210
244
  log('INFO', `AI isteği: ${provider}`, { message: message.slice(0, 100) });
211
245
 
@@ -10,14 +10,13 @@ const VANTUZ_HOME = path.join(os.homedir(), '.vantuz');
10
10
  const CONFIG_JSON = path.join(VANTUZ_HOME, 'config.json');
11
11
 
12
12
  const RISKY_KEYWORDS = [
13
- 'fiyat', 'zam', 'indirim', 'stok', 'g?ncelle', 'update',
14
- 'yay?nla', 'yay?ndan kald?r', 'unpublish', 'publish',
15
- 'kampanya', 'kupon', 'ip',
16
- 'sil', 'kald?r', 'de?i?tir', 'price', 'stock'
13
+ 'güncelle', 'update', 'yayınla', 'publish', 'yayından kaldır', 'unpublish',
14
+ 'değiştir', 'change', 'sil', 'kaldır', 'delete', 'remove',
15
+ 'fiyatı yap', 'stoğu yap'
17
16
  ];
18
17
 
19
18
  const APPROVE_KEYWORDS = ['onay', 'onayla', 'kabul', 'evet', 'tamam'];
20
- const REJECT_KEYWORDS = ['hay?r', 'iptal', 'vazge?', 'reddet'];
19
+ const REJECT_KEYWORDS = ['hayır', 'iptal', 'vazgeç', 'reddet'];
21
20
 
22
21
  function loadConfig() {
23
22
  try {
@@ -25,7 +24,7 @@ function loadConfig() {
25
24
  return JSON.parse(fs.readFileSync(CONFIG_JSON, 'utf-8'));
26
25
  }
27
26
  } catch (e) {
28
- log('WARN', 'Config okunamad?', { error: e.message });
27
+ log('WARN', 'Config okunamadı', { error: e.message });
29
28
  }
30
29
  return {};
31
30
  }
@@ -35,7 +34,7 @@ function saveConfig(config) {
35
34
  fs.writeFileSync(CONFIG_JSON, JSON.stringify(config, null, 2));
36
35
  return true;
37
36
  } catch (e) {
38
- log('WARN', 'Config yaz?lamad?', { error: e.message });
37
+ log('WARN', 'Config yazılamadı', { error: e.message });
39
38
  return false;
40
39
  }
41
40
  }
@@ -54,12 +53,12 @@ function messageHasAny(message, keywords) {
54
53
  function normalizeTr(input) {
55
54
  return String(input || '')
56
55
  .toLowerCase()
57
- .replace(/[??]/g, 'c')
58
- .replace(/[??]/g, 'g')
59
- .replace(/[??]/g, 'i')
60
- .replace(/[??]/g, 'o')
61
- .replace(/[??]/g, 's')
62
- .replace(/[??]/g, 'u');
56
+ .replace(/[çÇ]/g, 'c')
57
+ .replace(/[ğĞ]/g, 'g')
58
+ .replace(/[ıİ]/g, 'i')
59
+ .replace(/[öÖ]/g, 'o')
60
+ .replace(/[şŞ]/g, 's')
61
+ .replace(/[üÜ]/g, 'u');
63
62
  }
64
63
 
65
64
  function summarizeOrdersByStatus(orders = []) {
@@ -77,7 +76,7 @@ function summarizeOrdersByStatus(orders = []) {
77
76
 
78
77
  function isActiveStatus(status) {
79
78
  const s = String(status || '').toLowerCase();
80
- return s === 'created' || s === 'picking' || s === 'unpacked';
79
+ return s === 'created' || s === 'picking' || s === 'unpacked' || s === 'shipped';
81
80
  }
82
81
 
83
82
  function extractProductNames(order) {
@@ -107,7 +106,7 @@ function getOrderTimestamp(order) {
107
106
 
108
107
  function formatOrderLine(order) {
109
108
  const number = order.orderNumber || order.id || 'N/A';
110
- const name = order.customerName || order.customerfullName || order.customerFullName || 'M??teri';
109
+ const name = order.customerName || order.customerfullName || order.customerFullName || 'Müşteri';
111
110
  const total = order.totalPrice ?? order.totalAmount ?? order.total ?? '?';
112
111
  const status = order.status || order.shipmentPackageStatus || order.orderStatus || 'UNKNOWN';
113
112
  return `#${number} - ${name} - ${total} TL - ${status}`;
@@ -175,13 +174,13 @@ function removeApproval(config, approvalId) {
175
174
 
176
175
  async function planWithAI(message, engine) {
177
176
  const systemPrompt = [
178
- 'Sen Vantuz otomasyon planlay?c?s?s?n.',
179
- 'Kullan?c? mesaj?n? tek bir otomasyon plan?na ?evir ve sadece JSON d?nd?r.',
180
- '?ema:',
177
+ 'Sen Vantuz otomasyon planlayıcısısın.',
178
+ 'Kullanıcı mesajını tek bir otomasyon planına çevir ve sadece JSON döndür.',
179
+ 'Şema:',
181
180
  '{ "intent": "report|analysis|change|schedule|other", "risk": "low|high", "schedule": "", "action": "" }',
182
- 'risk = pazaryeri de?i?ikli?i varsa "high" olmal?.',
183
- 'schedule = cron ifadesi (bo? olabilir).',
184
- 'action = yap?lacak i?i k?sa T?rk?e ?zetle.'
181
+ 'risk = SADECE veri değiştiren/silen işlemler (update/delete/create) varsa "high" olmalı. Listeleme/okuma işlemleri her zaman "low".',
182
+ 'schedule = cron ifadesi (boş olabilir).',
183
+ 'action = yapılacak işi kısa Türkçe özetle.'
185
184
  ].join('\n');
186
185
 
187
186
  const response = await aiChat(message, {
@@ -200,10 +199,14 @@ async function planWithAI(message, engine) {
200
199
 
201
200
  function fallbackPlan(message) {
202
201
  const risky = messageHasAny(message, RISKY_KEYWORDS);
202
+ // Explicitly safe keywords check
203
+ const safeKeywords = ['listele', 'göster', 'nedir', 'kaç', 'ne kadar', 'durum', 'rapor'];
204
+ const safe = messageHasAny(message, safeKeywords);
205
+
203
206
  const schedule = message.includes('cron ') ? message.split('cron ')[1].trim() : '';
204
207
  return {
205
208
  intent: schedule ? 'schedule' : (risky ? 'change' : 'analysis'),
206
- risk: risky ? 'high' : 'low',
209
+ risk: (risky && !safe) ? 'high' : 'low',
207
210
  schedule,
208
211
  action: message
209
212
  };
@@ -231,15 +234,31 @@ export class AutomationManager {
231
234
  if (!job?.name || !job?.cron || !job?.message) return;
232
235
 
233
236
  this.scheduler.addJob(job.name, job.cron, async () => {
237
+ log('INFO', `Cron job executing: ${job.name}`, { message: job.message });
234
238
  await this.engine.chat(job.message);
235
239
  }, true);
236
240
 
237
241
  if (persist) {
238
- this.config.automation.cronJobs.push(job);
239
- saveConfig(this.config);
242
+ const exists = this.config.automation.cronJobs.find(j => j.name === job.name);
243
+ if (!exists) {
244
+ this.config.automation.cronJobs.push(job);
245
+ saveConfig(this.config);
246
+ }
240
247
  }
241
248
  }
242
249
 
250
+ _deleteJob(name) {
251
+ this.scheduler.removeJob(name);
252
+ this.config.automation.cronJobs = this.config.automation.cronJobs.filter(j => j.name !== name && j.name !== `auto-${name}`);
253
+ saveConfig(this.config);
254
+ return true;
255
+ }
256
+
257
+ _listJobs() {
258
+ return this.config.automation.cronJobs || [];
259
+ }
260
+
261
+
243
262
  async handleMessage(message, meta = { channel: 'local', from: 'local' }) {
244
263
  this.config = ensureConfigShape(loadConfig());
245
264