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 +62 -26
- package/config.js +55 -8
- package/core/agent-loop.js +190 -0
- package/core/ai-provider.js +41 -7
- package/core/automation.js +43 -24
- package/core/dashboard.js +230 -0
- package/core/engine.js +323 -339
- package/core/gateway.js +139 -68
- package/core/learning.js +214 -0
- package/core/marketplace-adapter.js +168 -0
- package/core/memory.js +190 -0
- package/core/queue.js +120 -0
- package/core/scheduler.js +111 -31
- package/core/self-healer.js +314 -0
- package/core/unified-product.js +214 -0
- package/core/vector-db.js +83 -17
- package/core/vision-service.js +113 -0
- package/package.json +1 -1
- package/platforms/amazon.js +4 -1
- package/platforms/ciceksepeti.js +2 -1
- package/platforms/n11.js +2 -2
- package/platforms/pazarama.js +2 -1
- package/platforms/pttavm.js +14 -0
- package/server/app.js +142 -29
package/README.md
CHANGED
|
@@ -1,44 +1,80 @@
|
|
|
1
|
-
# 🐙 Vantuz
|
|
1
|
+
# 🐙 Vantuz OS
|
|
2
2
|
|
|
3
|
-
**
|
|
3
|
+
**Yeni Nesil E-Ticaret İşletim Sistemi**
|
|
4
4
|
|
|
5
|
-
|
|
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
|
-
##
|
|
7
|
+
## 🚀 Temel Yetenekler
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
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
|
-
|
|
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
|
-
#
|
|
17
|
-
|
|
35
|
+
# Vantuz'u global olarak kurun
|
|
36
|
+
npm install -g vantuz
|
|
18
37
|
|
|
19
|
-
#
|
|
20
|
-
|
|
38
|
+
# Yapılandırma sihirbazını başlatın
|
|
39
|
+
vantuz config
|
|
21
40
|
```
|
|
22
41
|
|
|
23
|
-
##
|
|
42
|
+
## 🎮 Kullanım
|
|
24
43
|
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
npx vantuz status # Durum kontrolü
|
|
59
|
+
# Depo bilgisayarında:
|
|
60
|
+
node nodes/warehouse.js
|
|
37
61
|
```
|
|
38
62
|
|
|
39
|
-
|
|
63
|
+
---
|
|
40
64
|
|
|
41
|
-
|
|
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
|
-
|
|
44
|
-
|
|
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
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
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
|
|
604
|
-
console.log(c('
|
|
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;
|
package/core/ai-provider.js
CHANGED
|
@@ -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
|
|
168
|
-
3.
|
|
169
|
-
4.
|
|
170
|
-
5.
|
|
171
|
-
6.
|
|
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
|
|
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
|
|
package/core/automation.js
CHANGED
|
@@ -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
|
-
'
|
|
14
|
-
'
|
|
15
|
-
'
|
|
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 = ['
|
|
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
|
|
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
|
|
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(/[
|
|
58
|
-
.replace(/[
|
|
59
|
-
.replace(/[
|
|
60
|
-
.replace(/[
|
|
61
|
-
.replace(/[
|
|
62
|
-
.replace(/[
|
|
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 || '
|
|
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
|
|
179
|
-
'
|
|
180
|
-
'
|
|
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 =
|
|
183
|
-
'schedule = cron ifadesi (
|
|
184
|
-
'action =
|
|
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.
|
|
239
|
-
|
|
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
|
|