vantuz 3.3.6 → 3.4.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.
Files changed (46) hide show
  1. package/README.md +25 -31
  2. package/cli.js +0 -0
  3. package/config.js +24 -0
  4. package/core/agent-loop.js +190 -0
  5. package/core/ai-analyst.js +2 -2
  6. package/core/ai-provider.js +41 -7
  7. package/core/automation.js +43 -24
  8. package/core/dashboard.js +230 -0
  9. package/core/database.js +63 -72
  10. package/core/engine.js +31 -4
  11. package/core/learning.js +214 -0
  12. package/core/license-manager.js +9 -9
  13. package/core/marketplace-adapter.js +168 -0
  14. package/core/memory.js +190 -0
  15. package/core/queue.js +120 -0
  16. package/core/scheduler.js +111 -31
  17. package/core/self-healer.js +314 -0
  18. package/core/unified-product.js +214 -0
  19. package/core/vector-db.js +83 -17
  20. package/core/vision-service.js +113 -0
  21. package/index.js +175 -0
  22. package/modules/crm/sentiment-crm.js +231 -0
  23. package/modules/healer/listing-healer.js +201 -0
  24. package/modules/oracle/predictor.js +214 -0
  25. package/modules/researcher/agent.js +169 -0
  26. package/modules/team/agents/base.js +92 -0
  27. package/modules/team/agents/dev.js +33 -0
  28. package/modules/team/agents/josh.js +40 -0
  29. package/modules/team/agents/marketing.js +33 -0
  30. package/modules/team/agents/milo.js +36 -0
  31. package/modules/team/index.js +78 -0
  32. package/modules/team/shared-memory.js +87 -0
  33. package/modules/war-room/competitor-tracker.js +250 -0
  34. package/modules/war-room/pricing-engine.js +308 -0
  35. package/nodes/warehouse.js +238 -0
  36. package/onboard.js +0 -0
  37. package/package.json +47 -87
  38. package/platforms/amazon.js +3 -8
  39. package/platforms/ciceksepeti.js +2 -5
  40. package/platforms/hepsiburada.js +4 -6
  41. package/platforms/n11.js +3 -5
  42. package/platforms/pazarama.js +2 -5
  43. package/platforms/trendyol.js +3 -3
  44. package/plugins/vantuz/index.js +48 -0
  45. package/server/app.js +142 -29
  46. package/vantuz-3.3.4.tgz +0 -0
package/README.md CHANGED
@@ -1,44 +1,38 @@
1
- # 🐙 Vantuz AI
1
+ # 🐙 VANTUZ (Enterprise Edition)
2
2
 
3
- **Yapay Zeka Destekli E-Ticaret Yönetim Platformu**
3
+ **OpenClaw Altyapısıyla Güçlendirilmiş E-Ticaret Zekası**
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ğildir. OpenClaw'ın yapay zeka ve otomasyon gücünü kullanarak e-ticaret operasyonlarınızı otopilota alan, ticari bir zekadır.
6
6
 
7
- ## Kurulum
7
+ ## 🚀 Neden Vantuz?
8
8
 
9
- ```bash
10
- npm install vantuz
11
- ```
12
-
13
- ## Hızlı Başlangıç
14
-
15
- ```bash
16
- # Yapılandırma
17
- npx vantuz config
18
-
19
- # Başlat
20
- npx vantuz tui
21
- ```
9
+ 1. **AI Vision (Göz):** Ürün fotoğraflarını tanır, Trendyol/Amazon için başlık, açıklama ve fiyatı otomatik oluşturur.
10
+ 2. **OpenClaw Entegrasyonu (Beyin):** Sistem arka planda çalışan OpenClaw Gateway ile konuşur. Web tarayıcısını kullanarak rakip fiyat analizi yapar, trendleri takip eder.
11
+ 3. **Cross-Border (Kol):** Tek tuşla ürünlerinizi yurt dışı pazarlarına (Amazon DE, Etsy vb.) uyarlar.
22
12
 
23
- ## Özellikler
13
+ ## 📦 Kurulum
24
14
 
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
15
+ Vantuz, **Node.js** tabanlıdır ancak son kullanıcı için tek bir `.exe` (Windows) veya Binary (Linux/Mac) olarak paketlenmiştir.
30
16
 
31
- ## Komutlar
17
+ ### Kaynak Koddan Kurulum (Geliştiriciler İçin)
32
18
 
33
19
  ```bash
34
- npx vantuz tui # Sohbet modu
35
- npx vantuz config # Ayarlar
36
- npx vantuz status # Durum kontrolü
20
+ git clone https://github.com/vantuz-ai/core.git
21
+ cd vantuz
22
+ npm install
23
+ npm link
24
+ vantuz
37
25
  ```
38
26
 
39
- ## Lisans
27
+ ### Lisanslama
28
+ Sistemi ilk açtığınızda **Lisans Anahtarı** girmeniz istenecektir. Lisanssız kullanımda sadece "Demo Modu" (kısıtlı veri) çalışır.
29
+
30
+ ## 🛠️ Desteklenen Platformlar
40
31
 
41
- Ticari yazılım. Kullanım için lisans anahtarı gereklidir.
32
+ * ✅ **Trendyol** (Tam Entegrasyon)
33
+ * 🚧 **Hepsiburada** (Beta)
34
+ * 🚧 **Amazon** (Geliştiriliyor)
35
+ * 🚧 **N11** (Planlandı)
42
36
 
43
- 📧 nuricanavsar2000@gmail.com
44
- 🌐 https://nuricanavsar.com
37
+ ---
38
+ **Powered by OpenClaw Framework**
package/cli.js CHANGED
File without changes
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) {
@@ -630,6 +631,29 @@ class Configurator {
630
631
  }
631
632
 
632
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
+
633
657
  async step5_Save() {
634
658
  this.printHeader('AYARLAR KAYDEDİLİYOR', '💾');
635
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;
@@ -7,7 +7,7 @@ module.exports = {
7
7
  }
8
8
 
9
9
  const prompt = `
10
- Senin adın Vantuz. uzman e-ticaret analistisin.
10
+ Sen OmniMarket AI'ın uzman e-ticaret analistisin.
11
11
  Aşağıdaki e-ticaret verilerini analiz et ve mağaza sahibine 3 adet kritik, eyleme dönüştürülebilir öneri sun.
12
12
  Yanıtın kısa, net ve profesyonel olsun. Türkçe yanıt ver.
13
13
 
@@ -18,7 +18,7 @@ ${JSON.stringify(data, null, 2)}
18
18
  try {
19
19
  const baseURL = config.ai.baseUrl || 'https://api.openai.com/v1';
20
20
  const model = config.ai.model || 'gpt-4-turbo';
21
-
21
+
22
22
  // Generic OpenAI Compatible Endpoint Support (Works with DeepSeek, OpenAI, LocalAI)
23
23
  const response = await axios.post(`${baseURL}/chat/completions`, {
24
24
  model: model,
@@ -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