beast-agent 0.15.0 → 0.16.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/.env.example +1 -0
- package/README.md +32 -15
- package/bin/beast-agent.js +13 -3
- package/config.example.yaml +15 -2
- package/package.json +2 -1
- package/scripts/release.js +134 -0
- package/src/main.js +207 -15
- package/src/preload.js +4 -0
- package/src/renderer/i18n.js +34 -0
- package/src/renderer/index.html +2 -0
- package/src/renderer/renderer.js +86 -3
package/.env.example
CHANGED
package/README.md
CHANGED
|
@@ -25,30 +25,47 @@ Beast Agent is a personal AI agent that runs on your machine and connects to **a
|
|
|
25
25
|
- **Full TR/EN interface** — everything switches language instantly
|
|
26
26
|
- **/health** — `http://127.0.0.1:8788/health` liveness endpoint from the moment the app boots
|
|
27
27
|
|
|
28
|
-
## 📦 Download
|
|
28
|
+
## 📦 Download & Install — 2 commands, ready
|
|
29
29
|
|
|
30
|
-
### Installer (recommended)
|
|
31
|
-
Grab `BeastAgent-Setup-x.x.x.exe` from the [Releases](../../releases) page. After install, Beast starts automatically with Windows (lives in the tray).
|
|
32
|
-
|
|
33
|
-
### npm
|
|
34
30
|
```bash
|
|
35
31
|
npm install -g beast-agent
|
|
36
32
|
```
|
|
37
|
-
Then run `beast-agent` from any terminal. On first launch Beast creates a desktop shortcut and registers itself for auto-start.
|
|
38
33
|
|
|
39
|
-
|
|
34
|
+
Then start it:
|
|
35
|
+
|
|
40
36
|
```bash
|
|
41
|
-
|
|
42
|
-
cd beast-agent
|
|
43
|
-
npm install
|
|
44
|
-
npm start
|
|
37
|
+
beast-agent
|
|
45
38
|
```
|
|
46
39
|
|
|
47
|
-
|
|
40
|
+
That's it — the app window opens. On first launch Beast also creates a **desktop shortcut** and registers itself to **start with Windows** (lives in the tray). Later updates: close the app and run `beast-agent update`.
|
|
48
41
|
|
|
49
42
|
## ⚙️ Configuration
|
|
50
43
|
|
|
51
|
-
|
|
44
|
+
### Recommended: OpenCode Zen (free tier, no credit card)
|
|
45
|
+
|
|
46
|
+
Get a key at [opencode.ai/auth](https://opencode.ai/auth), then copy `config.example.yaml` → `config.yaml`:
|
|
47
|
+
|
|
48
|
+
```yaml
|
|
49
|
+
defaultSelection: opencode::glm-5.2
|
|
50
|
+
providers:
|
|
51
|
+
- id: opencode
|
|
52
|
+
name: OpenCode Zen
|
|
53
|
+
baseUrl: https://opencode.ai/zen/v1
|
|
54
|
+
apiKey: <your-key>
|
|
55
|
+
models:
|
|
56
|
+
- glm-5.2
|
|
57
|
+
- kimi-k2.7-code
|
|
58
|
+
- deepseek-v4-flash-free
|
|
59
|
+
- big-pickle
|
|
60
|
+
- minimax-m3
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
> Free models include `deepseek-v4-flash-free`, `big-pickle`, `mimo-v2.5-free`, `nemotron-3-ultra-free`. If you have an **OpenCode Go** subscription, use the same config with `baseUrl: https://opencode.ai/zen/go/v1`.
|
|
64
|
+
|
|
65
|
+
### Any other OpenAI-compatible provider
|
|
66
|
+
|
|
67
|
+
OpenRouter, Zhipu, Ollama, OpenAI — anything that speaks `/v1/chat/completions` works:
|
|
68
|
+
|
|
52
69
|
```yaml
|
|
53
70
|
defaultSelection: openrouter::anthropic/claude-3.5-sonnet
|
|
54
71
|
providers:
|
|
@@ -60,8 +77,8 @@ providers:
|
|
|
60
77
|
- anthropic/claude-3.5-sonnet
|
|
61
78
|
- gpt-4o
|
|
62
79
|
```
|
|
63
|
-
|
|
64
|
-
|
|
80
|
+
|
|
81
|
+
API keys can also go into `.env` (copy `.env.example` → `.env`). On first launch, Settings → Provider pulls your models automatically.
|
|
65
82
|
|
|
66
83
|
All app data lives under `%APPDATA%\beast` (sessions, memory, WhatsApp pairing, encrypted settings).
|
|
67
84
|
|
package/bin/beast-agent.js
CHANGED
|
@@ -2,11 +2,21 @@
|
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
/* Beast Agent global npm başlatıcısı:
|
|
5
|
-
`beast-agent`
|
|
5
|
+
`beast-agent` → uygulamayı detached başlatır, terminali hemen serbest bırakır
|
|
6
|
+
`beast-agent update` → npm'den en son sürümü yükler (uygulama kapalıyken çalıştır) */
|
|
6
7
|
|
|
7
|
-
const { spawn } = require('child_process');
|
|
8
|
+
const { spawn, spawnSync } = require('child_process');
|
|
8
9
|
const path = require('path');
|
|
9
10
|
|
|
11
|
+
/* güncelleme modu: uygulama kapalıyken dosyalar kilitli olmaz */
|
|
12
|
+
if (process.argv[2] === 'update') {
|
|
13
|
+
const r = spawnSync('npm', ['install', '-g', 'beast-agent@latest'], { stdio: 'inherit', shell: true });
|
|
14
|
+
console.log(r.status === 0
|
|
15
|
+
? '\n✓ beast-agent güncellendi — "beast-agent" ile başlatabilirsin.'
|
|
16
|
+
: '\n✗ güncelleme başarısız — elle: npm install -g beast-agent@latest');
|
|
17
|
+
process.exit(r.status || 0);
|
|
18
|
+
}
|
|
19
|
+
|
|
10
20
|
const electron = require('electron');
|
|
11
21
|
if (typeof electron !== 'string') {
|
|
12
22
|
/* electron runtime içindeyiz — bu script için anlamsız */
|
|
@@ -17,7 +27,7 @@ const appPath = path.resolve(__dirname, '..');
|
|
|
17
27
|
const child = spawn(electron, [appPath, ...process.argv.slice(2)], {
|
|
18
28
|
stdio: 'ignore',
|
|
19
29
|
detached: true,
|
|
20
|
-
windowsHide:
|
|
30
|
+
/* windowsHide KULLANMA: Chromium ilk pencereyi gizli başlatıyor (tray-only bug) */
|
|
21
31
|
});
|
|
22
32
|
child.unref();
|
|
23
33
|
process.exit(0);
|
package/config.example.yaml
CHANGED
|
@@ -2,7 +2,20 @@
|
|
|
2
2
|
# Kullanım: bu dosyayı %APPDATA%\beast\config.yaml olarak kopyala ve düzenle.
|
|
3
3
|
# (Taşınabilir kullanım için BEAST_DATA ortam değişkeni ile başka klasöre alabilirsin.)
|
|
4
4
|
|
|
5
|
+
# ÖNERİLEN: OpenCode Zen — ücretsiz modeller, kredi kartı gerekmez (opencode.ai/auth)
|
|
5
6
|
providers:
|
|
7
|
+
opencode:
|
|
8
|
+
name: OpenCode Zen
|
|
9
|
+
base_url: https://opencode.ai/zen/v1
|
|
10
|
+
key_env: OPENCODE_API_KEY
|
|
11
|
+
models:
|
|
12
|
+
glm-5.2: {}
|
|
13
|
+
kimi-k2.7-code: {}
|
|
14
|
+
deepseek-v4-flash-free: {}
|
|
15
|
+
big-pickle: {}
|
|
16
|
+
minimax-m3: {}
|
|
17
|
+
# OpenCode Go aboneliğin varsa base_url: https://opencode.ai/zen/go/v1 kullan.
|
|
18
|
+
|
|
6
19
|
zhipu:
|
|
7
20
|
name: Zhipu AI
|
|
8
21
|
base_url: https://api.z.ai/api/paas/v4
|
|
@@ -20,7 +33,7 @@ providers:
|
|
|
20
33
|
gpt-4o: {}
|
|
21
34
|
|
|
22
35
|
model:
|
|
23
|
-
provider:
|
|
24
|
-
default: glm-
|
|
36
|
+
provider: opencode # varsayılan provider id (yoksa ilk provider kullanılır)
|
|
37
|
+
default: glm-5.2 # varsayılan model
|
|
25
38
|
# base_url: ... # istersen aktif provider'ın URL'ini override et
|
|
26
39
|
# key_env: ... # istersen anahtarı farklı env'den oku
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "beast-agent",
|
|
3
3
|
"productName": "Beast Agent",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.16.1",
|
|
5
5
|
"description": "Ultra-fast local agent shell for Windows.",
|
|
6
6
|
"author": "algokodcom (AlgoKod)",
|
|
7
7
|
"license": "MIT",
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
"start": "electron .",
|
|
32
32
|
"dist": "electron-builder --win nsis portable",
|
|
33
33
|
"test": "node --test \"tests/**/*.test.js\"",
|
|
34
|
+
"release": "node scripts/release.js",
|
|
34
35
|
"prepublishOnly": "node scripts/swap-electron.js deps",
|
|
35
36
|
"postpublish": "node scripts/swap-electron.js devdeps"
|
|
36
37
|
},
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/* Beast Agent — Dağıtım / Senkron Sistemi
|
|
4
|
+
Tek komutla tam sürüm akışı:
|
|
5
|
+
npm run release → otomatik patch bump (0.15.0 → 0.15.1)
|
|
6
|
+
npm run release -- minor → 0.16.0
|
|
7
|
+
npm release -- 0.17.0 → belirli sürüm
|
|
8
|
+
|
|
9
|
+
Adımlar:
|
|
10
|
+
1) package.json sürümü yükselt
|
|
11
|
+
2) commit + tag + push (main + tag)
|
|
12
|
+
3) npm run dist (NSIS setup + portable)
|
|
13
|
+
4) GitHub Release + exe upload (gh CLI)
|
|
14
|
+
5) npm publish (NPM_TOKEN env var ise; yoksa atlar ve uyarır)
|
|
15
|
+
6) OneDrive yedek klasörüne kaynak kopyası (beast-v< sürüm >)
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const fs = require('fs');
|
|
19
|
+
const path = require('path');
|
|
20
|
+
const { execSync } = require('child_process');
|
|
21
|
+
|
|
22
|
+
const ROOT = path.join(__dirname, '..');
|
|
23
|
+
const ONE_DRIVE_DIR = process.env.BEAST_BACKUP_DIR
|
|
24
|
+
|| 'C:\\Users\\batuh\\OneDrive\\Masaüstü\\Beast Agent';
|
|
25
|
+
|
|
26
|
+
const step = (msg) => console.log('\n\x1b[1m▶ ' + msg + '\x1b[0m');
|
|
27
|
+
const ok = (msg) => console.log(' \x1b[32m✓\x1b[0m ' + msg);
|
|
28
|
+
const warn = (msg) => console.log(' \x1b[33m!\x1b[0m ' + msg);
|
|
29
|
+
const fail = (msg) => { console.error(' \x1b[31m✗ ' + msg + '\x1b[0m'); process.exit(1); };
|
|
30
|
+
|
|
31
|
+
function run(cmd, opts = {}) {
|
|
32
|
+
return execSync(cmd, { cwd: ROOT, stdio: opts.inherit ? 'inherit' : 'pipe', encoding: 'utf8', ...opts }).trim();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function bump(version, kind) {
|
|
36
|
+
const [ma, mi, pa] = version.split('.').map(Number);
|
|
37
|
+
if (kind === 'major') return `${ma + 1}.0.0`;
|
|
38
|
+
if (kind === 'minor') return `${ma}.${mi + 1}.0`;
|
|
39
|
+
return `${ma}.${mi}.${pa + 1}`;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* ---------- argümanlar ---------- */
|
|
43
|
+
const arg = process.argv[2] || 'patch';
|
|
44
|
+
const pkgPath = path.join(ROOT, 'package.json');
|
|
45
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
46
|
+
const current = pkg.version;
|
|
47
|
+
const next = ['major', 'minor', 'patch'].includes(arg) ? bump(current, arg) : arg.replace(/^v/, '');
|
|
48
|
+
if (!/^\d+\.\d+\.\d+$/.test(next)) fail('geçersiz sürüm: ' + next);
|
|
49
|
+
const tag = 'v' + next;
|
|
50
|
+
|
|
51
|
+
console.log(`\x1b[1mBeast Agent release: ${current} → ${next}\x1b[0m`);
|
|
52
|
+
|
|
53
|
+
/* ---------- 1) sürüm bump ---------- */
|
|
54
|
+
step('1/6 sürüm yükselt: ' + current + ' → ' + next);
|
|
55
|
+
pkg.version = next;
|
|
56
|
+
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 4) + '\n');
|
|
57
|
+
ok('package.json → ' + next);
|
|
58
|
+
|
|
59
|
+
/* ---------- 2) commit + push ---------- */
|
|
60
|
+
step('2/6 git: commit + tag + push');
|
|
61
|
+
try {
|
|
62
|
+
run('git add -A');
|
|
63
|
+
run(`git commit -m "v${next}"`);
|
|
64
|
+
} catch { warn('commit edilecek değişiklik yoktu'); }
|
|
65
|
+
try { run(`git tag ${tag} -f`); } catch {}
|
|
66
|
+
run('git push origin main');
|
|
67
|
+
run(`git push origin ${tag}`);
|
|
68
|
+
ok('pushed: main + ' + tag);
|
|
69
|
+
|
|
70
|
+
/* ---------- 3) build ---------- */
|
|
71
|
+
step('3/6 build: npm run dist (birkaç dakika)');
|
|
72
|
+
run('npm run dist', { inherit: true });
|
|
73
|
+
const setupExe = path.join(ROOT, 'dist', `BeastAgent-Setup-${next}.exe`);
|
|
74
|
+
const portableExe = path.join(ROOT, 'dist', 'BeastAgent.exe');
|
|
75
|
+
if (!fs.existsSync(setupExe)) fail('setup exe bulunamadı: ' + setupExe);
|
|
76
|
+
if (!fs.existsSync(portableExe)) fail('portable exe bulunamadı: ' + portableExe);
|
|
77
|
+
ok('dist hazır: Setup + Portable');
|
|
78
|
+
|
|
79
|
+
/* ---------- 4) GitHub release ---------- */
|
|
80
|
+
step('4/6 GitHub Release ' + tag);
|
|
81
|
+
const gh = process.env.GH || 'gh';
|
|
82
|
+
try {
|
|
83
|
+
const notes = [
|
|
84
|
+
`## Beast Agent ${tag}`,
|
|
85
|
+
'',
|
|
86
|
+
'- `BeastAgent-Setup-${next}.exe` — kurulumlu (önerilen)',
|
|
87
|
+
'- \`BeastAgent.exe\` — portable',
|
|
88
|
+
'- \`npm i -g beast-agent\` — npm üzerinden',
|
|
89
|
+
'',
|
|
90
|
+
'Tam değişiklik listesi: commit geçmişi.',
|
|
91
|
+
].join('\n');
|
|
92
|
+
const notesFile = path.join(ROOT, 'dist', 'release-notes.md');
|
|
93
|
+
fs.writeFileSync(notesFile, notes);
|
|
94
|
+
const assets = [setupExe, setupExe + '.blockmap', portableExe, path.join(ROOT, 'dist', 'latest.yml')]
|
|
95
|
+
.filter((f) => fs.existsSync(f))
|
|
96
|
+
.map((f) => `"${f}"`)
|
|
97
|
+
.join(' ');
|
|
98
|
+
try { run(`${gh} release delete ${tag} --yes --cleanup-tag`); } catch {}
|
|
99
|
+
run(`${gh} release create ${tag} ${assets} --title "Beast Agent ${tag}" --notes-file "${notesFile}"`, { inherit: true });
|
|
100
|
+
ok('release yayında: https://github.com/algokodcom/beast-agent/releases/tag/' + tag);
|
|
101
|
+
} catch (e) {
|
|
102
|
+
fail('GitHub release: ' + String(e));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/* ---------- 5) npm publish ---------- */
|
|
106
|
+
step('5/6 npm publish');
|
|
107
|
+
if (process.env.NPM_TOKEN) {
|
|
108
|
+
try {
|
|
109
|
+
run(`npm publish --//registry.npmjs.org/:_authToken=${process.env.NPM_TOKEN}`, { inherit: true });
|
|
110
|
+
ok('npm: beast-agent@' + next);
|
|
111
|
+
} catch (e) {
|
|
112
|
+
warn('npm publish başarısız (sürüm zaten var olabilir): ' + String(e).slice(0, 200));
|
|
113
|
+
}
|
|
114
|
+
} else {
|
|
115
|
+
warn('NPM_TOKEN env yok — npm adımı atlandı.');
|
|
116
|
+
warn('Elle: set NPM_TOKEN=<token> && npm publish');
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/* ---------- 6) OneDrive kaynak yedeği ---------- */
|
|
120
|
+
step('6/6 OneDrive yedek (beast-v' + next + ')');
|
|
121
|
+
try {
|
|
122
|
+
const dest = path.join(ONE_DRIVE_DIR, 'beast-v' + next);
|
|
123
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
124
|
+
run(`robocopy "${ROOT}" "${dest}" /E /XD node_modules dist "beast agent web" .git /NFL /NDL /NJH`);
|
|
125
|
+
const info = path.join(dest, `YEDEK-BILGI-v${next}.txt`);
|
|
126
|
+
fs.writeFileSync(info, `BEAST AGENT v${next} — ${new Date().toLocaleString('tr-TR')}\nKaynak: ${ROOT}\nGitHub: https://github.com/algokodcom/beast-agent/releases/tag/${tag}\nnpm: https://www.npmjs.com/package/beast-agent\n`);
|
|
127
|
+
ok(dest);
|
|
128
|
+
} catch (e) {
|
|
129
|
+
warn('OneDrive yedeği atlandı: ' + String(e).slice(0, 160));
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
console.log(`\n\x1b[1m\x1b[32m✓ v${next} dağıtımı tamam.\x1b[0m`);
|
|
133
|
+
console.log(' GitHub : https://github.com/algokodcom/beast-agent/releases/tag/' + tag);
|
|
134
|
+
console.log(' npm : https://www.npmjs.com/package/beast-agent');
|
package/src/main.js
CHANGED
|
@@ -19,27 +19,84 @@ const computeruse = require('./agent/computeruse');
|
|
|
19
19
|
const log = require('./agent/logger');
|
|
20
20
|
|
|
21
21
|
/* #3 otomatik updater: sessiz — indirir, kapanışta kurar, kullanıcıya soru sormaz.
|
|
22
|
-
Paketlenmemiş (npm start) modda devre d
|
|
22
|
+
Paketlenmemiş (npm start) modda devre dışı; Update sekmesi ve /update komutu kontrol eder. */
|
|
23
23
|
let autoUpdater = null;
|
|
24
24
|
try { if (app.isPackaged) autoUpdater = require('electron-updater').autoUpdater; } catch {}
|
|
25
25
|
|
|
26
|
+
const updateState = { checking: false, available: false, downloaded: false, version: null, progress: null, error: null };
|
|
27
|
+
const updateReplies = { sids: new Set(), jids: new Set() }; // /update isteyen hedefler — sonuç oraya gider
|
|
28
|
+
|
|
29
|
+
function isNpmMode() {
|
|
30
|
+
return !app.isPackaged && /node_modules[\\/]beast-agent/i.test(String(app.getAppPath()));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function emitUpdateEvent() {
|
|
34
|
+
try {
|
|
35
|
+
if (win && !win.isDestroyed()) {
|
|
36
|
+
win.webContents.send('agent:event', { type: 'update', ...updateState, current: app.getVersion() });
|
|
37
|
+
}
|
|
38
|
+
} catch {}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function replyUpdate(text) {
|
|
42
|
+
try {
|
|
43
|
+
for (const sid of updateReplies.sids) desktopEcho(sid, '/update', text);
|
|
44
|
+
for (const jid of updateReplies.jids) sendWaSafe(jid, text).catch(() => {});
|
|
45
|
+
updateReplies.sids.clear();
|
|
46
|
+
updateReplies.jids.clear();
|
|
47
|
+
} catch {}
|
|
48
|
+
}
|
|
49
|
+
|
|
26
50
|
function startAutoUpdater() {
|
|
27
51
|
if (!autoUpdater || !app.isPackaged) return;
|
|
28
52
|
try {
|
|
29
|
-
autoUpdater.autoDownload =
|
|
30
|
-
autoUpdater.autoInstallOnAppQuit = true;
|
|
53
|
+
autoUpdater.autoDownload = settings.autoDownloadUpdate !== false; // sessiz indir (toggle'lı)
|
|
54
|
+
autoUpdater.autoInstallOnAppQuit = true; // kapanışta sessiz kur
|
|
31
55
|
autoUpdater.logger = {
|
|
32
56
|
info: (m) => waLog('[UPD] ' + m),
|
|
33
57
|
warn: (m) => waLog('[UPD] ' + m),
|
|
34
58
|
error: (m) => waLog('[UPD] ' + m),
|
|
35
59
|
debug: () => {},
|
|
36
60
|
};
|
|
37
|
-
autoUpdater.on('update
|
|
38
|
-
|
|
61
|
+
autoUpdater.on('checking-for-update', () => {
|
|
62
|
+
updateState.checking = true; updateState.error = null; emitUpdateEvent();
|
|
63
|
+
});
|
|
64
|
+
autoUpdater.on('update-available', (i) => {
|
|
65
|
+
updateState.checking = false; updateState.available = true;
|
|
66
|
+
updateState.version = (i && i.version) || null;
|
|
67
|
+
emitUpdateEvent();
|
|
68
|
+
replyUpdate(`🔄 *Yeni sürüm bulundu:* v${updateState.version} (mevcut v${app.getVersion()}) — indiriliyor…`);
|
|
69
|
+
});
|
|
70
|
+
autoUpdater.on('update-not-available', () => {
|
|
71
|
+
updateState.checking = false; updateState.available = false; updateState.version = null;
|
|
72
|
+
emitUpdateEvent();
|
|
73
|
+
replyUpdate(`✅ *Güncelsin* — v${app.getVersion()} en son sürüm.`);
|
|
74
|
+
});
|
|
75
|
+
autoUpdater.on('download-progress', (p) => {
|
|
76
|
+
updateState.progress = {
|
|
77
|
+
percent: Math.round(Number(p && p.percent) || 0),
|
|
78
|
+
mbps: Math.round((Number(p && p.bytesPerSecond) || 0) / 1048576 * 10) / 10,
|
|
79
|
+
};
|
|
80
|
+
emitUpdateEvent();
|
|
81
|
+
});
|
|
82
|
+
autoUpdater.on('update-downloaded', (i) => {
|
|
83
|
+
updateState.checking = false; updateState.downloaded = true;
|
|
84
|
+
updateState.version = (i && i.version) || updateState.version;
|
|
85
|
+
updateState.progress = null;
|
|
86
|
+
emitUpdateEvent();
|
|
87
|
+
waLog('[UPD] güncelleme indirildi — /update now veya kapanışta kurulacak');
|
|
88
|
+
replyUpdate(`✅ *v${updateState.version} indirildi.* Kurmak için: \`/update now\` — ya da uygulama kapanınca otomatik kurulur.`);
|
|
39
89
|
});
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
90
|
+
autoUpdater.on('error', (e) => {
|
|
91
|
+
updateState.checking = false; updateState.error = String((e && e.message) || e);
|
|
92
|
+
emitUpdateEvent();
|
|
93
|
+
});
|
|
94
|
+
/* otomatik kontrol: açılışta + 6 saatte bir (Update sekmesinden kapatılabilir) */
|
|
95
|
+
if (settings.autoCheckUpdate !== false) {
|
|
96
|
+
const check = () => autoUpdater.checkForUpdates().catch(() => {});
|
|
97
|
+
check();
|
|
98
|
+
setInterval(check, 6 * 60 * 60 * 1000);
|
|
99
|
+
}
|
|
43
100
|
} catch {}
|
|
44
101
|
}
|
|
45
102
|
const { htmlToText, setExaKey, setTinyfishKey } = require('./agent/tools');
|
|
@@ -472,6 +529,7 @@ function waSlashHelp() {
|
|
|
472
529
|
'• /allow <isim> <numara> – WhatsApp allow listesine kişi ekle (örn: /allow batu 905414178456)',
|
|
473
530
|
'• /block – allow listesini numaralarıyla listele (/block 3: 3. kişiyi çıkar; 1 = sahip, silinemez)',
|
|
474
531
|
'• /approve – bekleyen riskli işlemi onayla (/approve always: bir daha sorulmasın · /deny: reddet)',
|
|
532
|
+
'• /update – yeni sürüm kontrolü (/update now: indirileni hemen kur)',
|
|
475
533
|
'• /model – aktif modeli göster (/model <isim> ile değiştir)',
|
|
476
534
|
'• /skills – kurulu skill\u2019ler',
|
|
477
535
|
'• /usage – bugünkü kullanım',
|
|
@@ -848,6 +906,19 @@ async function tryWaSlash(jid, rawText, senderNum) {
|
|
|
848
906
|
out = r.ok
|
|
849
907
|
? `*${cmd === 'deny' ? 'Reddedildi' : 'Onaylandı'}:* ${r.tool}${always ? ' — bu araç için bir daha sorulmayacak' : ''}`
|
|
850
908
|
: 'Bekleyen onay yok.';
|
|
909
|
+
} else if (cmd === 'update') {
|
|
910
|
+
/* /update — sürüm kontrol; /update now — indirileni kur */
|
|
911
|
+
if (String(arg || '').toLowerCase() === 'now') {
|
|
912
|
+
if (updateState.downloaded && autoUpdater) {
|
|
913
|
+
out = `*v${updateState.version} kuruluyor* — uygulama yeniden başlayacak.`;
|
|
914
|
+
setTimeout(() => { try { autoUpdater.quitAndInstall(); } catch {} }, 1200);
|
|
915
|
+
} else {
|
|
916
|
+
out = 'İndirilmiş sürüm yok — önce `/update` yaz.';
|
|
917
|
+
}
|
|
918
|
+
} else {
|
|
919
|
+
updateReplies.jids.add(jid);
|
|
920
|
+
await runUpdateCommand(async (text) => { out = text; });
|
|
921
|
+
}
|
|
851
922
|
} else if (cmd === 'think') {
|
|
852
923
|
const r = arg ? applyThinkLevel(arg) : null;
|
|
853
924
|
out = r && r.error ? r.error : r ? r.text : thinkStatusText();
|
|
@@ -1604,11 +1675,8 @@ if (!gotLock) {
|
|
|
1604
1675
|
app.quit();
|
|
1605
1676
|
} else {
|
|
1606
1677
|
app.on('second-instance', () => {
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
win.show();
|
|
1610
|
-
win.focus();
|
|
1611
|
-
}
|
|
1678
|
+
/* ikinci `beast-agent` çağrısı: tepside gizli olsa bile pencereyi öne getir */
|
|
1679
|
+
try { showWin(); } catch {}
|
|
1612
1680
|
});
|
|
1613
1681
|
|
|
1614
1682
|
/* npm (global) kurulumda masaüstü kısayolu — yoksa bir kez oluşturulur.
|
|
@@ -1623,7 +1691,7 @@ function ensureDesktopShortcut() {
|
|
|
1623
1691
|
args: app.getAppPath(),
|
|
1624
1692
|
cwd: app.getAppPath(),
|
|
1625
1693
|
description: 'Beast Agent — hızlı, hafif ve becerikli',
|
|
1626
|
-
icon:
|
|
1694
|
+
icon: path.join(__dirname, '..', 'assets', 'app.ico'),
|
|
1627
1695
|
iconIndex: 0,
|
|
1628
1696
|
});
|
|
1629
1697
|
log.info('main', ok ? 'Masaüstü kısayolu oluşturuldu (npm modu)' : 'Masaüstü kısayolu oluşturulamadı');
|
|
@@ -1662,6 +1730,7 @@ app.whenReady().then(() => {
|
|
|
1662
1730
|
ensureDesktopShortcut();
|
|
1663
1731
|
}
|
|
1664
1732
|
reloadBackend();
|
|
1733
|
+
createSplash();
|
|
1665
1734
|
createWindow();
|
|
1666
1735
|
log.info('main', 'Beast Agent başlatıldı');
|
|
1667
1736
|
createTray();
|
|
@@ -2362,6 +2431,49 @@ const startHidden =
|
|
|
2362
2431
|
process.argv.includes('--silent') ||
|
|
2363
2432
|
String(process.env.BEAST_HIDDEN || '') === '1';
|
|
2364
2433
|
|
|
2434
|
+
/* Splash: npm/ portable başlangıcında logolu karşılama penceresi — ana pencere
|
|
2435
|
+
hazır olunca kapanır. */
|
|
2436
|
+
let splash = null;
|
|
2437
|
+
|
|
2438
|
+
function createSplash() {
|
|
2439
|
+
try {
|
|
2440
|
+
const dark = settings.theme === 'dark';
|
|
2441
|
+
const bg = dark ? '#0d0d0f' : '#f7f7f8';
|
|
2442
|
+
const fg = dark ? '#f2f2f4' : '#17171a';
|
|
2443
|
+
const muted = dark ? '#9a9aa2' : '#707078';
|
|
2444
|
+
splash = new BrowserWindow({
|
|
2445
|
+
width: 420,
|
|
2446
|
+
height: 250,
|
|
2447
|
+
frame: false,
|
|
2448
|
+
resizable: false,
|
|
2449
|
+
alwaysOnTop: true,
|
|
2450
|
+
skipTaskbar: true,
|
|
2451
|
+
center: true,
|
|
2452
|
+
backgroundColor: bg,
|
|
2453
|
+
icon: path.join(__dirname, '..', 'assets', 'app.ico'),
|
|
2454
|
+
});
|
|
2455
|
+
const html = `<!doctype html><html><head><meta charset="utf-8"><style>
|
|
2456
|
+
body{margin:0;height:100vh;display:flex;flex-direction:column;align-items:center;justify-content:center;background:${bg};font-family:'Segoe UI',sans-serif;color:${fg}}
|
|
2457
|
+
.logo{width:84px;height:84px;border-radius:20px;background:${fg};color:${bg};display:flex;align-items:center;justify-content:center;font-weight:900;font-size:44px}
|
|
2458
|
+
.t{margin-top:16px;font-size:20px;color:${fg}}.t b{font-weight:900}
|
|
2459
|
+
.s{margin-top:6px;font-size:12px;color:${muted}}
|
|
2460
|
+
.bar{margin-top:18px;width:180px;height:3px;background:${muted}44;border-radius:2px;overflow:hidden}
|
|
2461
|
+
.bar>i{display:block;height:100%;width:40%;background:${fg};border-radius:2px;animation:sw 1.1s ease-in-out infinite}
|
|
2462
|
+
@keyframes sw{0%{transform:translateX(-100%)}100%{transform:translateX(260%)}}
|
|
2463
|
+
</style></head><body>
|
|
2464
|
+
<div class="logo">B</div>
|
|
2465
|
+
<div class="t"><b>BEAST</b> Agent</div>
|
|
2466
|
+
<div class="s">hızlı · hafif · becerikli — v${app.getVersion()}</div>
|
|
2467
|
+
<div class="bar"><i></i></div>
|
|
2468
|
+
</body></html>`;
|
|
2469
|
+
splash.loadURL('data:text/html;charset=utf-8,' + encodeURIComponent(html));
|
|
2470
|
+
} catch {}
|
|
2471
|
+
}
|
|
2472
|
+
|
|
2473
|
+
function closeSplash() {
|
|
2474
|
+
try { if (splash) { splash.close(); splash = null; } } catch {}
|
|
2475
|
+
}
|
|
2476
|
+
|
|
2365
2477
|
function createWindow() {
|
|
2366
2478
|
log.info('main', 'Pencere oluşturuluyor…');
|
|
2367
2479
|
const dark = settings.theme === 'dark';
|
|
@@ -2372,6 +2484,7 @@ function createWindow() {
|
|
|
2372
2484
|
minHeight: 500,
|
|
2373
2485
|
show: false,
|
|
2374
2486
|
backgroundColor: dark ? '#0d0d0f' : '#f7f7f8',
|
|
2487
|
+
icon: path.join(__dirname, '..', 'assets', 'app.ico'),
|
|
2375
2488
|
titleBarStyle: 'hidden',
|
|
2376
2489
|
titleBarOverlay: {
|
|
2377
2490
|
color: 'transparent',
|
|
@@ -2390,8 +2503,16 @@ function createWindow() {
|
|
|
2390
2503
|
win.setMenuBarVisibility(false);
|
|
2391
2504
|
win.loadFile(path.join(__dirname, 'renderer', 'index.html'));
|
|
2392
2505
|
win.once('ready-to-show', () => {
|
|
2506
|
+
closeSplash();
|
|
2393
2507
|
if (startHidden) win.hide();
|
|
2394
|
-
else
|
|
2508
|
+
else {
|
|
2509
|
+
win.show();
|
|
2510
|
+
win.focus();
|
|
2511
|
+
/* emniyet: bazı başlatma yollarında ilk show yutulur — tekrar dene */
|
|
2512
|
+
setTimeout(() => {
|
|
2513
|
+
try { if (win && !win.isDestroyed() && !win.isVisible()) { win.show(); win.focus(); } } catch {}
|
|
2514
|
+
}, 1200);
|
|
2515
|
+
}
|
|
2395
2516
|
});
|
|
2396
2517
|
win.on('resize', layoutBrowser);
|
|
2397
2518
|
win.on('maximize', layoutBrowser);
|
|
@@ -2489,6 +2610,21 @@ ipcMain.handle('agent:send', (_e, { sessionId, text }) => {
|
|
|
2489
2610
|
);
|
|
2490
2611
|
return true;
|
|
2491
2612
|
}
|
|
2613
|
+
if (t === '/update' || t.startsWith('/update ')) {
|
|
2614
|
+
const a = t.slice(7).trim().toLowerCase();
|
|
2615
|
+
updateReplies.sids.add(String(sessionId || ''));
|
|
2616
|
+
if (a === 'now') {
|
|
2617
|
+
if (updateState.downloaded && autoUpdater) {
|
|
2618
|
+
desktopEcho(sessionId, t, `*v${updateState.version} kuruluyor* — uygulama yeniden başlayacak.`);
|
|
2619
|
+
setTimeout(() => { try { autoUpdater.quitAndInstall(); } catch {} }, 1200);
|
|
2620
|
+
} else {
|
|
2621
|
+
desktopEcho(sessionId, t, 'İndirilmiş sürüm yok — önce `/update` yaz.');
|
|
2622
|
+
}
|
|
2623
|
+
} else {
|
|
2624
|
+
runUpdateCommand((text) => desktopEcho(sessionId, t, text));
|
|
2625
|
+
}
|
|
2626
|
+
return true;
|
|
2627
|
+
}
|
|
2492
2628
|
resumeServices(); // pause durumunda gerçek mesaj her şeyi canlandırır
|
|
2493
2629
|
queueDesktopMessage(sessionId, text);
|
|
2494
2630
|
return true;
|
|
@@ -2998,6 +3134,62 @@ ipcMain.handle('sec:set', (_e, cfg) => {
|
|
|
2998
3134
|
});
|
|
2999
3135
|
ipcMain.handle('approval:respond', (_e, { id, ok, always }) => resolveApproval(id, ok, always));
|
|
3000
3136
|
|
|
3137
|
+
/* ---------------- #Update IPC ---------------- */
|
|
3138
|
+
ipcMain.handle('update:status', () => ({
|
|
3139
|
+
current: app.getVersion(),
|
|
3140
|
+
packaged: app.isPackaged,
|
|
3141
|
+
npm: isNpmMode(),
|
|
3142
|
+
...updateState,
|
|
3143
|
+
autoCheck: settings.autoCheckUpdate !== false,
|
|
3144
|
+
autoDownload: settings.autoDownloadUpdate !== false,
|
|
3145
|
+
}));
|
|
3146
|
+
|
|
3147
|
+
ipcMain.handle('update:check', async (_e, viaCommand) => {
|
|
3148
|
+
if (isNpmMode()) return { ok: false, npm: true, error: 'npm kurulumu — güncelleme: beast-agent update' };
|
|
3149
|
+
if (!autoUpdater) return { ok: false, error: 'updater kullanılamıyor (taşınabilir/geliştirme modu)' };
|
|
3150
|
+
try {
|
|
3151
|
+
const r = await autoUpdater.checkForUpdates();
|
|
3152
|
+
const v = r && r.update && r.update.version;
|
|
3153
|
+
return { ok: true, version: v, available: !!v && v !== app.getVersion() };
|
|
3154
|
+
} catch (e) {
|
|
3155
|
+
return { ok: false, error: String((e && e.message) || e) };
|
|
3156
|
+
}
|
|
3157
|
+
});
|
|
3158
|
+
|
|
3159
|
+
ipcMain.handle('update:install', () => {
|
|
3160
|
+
if (isNpmMode() || !autoUpdater) return { ok: false, error: 'updater kullanılamıyor' };
|
|
3161
|
+
if (!updateState.downloaded) return { ok: false, error: 'indirilmiş sürüm yok — önce /update' };
|
|
3162
|
+
try { autoUpdater.quitAndInstall(); return { ok: true }; } catch (e) {
|
|
3163
|
+
return { ok: false, error: String((e && e.message) || e) };
|
|
3164
|
+
}
|
|
3165
|
+
});
|
|
3166
|
+
|
|
3167
|
+
ipcMain.handle('update:setAuto', (_e, cfg) => {
|
|
3168
|
+
if (cfg && typeof cfg.autoCheck === 'boolean') settings.autoCheckUpdate = cfg.autoCheck;
|
|
3169
|
+
if (cfg && typeof cfg.autoDownload === 'boolean') {
|
|
3170
|
+
settings.autoDownloadUpdate = cfg.autoDownload;
|
|
3171
|
+
if (autoUpdater) autoUpdater.autoDownload = cfg.autoDownload;
|
|
3172
|
+
}
|
|
3173
|
+
saveSettings();
|
|
3174
|
+
return { autoCheck: settings.autoCheckUpdate !== false, autoDownload: settings.autoDownloadUpdate !== false };
|
|
3175
|
+
});
|
|
3176
|
+
|
|
3177
|
+
/* /update komutu (masaüstü + WA): hedefi kaydet, kontrol başlat */
|
|
3178
|
+
async function runUpdateCommand(reply /* fn(text) */) {
|
|
3179
|
+
if (isNpmMode()) {
|
|
3180
|
+
reply('npm kurulumu — güncellemek için:\n1) Uygulamayı kapat\n2) Terminalde: `beast-agent update`\n3) Tekrar: `beast-agent`');
|
|
3181
|
+
return;
|
|
3182
|
+
}
|
|
3183
|
+
if (!autoUpdater) {
|
|
3184
|
+
reply('Updater bu modda kullanılamıyor (taşınabilir sürüm). Yeni exe: github.com/algokodcom/beast-agent/releases');
|
|
3185
|
+
return;
|
|
3186
|
+
}
|
|
3187
|
+
reply(`🔍 v${app.getVersion()} — güncellemeler kontrol ediliyor…`);
|
|
3188
|
+
try { await autoUpdater.checkForUpdates(); } catch (e) {
|
|
3189
|
+
reply('Güncelleme kontrolü başarısız: ' + String((e && e.message) || e));
|
|
3190
|
+
}
|
|
3191
|
+
}
|
|
3192
|
+
|
|
3001
3193
|
/* #STT: sohbet mikrofonu — MediaRecorder sesini (webm/opus) yerel whisper'a çevir */
|
|
3002
3194
|
ipcMain.handle('stt:transcribe', async (_e, b64) => {
|
|
3003
3195
|
try {
|
package/src/preload.js
CHANGED
|
@@ -45,6 +45,10 @@ contextBridge.exposeInMainWorld('beast', {
|
|
|
45
45
|
secSet: (cfg) => ipcRenderer.invoke('sec:set', cfg),
|
|
46
46
|
approvalRespond: (id, ok, always) => ipcRenderer.invoke('approval:respond', { id, ok, always }),
|
|
47
47
|
sttTranscribe: (b64) => ipcRenderer.invoke('stt:transcribe', b64),
|
|
48
|
+
updateStatus: () => ipcRenderer.invoke('update:status'),
|
|
49
|
+
updateCheck: () => ipcRenderer.invoke('update:check'),
|
|
50
|
+
updateInstall: () => ipcRenderer.invoke('update:install'),
|
|
51
|
+
updateSetAuto: (cfg) => ipcRenderer.invoke('update:setAuto', cfg),
|
|
48
52
|
setModel: (sel) => ipcRenderer.invoke('model:set', sel),
|
|
49
53
|
setRoleModels: (map) => ipcRenderer.invoke('model:role', map),
|
|
50
54
|
deleteModel: (sel) => ipcRenderer.invoke('model:delete', sel),
|
package/src/renderer/i18n.js
CHANGED
|
@@ -61,6 +61,23 @@
|
|
|
61
61
|
tab_websearch: 'Web Arama',
|
|
62
62
|
tab_limits: 'Limit Ayarları',
|
|
63
63
|
tab_security: 'Güvenlik',
|
|
64
|
+
tab_update: 'Güncelleme',
|
|
65
|
+
up_h2: 'Güncelleme',
|
|
66
|
+
up_sub: 'Sürümler GitHub Releases\u2019ten gelir. Otomatik kontrol açılışta ve 6 saatte bir çalışır.',
|
|
67
|
+
up_current: 'Mevcut sürüm',
|
|
68
|
+
up_latest: 'En son sürüm',
|
|
69
|
+
up_status: 'Durum',
|
|
70
|
+
up_checking: 'Kontrol ediliyor…',
|
|
71
|
+
up_available: 'Yeni sürüm var',
|
|
72
|
+
up_uptodate: 'Güncel',
|
|
73
|
+
up_downloading: 'İndiriliyor…',
|
|
74
|
+
up_downloaded: 'İndirildi — kurulum için hazır',
|
|
75
|
+
up_npm_mode: 'npm kurulumu — güncelleme: beast-agent update',
|
|
76
|
+
up_auto_check: 'Otomatik sürüm kontrolü (açılış + 6 saatte bir)',
|
|
77
|
+
up_auto_dl: 'Yeni sürümü otomatik indir (kapanışta kurulur)',
|
|
78
|
+
up_check_now: 'Şimdi Kontrol Et',
|
|
79
|
+
up_install_now: 'Yeniden Başlat & Kur',
|
|
80
|
+
up_note: 'Kurulumdan sonra uygulama otomatik yeniden başlar. İstersen WhatsApp\u2019tan /update komutunu da kullanabilirsin.',
|
|
64
81
|
tab_events: 'Olay Merkezi',
|
|
65
82
|
tab_cron: 'Cron',
|
|
66
83
|
tab_usage: 'Maliyet',
|
|
@@ -394,6 +411,23 @@
|
|
|
394
411
|
tab_websearch: 'Web Search',
|
|
395
412
|
tab_limits: 'Limits',
|
|
396
413
|
tab_security: 'Security',
|
|
414
|
+
tab_update: 'Update',
|
|
415
|
+
up_h2: 'Update',
|
|
416
|
+
up_sub: 'Versions come from GitHub Releases. Automatic check runs on startup and every 6 hours.',
|
|
417
|
+
up_current: 'Current version',
|
|
418
|
+
up_latest: 'Latest version',
|
|
419
|
+
up_status: 'Status',
|
|
420
|
+
up_checking: 'Checking…',
|
|
421
|
+
up_available: 'New version available',
|
|
422
|
+
up_uptodate: 'Up to date',
|
|
423
|
+
up_downloading: 'Downloading…',
|
|
424
|
+
up_downloaded: 'Downloaded — ready to install',
|
|
425
|
+
up_npm_mode: 'npm install — update via: beast-agent update',
|
|
426
|
+
up_auto_check: 'Automatic version check (on startup + every 6 hours)',
|
|
427
|
+
up_auto_dl: 'Auto-download new versions (installed on quit)',
|
|
428
|
+
up_check_now: 'Check Now',
|
|
429
|
+
up_install_now: 'Restart & Install',
|
|
430
|
+
up_note: 'The app restarts itself after installing. You can also use the /update command from WhatsApp.',
|
|
397
431
|
tab_events: 'Event Center',
|
|
398
432
|
tab_cron: 'Cron',
|
|
399
433
|
tab_usage: 'Costs',
|
package/src/renderer/index.html
CHANGED
|
@@ -109,6 +109,7 @@
|
|
|
109
109
|
<button class="tab" data-tab="dash" data-i18n="tab_dash">Dashboard</button>
|
|
110
110
|
<button class="tab" data-tab="limits" data-i18n="tab_limits">Limit</button>
|
|
111
111
|
<button class="tab" data-tab="sec" data-i18n="tab_security">Güvenlik</button>
|
|
112
|
+
<button class="tab" data-tab="update" data-i18n="tab_update">Güncelleme</button>
|
|
112
113
|
<div class="set-tabs-foot">
|
|
113
114
|
<div id="beastCodeBox" class="beast-code" data-i18n-title="bc_label" title="Beast Kodu">
|
|
114
115
|
<div class="bc-row">
|
|
@@ -135,6 +136,7 @@
|
|
|
135
136
|
<div id="tab-dash" class="pane" hidden></div>
|
|
136
137
|
<div id="tab-limits" class="pane" hidden></div>
|
|
137
138
|
<div id="tab-sec" class="pane" hidden></div>
|
|
139
|
+
<div id="tab-update" class="pane" hidden></div>
|
|
138
140
|
<div id="tab-cron" class="pane" hidden>
|
|
139
141
|
<h2 data-i18n="cron_h2">Cron Görevler</h2>
|
|
140
142
|
<div class="sub" data-i18n="cron_sub">Zamanlanmış görevler — saati gelince agent otomatik çalışır</div>
|
package/src/renderer/renderer.js
CHANGED
|
@@ -508,6 +508,7 @@ async function renderActiveSettingsTab() {
|
|
|
508
508
|
case 'dash': await renderDashboardPane(); break;
|
|
509
509
|
case 'limits': await renderLimitsPane(); break;
|
|
510
510
|
case 'sec': await renderSecurityPane(); break;
|
|
511
|
+
case 'update': await renderUpdatePane(); break;
|
|
511
512
|
}
|
|
512
513
|
}
|
|
513
514
|
|
|
@@ -516,7 +517,7 @@ function switchTab(name) {
|
|
|
516
517
|
document.querySelectorAll('#setTabs .tab').forEach((b) =>
|
|
517
518
|
b.classList.toggle('active', b.dataset.tab === name)
|
|
518
519
|
);
|
|
519
|
-
for (const p of ['provider', 'fallout', 'memory', 'skills', 'agents', 'tts', 'email', 'integrations', 'websearch', 'events', 'cron', 'usage', 'logs', 'dash', 'limits', 'sec']) {
|
|
520
|
+
for (const p of ['provider', 'fallout', 'memory', 'skills', 'agents', 'tts', 'email', 'integrations', 'websearch', 'events', 'cron', 'usage', 'logs', 'dash', 'limits', 'sec', 'update']) {
|
|
520
521
|
$('#tab-' + p).hidden = p !== name;
|
|
521
522
|
}
|
|
522
523
|
if (name === 'cron') openCron();
|
|
@@ -526,6 +527,7 @@ function switchTab(name) {
|
|
|
526
527
|
if (name === 'dash') renderDashboardPane();
|
|
527
528
|
if (name === 'limits') renderLimitsPane();
|
|
528
529
|
if (name === 'sec') renderSecurityPane();
|
|
530
|
+
if (name === 'update') renderUpdatePane();
|
|
529
531
|
if (name === 'agents') refreshAgentsPane();
|
|
530
532
|
if (name === 'websearch') renderWebSearchPane();
|
|
531
533
|
/* Fallout: her açılışta güncel provider zincirini çek */
|
|
@@ -1542,8 +1544,84 @@ async function renderSecurityPane() {
|
|
|
1542
1544
|
}
|
|
1543
1545
|
}
|
|
1544
1546
|
|
|
1545
|
-
/*
|
|
1546
|
-
|
|
1547
|
+
/* ---------------- Update: sürüm kontrol + otomatik güncelleme ---------------- */
|
|
1548
|
+
|
|
1549
|
+
let updatePaneTimer = null;
|
|
1550
|
+
|
|
1551
|
+
function renderUpdateStateHtml(st) {
|
|
1552
|
+
let status;
|
|
1553
|
+
if (st.npm) status = _t('up_npm_mode');
|
|
1554
|
+
else if (st.error) status = '⚠ ' + st.error;
|
|
1555
|
+
else if (st.downloaded) status = _t('up_downloaded') + ' (v' + (st.version || '?') + ')';
|
|
1556
|
+
else if (st.progress) status = _t('up_downloading') + ' %' + st.progress.percent;
|
|
1557
|
+
else if (st.checking) status = _t('up_checking');
|
|
1558
|
+
else if (st.available) status = _t('up_available') + ' (v' + (st.version || '?') + ')';
|
|
1559
|
+
else if (st.available === false) status = _t('up_uptodate');
|
|
1560
|
+
else status = '—';
|
|
1561
|
+
|
|
1562
|
+
return `<div class="usage-stat" style="margin-top:10px"><div class="us-label">${_t('up_status')}</div><div class="us-value" style="font-size:14px">${escapeHtml(status)}</div></div>`;
|
|
1563
|
+
}
|
|
1564
|
+
|
|
1565
|
+
async function renderUpdatePane() {
|
|
1566
|
+
const pane = $('#tab-update');
|
|
1567
|
+
if (!pane) return;
|
|
1568
|
+
const st = await beast.updateStatus().catch(() => null);
|
|
1569
|
+
if (!st) return;
|
|
1570
|
+
clearInterval(updatePaneTimer);
|
|
1571
|
+
|
|
1572
|
+
pane.innerHTML =
|
|
1573
|
+
'<h2>' + _t('up_h2') + '</h2>' +
|
|
1574
|
+
'<div class="sub">' + _t('up_sub') + '</div>' +
|
|
1575
|
+
'<div class="usage-cards">' +
|
|
1576
|
+
`<div class="usage-stat"><div class="us-label">${_t('up_current')}</div><div class="us-value">v${escapeHtml(st.current)}</div></div>` +
|
|
1577
|
+
`<div class="usage-stat"><div class="us-label">${_t('up_latest')}</div><div class="us-value">${st.version ? 'v' + escapeHtml(st.version) : '—'}</div></div>` +
|
|
1578
|
+
'</div>' +
|
|
1579
|
+
renderUpdateStateHtml(st) +
|
|
1580
|
+
`<div class="fo-toggles" style="margin-top:12px">
|
|
1581
|
+
<label class="lock-row"><input type="checkbox" id="upAutoCheck" ${st.autoCheck ? 'checked' : ''}/><span>${_t('up_auto_check')}</span></label>
|
|
1582
|
+
<label class="lock-row"><input type="checkbox" id="upAutoDl" ${st.autoDownload ? 'checked' : ''}/><span>${_t('up_auto_dl')}</span></label>
|
|
1583
|
+
</div>` +
|
|
1584
|
+
(st.npm
|
|
1585
|
+
? `<div class="codeblock npm" style="margin-top:10px"><pre># uygulamayı kapat, sonra:
|
|
1586
|
+
beast-agent update</pre></div>`
|
|
1587
|
+
: `<div class="form-grid" style="grid-template-columns:auto auto;gap:8px;margin-top:12px">
|
|
1588
|
+
<button id="upCheck" class="btn ghost">${_t('up_check_now')}</button>
|
|
1589
|
+
<button id="upInstall" class="btn ghost" ${st.downloaded ? '' : 'disabled style="opacity:.45;cursor:default"'}>${_t('up_install_now')}</button>
|
|
1590
|
+
</div>
|
|
1591
|
+
<div class="sub" style="margin-top:8px">${_t('up_note')}</div>`);
|
|
1592
|
+
|
|
1593
|
+
const chk = pane.querySelector('#upAutoCheck');
|
|
1594
|
+
if (chk) chk.addEventListener('change', async (e) => { await beast.updateSetAuto({ autoCheck: e.target.checked }); });
|
|
1595
|
+
const dl = pane.querySelector('#upAutoDl');
|
|
1596
|
+
if (dl) dl.addEventListener('change', async (e) => { await beast.updateSetAuto({ autoDownload: e.target.checked }); });
|
|
1597
|
+
|
|
1598
|
+
const btnCheck = pane.querySelector('#upCheck');
|
|
1599
|
+
if (btnCheck) btnCheck.addEventListener('click', async () => {
|
|
1600
|
+
btnCheck.disabled = true;
|
|
1601
|
+
const r = await beast.updateCheck().catch(() => ({ ok: false, error: 'ipc' }));
|
|
1602
|
+
if (!r.ok && r.error) toast(r.error);
|
|
1603
|
+
});
|
|
1604
|
+
const btnInstall = pane.querySelector('#upInstall');
|
|
1605
|
+
if (btnInstall) btnInstall.addEventListener('click', async () => {
|
|
1606
|
+
const r = await beast.updateInstall().catch(() => ({ ok: false, error: 'ipc' }));
|
|
1607
|
+
if (!r.ok && r.error) toast(r.error);
|
|
1608
|
+
});
|
|
1609
|
+
|
|
1610
|
+
/* indirme ilerlemesi için sekme açıkken canlı tazele */
|
|
1611
|
+
updatePaneTimer = setInterval(() => {
|
|
1612
|
+
if ($('#tab-update').hidden || els.settingsOverlay.hidden) { clearInterval(updatePaneTimer); return; }
|
|
1613
|
+
beast.updateStatus().then((s) => {
|
|
1614
|
+
if (!s) return;
|
|
1615
|
+
const box = pane.querySelector('.us-value');
|
|
1616
|
+
if (box) {
|
|
1617
|
+
const wrap = pane.querySelector('.usage-stat');
|
|
1618
|
+
if (wrap) wrap.outerHTML = renderUpdateStateHtml(s);
|
|
1619
|
+
}
|
|
1620
|
+
}).catch(() => {});
|
|
1621
|
+
}, 1000);
|
|
1622
|
+
}
|
|
1623
|
+
|
|
1624
|
+
/* Sohbete onay kartı düşürür — Onayla / Her zaman / Reddet */function showApprovalCard(ev) {
|
|
1547
1625
|
streamEl = null;
|
|
1548
1626
|
const card = document.createElement('div');
|
|
1549
1627
|
card.className = 'tool-card';
|
|
@@ -2114,6 +2192,11 @@ function onEvent(ev) {
|
|
|
2114
2192
|
showApprovalCard(ev);
|
|
2115
2193
|
return;
|
|
2116
2194
|
}
|
|
2195
|
+
if (ev.type === 'update') {
|
|
2196
|
+
if (ev.downloaded) toast(_t('up_downloaded') + ' (v' + (ev.version || '?') + ') — /update now');
|
|
2197
|
+
if (!els.settingsOverlay.hidden && setTab === 'update') renderUpdatePane();
|
|
2198
|
+
return;
|
|
2199
|
+
}
|
|
2117
2200
|
if (ev.type === 'agents') {
|
|
2118
2201
|
agentState.jobs = ev.jobs || [];
|
|
2119
2202
|
updateAgentIds();
|