beast-agent 0.19.0 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/agent/bots.js +495 -0
- package/src/agent/engine.js +3303 -3118
- package/src/agent/memory.js +419 -383
- package/src/agent/mqueue.js +124 -0
- package/src/agent/skills.js +523 -522
- package/src/agent/store.js +378 -0
- package/src/agent/watext.js +76 -75
- package/src/main.js +4719 -3951
- package/src/preload.js +149 -128
- package/src/renderer/i18n.js +1073 -795
- package/src/renderer/index.html +288 -231
- package/src/renderer/renderer.js +4317 -3356
- package/src/renderer/style.css +1826 -1307
- package/store/skills.json +5 -0
- package/tests/bg-jobs.test.js +4 -3
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/* OFFLINE MESAJ KUYRUĞU (FEATURE 2)
|
|
4
|
+
İnternet/elektrik kesintisinde WhatsApp mesajları kaybolmasın:
|
|
5
|
+
- Gönderilemeyen mesaj dosyaya yazılır (%APPDATA%\beast\message_queue.json)
|
|
6
|
+
- Bağlantı gelince sırayla gönderilir, gönderilenler kuyruktan silinir
|
|
7
|
+
- Retry: 30sn → 1dk → 5dk → 15dk → 30dk backoff; 5 denemeden sonra "failed"
|
|
8
|
+
- Dosya tabanlı olduğu için elektrik kesintisinde veri korunur */
|
|
9
|
+
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
const path = require('path');
|
|
12
|
+
const os = require('os');
|
|
13
|
+
|
|
14
|
+
const MAX_QUEUE = 100; // kuyruk üst sınırı — taşarsa eski (öncelikle işlenmiş) kayıtlar düşer
|
|
15
|
+
const MAX_RETRY = 5;
|
|
16
|
+
const BACKOFF_MS = [30e3, 60e3, 300e3, 900e3, 1800e3]; // 30sn, 1dk, 5dk, 15dk, 30dk
|
|
17
|
+
|
|
18
|
+
function root() {
|
|
19
|
+
if (process.env.BEAST_DATA) return process.env.BEAST_DATA;
|
|
20
|
+
return process.env.APPDATA
|
|
21
|
+
? path.join(process.env.APPDATA, 'beast')
|
|
22
|
+
: path.join(os.homedir(), 'AppData', 'Roaming', 'beast');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function file() {
|
|
26
|
+
return path.join(root(), 'message_queue.json');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
let CACHE = null;
|
|
30
|
+
|
|
31
|
+
function load() {
|
|
32
|
+
if (CACHE) return CACHE;
|
|
33
|
+
try {
|
|
34
|
+
const j = JSON.parse(fs.readFileSync(file(), 'utf8'));
|
|
35
|
+
CACHE = Array.isArray(j.items) ? j.items : [];
|
|
36
|
+
} catch {
|
|
37
|
+
CACHE = [];
|
|
38
|
+
}
|
|
39
|
+
return CACHE;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function save() {
|
|
43
|
+
try {
|
|
44
|
+
fs.mkdirSync(root(), { recursive: true });
|
|
45
|
+
const tmp = file() + '.tmp';
|
|
46
|
+
fs.writeFileSync(tmp, JSON.stringify({ items: CACHE }, null, 2));
|
|
47
|
+
fs.renameSync(tmp, file()); // atomik yazım — yarı kalmış dosya kirliliği olmaz
|
|
48
|
+
} catch {}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/* Gönderilemeyen mesajı kuyruğa al */
|
|
52
|
+
function add({ to, body }) {
|
|
53
|
+
const list = load();
|
|
54
|
+
const item = {
|
|
55
|
+
id: 'q' + Date.now().toString(36) + Math.random().toString(36).slice(2, 6),
|
|
56
|
+
timestamp: new Date().toISOString(),
|
|
57
|
+
to: String(to || ''),
|
|
58
|
+
body: String(body || '').slice(0, 4000),
|
|
59
|
+
status: 'pending',
|
|
60
|
+
retry_count: 0,
|
|
61
|
+
nextAt: 0, // hemen denenmeye hazır
|
|
62
|
+
};
|
|
63
|
+
list.push(item);
|
|
64
|
+
/* taşma koruması: önce sent/failed kayıtlarını, sonra en eski pending'i düşür */
|
|
65
|
+
while (list.length > MAX_QUEUE) {
|
|
66
|
+
const idx = list.findIndex((x) => x.status === 'sent' || x.status === 'failed');
|
|
67
|
+
if (idx >= 0) list.splice(idx, 1);
|
|
68
|
+
else list.shift();
|
|
69
|
+
}
|
|
70
|
+
save();
|
|
71
|
+
return item;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/* Şu an denenecek kuyruk öğeleri (backoff süresi dolmuş pending'ler, sırayla) */
|
|
75
|
+
function due() {
|
|
76
|
+
const now = Date.now();
|
|
77
|
+
return load()
|
|
78
|
+
.filter((x) => x.status === 'pending' && (x.nextAt || 0) <= now)
|
|
79
|
+
.sort((a, b) => String(a.timestamp).localeCompare(String(b.timestamp)));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/* Gönderildi → kuyruktan sil */
|
|
83
|
+
function markSent(id) {
|
|
84
|
+
const list = load();
|
|
85
|
+
const idx = list.findIndex((x) => x.id === id);
|
|
86
|
+
if (idx >= 0) {
|
|
87
|
+
list.splice(idx, 1);
|
|
88
|
+
save();
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/* Deneme başarısız → backoff planla; 5 denemeden sonra "failed" (gonderilemedi) */
|
|
93
|
+
function bumpRetry(id) {
|
|
94
|
+
const it = load().find((x) => x.id === id);
|
|
95
|
+
if (!it) return;
|
|
96
|
+
it.retry_count = (it.retry_count || 0) + 1;
|
|
97
|
+
if (it.retry_count >= MAX_RETRY) {
|
|
98
|
+
it.status = 'failed';
|
|
99
|
+
it.failedAt = new Date().toISOString();
|
|
100
|
+
} else {
|
|
101
|
+
it.nextAt = Date.now() + BACKOFF_MS[it.retry_count - 1];
|
|
102
|
+
}
|
|
103
|
+
save();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function pendingCount() {
|
|
107
|
+
return load().filter((x) => x.status === 'pending').length;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function stats() {
|
|
111
|
+
const l = load();
|
|
112
|
+
return {
|
|
113
|
+
pending: l.filter((x) => x.status === 'pending').length,
|
|
114
|
+
failed: l.filter((x) => x.status === 'failed').length,
|
|
115
|
+
total: l.length,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/* bağlantı geri gelince testler için */
|
|
120
|
+
function _reset() {
|
|
121
|
+
CACHE = null;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
module.exports = { MAX_QUEUE, MAX_RETRY, BACKOFF_MS, add, due, markSent, bumpRetry, pendingCount, stats, _reset, file };
|