beast-agent 2.44.0 → 2.44.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/README.md +2 -2
- package/bin/beast-agent.js +137 -137
- package/package.json +1 -1
- package/scripts/release.js +137 -137
- package/src/agent/engine.js +8 -2
- package/src/agent/searxng.js +37 -37
- package/src/main.js +23 -1
- package/src/preload.js +1 -0
- package/src/renderer/renderer.js +47 -34
package/README.md
CHANGED
|
@@ -24,8 +24,8 @@ Beast Agent is a personal AI agent that runs on your machine and connects to **a
|
|
|
24
24
|
- **Slash commands** — `/new`, `/open`, `/change`, `/think`, `/rule`, `/allow`, `/block`, `/backup`, `/approve` and more
|
|
25
25
|
- **Event center** — IMAP IDLE email watching, file-change watching, price feed (Binance), webhook inputs
|
|
26
26
|
- **Cron + watchers** — scheduled tasks, file/web/battery watchers
|
|
27
|
-
- **MCP
|
|
28
|
-
- **Skills** — `%APPDATA%\beast\skills\<
|
|
27
|
+
- **MCP support** — connect Model Context Protocol servers (filesystem, git, fetch, memory + thousands of community servers); tools are exposed to the model as `mcp__server__tool` and managed from Settings → MCP
|
|
28
|
+
- **Skills** — teach your agent new abilities with `%APPDATA%\beast\skills\<name>\SKILL.md`; the Superpowers methodology pack (planning, TDD, parallel-agent discipline, verification) ships built-in
|
|
29
29
|
- **Web search chain** — TinyFish (free, used first if a key is set) → built-in browser (direct Google) → python multi-engine (DDG/Bing/Mojeek) → Exa
|
|
30
30
|
- **Approval gate** — optional confirmation for risky actions (commands / file deletion-modification): default OFF (everything free), when ON the agent asks (`/approve`, `/approve always`, `/deny`)
|
|
31
31
|
- **Provider-based limits** — per-provider max input token limit with context compression
|
package/bin/beast-agent.js
CHANGED
|
@@ -1,137 +1,137 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
'use strict';
|
|
3
|
-
|
|
4
|
-
/* Beast Agent global npm
|
|
5
|
-
`beast` / `beast-agent` →
|
|
6
|
-
`beast update` →
|
|
7
|
-
|
|
8
|
-
const { spawn, spawnSync } = require('child_process');
|
|
9
|
-
const path = require('path');
|
|
10
|
-
const fs = require('fs');
|
|
11
|
-
const os = require('os');
|
|
12
|
-
|
|
13
|
-
/*
|
|
14
|
-
(%APPDATA%\beast: config.yaml, .env,
|
|
15
|
-
if (process.argv[2] === 'uninstall') {
|
|
16
|
-
const isWin = process.platform === 'win32';
|
|
17
|
-
console.log('Beast Agent
|
|
18
|
-
if (isWin) {
|
|
19
|
-
/*
|
|
20
|
-
try {
|
|
21
|
-
spawnSync('powershell.exe', ['-NoProfile', '-Command',
|
|
22
|
-
"Get-Process electron -ErrorAction SilentlyContinue | Where-Object { $_.Path -like '*node_modules*beast-agent*' } | Stop-Process -Force"],
|
|
23
|
-
{ stdio: 'ignore' });
|
|
24
|
-
console.log('\u2022
|
|
25
|
-
} catch {}
|
|
26
|
-
/* startup
|
|
27
|
-
try {
|
|
28
|
-
spawnSync('powershell.exe', ['-NoProfile', '-Command',
|
|
29
|
-
"$k='HKCU:\\Software\\Microsoft\\Windows\\CurrentVersion\\Run';" +
|
|
30
|
-
"foreach($n in (Get-Item $k -ErrorAction SilentlyContinue).GetValueNames()){" +
|
|
31
|
-
"$v=(Get-ItemProperty $k).$n; if($v -like '*node_modules*beast-agent*'){ Remove-ItemProperty -Path $k -Name $n; Write-Host '
|
|
32
|
-
{ stdio: 'inherit' });
|
|
33
|
-
} catch {}
|
|
34
|
-
/*
|
|
35
|
-
try {
|
|
36
|
-
spawnSync('powershell.exe', ['-NoProfile', '-Command',
|
|
37
|
-
"$d=[Environment]::GetFolderPath('Desktop'); if(Test-Path \"$d\\Beast Agent.lnk\"){ Remove-Item \"$d\\Beast Agent.lnk\" -Force; Write-Host '\u2022
|
|
38
|
-
{ stdio: 'inherit' });
|
|
39
|
-
} catch {}
|
|
40
|
-
} else {
|
|
41
|
-
try { spawnSync('pkill', ['-f', 'node_modules/beast-agent'], { stdio: 'ignore' }); } catch {}
|
|
42
|
-
}
|
|
43
|
-
console.log('\u2022 npm
|
|
44
|
-
const ur = spawnSync('npm', ['uninstall', '-g', 'beast-agent'], { stdio: 'inherit', shell: isWin });
|
|
45
|
-
console.log('\n\u2713 Beast Agent
|
|
46
|
-
console.log('\u2139
|
|
47
|
-
console.log(' (config.yaml, .env,
|
|
48
|
-
console.log('
|
|
49
|
-
process.exit(ur.status || 0);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/*
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
if (process.argv[2] === 'update') {
|
|
56
|
-
const isWin = process.platform === 'win32';
|
|
57
|
-
console.log('\u27F3 beast-agent
|
|
58
|
-
if (isWin) {
|
|
59
|
-
try {
|
|
60
|
-
spawnSync('powershell.exe', ['-NoProfile', '-Command',
|
|
61
|
-
"Get-Process electron -ErrorAction SilentlyContinue | Where-Object { $_.Path -like '*node_modules*beast-agent*' } | Stop-Process -Force"],
|
|
62
|
-
{ stdio: 'ignore' });
|
|
63
|
-
console.log('\u2022
|
|
64
|
-
} catch {}
|
|
65
|
-
} else {
|
|
66
|
-
try { spawnSync('pkill', ['-f', 'node_modules/beast-agent'], { stdio: 'ignore' }); } catch {}
|
|
67
|
-
}
|
|
68
|
-
/*
|
|
69
|
-
let ok = false;
|
|
70
|
-
for (let i = 1; i <= 5 && !ok; i++) {
|
|
71
|
-
const r = spawnSync('npm', ['install', '-g', 'beast-agent@latest'], { stdio: 'inherit', shell: isWin });
|
|
72
|
-
ok = r.status === 0;
|
|
73
|
-
if (!ok && i < 5) {
|
|
74
|
-
console.log(` \u2022
|
|
75
|
-
if (isWin) spawnSync('powershell.exe', ['-NoProfile', '-Command', 'Start-Sleep -Seconds 3'], { stdio: 'ignore' });
|
|
76
|
-
else spawnSync('sleep', ['3']);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
if (!ok) {
|
|
80
|
-
console.log('\n\u2717
|
|
81
|
-
if (isWin) spawnSync('cmd.exe', ['/c', 'pause'], { stdio: 'inherit', shell: false });
|
|
82
|
-
process.exit(1);
|
|
83
|
-
}
|
|
84
|
-
console.log('\n\u2713 beast-agent
|
|
85
|
-
launchDetached([]);
|
|
86
|
-
if (isWin) spawnSync('cmd.exe', ['/c', 'timeout /t 3'], { stdio: 'ignore' });
|
|
87
|
-
process.exit(0);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/* SearXNG:
|
|
91
|
-
beast searxng →
|
|
92
|
-
beast searxng status →
|
|
93
|
-
beast searxng stop →
|
|
94
|
-
if (process.argv[2] === 'searxng') {
|
|
95
|
-
require('../src/agent/searxng').cli(process.argv.slice(3)).catch((e) => {
|
|
96
|
-
console.error('\u2717 ' + String((e && e.message) || e));
|
|
97
|
-
process.exit(1);
|
|
98
|
-
});
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/* detached
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
function launchDetached(extraArgs) {
|
|
106
|
-
let electron = null;
|
|
107
|
-
try { electron = require('electron'); } catch {}
|
|
108
|
-
if (typeof electron !== 'string') {
|
|
109
|
-
let fix = null;
|
|
110
|
-
try { fix = require('../scripts/fix-electron'); } catch {}
|
|
111
|
-
if (fix) {
|
|
112
|
-
console.log('\u27F3 electron
|
|
113
|
-
const r = fix.repair({ quiet: false });
|
|
114
|
-
if (r.ok && !r.skipped) console.log('\u2713 electron
|
|
115
|
-
if (r.ok) {
|
|
116
|
-
try { electron = require('electron'); } catch {}
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
if (typeof electron !== 'string') {
|
|
121
|
-
console.log('\n\u2717 Electron
|
|
122
|
-
console.log('
|
|
123
|
-
console.log(' npm config set ignore-scripts false');
|
|
124
|
-
console.log(' npm install -g beast-agent');
|
|
125
|
-
return false;
|
|
126
|
-
}
|
|
127
|
-
const child = spawn(electron, [path.resolve(__dirname, '..'), ...extraArgs], {
|
|
128
|
-
stdio: 'ignore',
|
|
129
|
-
detached: true,
|
|
130
|
-
/* windowsHide
|
|
131
|
-
});
|
|
132
|
-
child.unref();
|
|
133
|
-
return true;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
if (!launchDetached(process.argv.slice(2))) process.exit(1);
|
|
137
|
-
process.exit(0);
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/* Beast Agent global npm launcher (`beast` short alias points to the same script):
|
|
5
|
+
`beast` / `beast-agent` → launches the app detached, frees the terminal immediately
|
|
6
|
+
`beast update` → installs the latest version from npm (run while the app is closed) */
|
|
7
|
+
|
|
8
|
+
const { spawn, spawnSync } = require('child_process');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
const os = require('os');
|
|
12
|
+
|
|
13
|
+
/* uninstall mode: removes the app but KEEPS YOUR PERSONAL DATA
|
|
14
|
+
(%APPDATA%\beast: config.yaml, .env, sessions, memory, WhatsApp pairing, backups) */
|
|
15
|
+
if (process.argv[2] === 'uninstall') {
|
|
16
|
+
const isWin = process.platform === 'win32';
|
|
17
|
+
console.log('Uninstalling Beast Agent\u2026');
|
|
18
|
+
if (isWin) {
|
|
19
|
+
/* stop running instances */
|
|
20
|
+
try {
|
|
21
|
+
spawnSync('powershell.exe', ['-NoProfile', '-Command',
|
|
22
|
+
"Get-Process electron -ErrorAction SilentlyContinue | Where-Object { $_.Path -like '*node_modules*beast-agent*' } | Stop-Process -Force"],
|
|
23
|
+
{ stdio: 'ignore' });
|
|
24
|
+
console.log('\u2022 stopped running Beast instances (if any)');
|
|
25
|
+
} catch {}
|
|
26
|
+
/* remove the startup entry — only if it points to beast-agent */
|
|
27
|
+
try {
|
|
28
|
+
spawnSync('powershell.exe', ['-NoProfile', '-Command',
|
|
29
|
+
"$k='HKCU:\\Software\\Microsoft\\Windows\\CurrentVersion\\Run';" +
|
|
30
|
+
"foreach($n in (Get-Item $k -ErrorAction SilentlyContinue).GetValueNames()){" +
|
|
31
|
+
"$v=(Get-ItemProperty $k).$n; if($v -like '*node_modules*beast-agent*'){ Remove-ItemProperty -Path $k -Name $n; Write-Host '\u2022 startup entry removed' } }"],
|
|
32
|
+
{ stdio: 'inherit' });
|
|
33
|
+
} catch {}
|
|
34
|
+
/* remove the desktop shortcut (real Desktop path: may be under OneDrive) */
|
|
35
|
+
try {
|
|
36
|
+
spawnSync('powershell.exe', ['-NoProfile', '-Command',
|
|
37
|
+
"$d=[Environment]::GetFolderPath('Desktop'); if(Test-Path \"$d\\Beast Agent.lnk\"){ Remove-Item \"$d\\Beast Agent.lnk\" -Force; Write-Host '\u2022 desktop shortcut removed' }"],
|
|
38
|
+
{ stdio: 'inherit' });
|
|
39
|
+
} catch {}
|
|
40
|
+
} else {
|
|
41
|
+
try { spawnSync('pkill', ['-f', 'node_modules/beast-agent'], { stdio: 'ignore' }); } catch {}
|
|
42
|
+
}
|
|
43
|
+
console.log('\u2022 removing the npm package\u2026');
|
|
44
|
+
const ur = spawnSync('npm', ['uninstall', '-g', 'beast-agent'], { stdio: 'inherit', shell: isWin });
|
|
45
|
+
console.log('\n\u2713 Beast Agent uninstalled.');
|
|
46
|
+
console.log('\u2139 Your personal data is preserved \u2014 %APPDATA%\\beast');
|
|
47
|
+
console.log(' (config.yaml, .env, sessions, memory, WhatsApp pairing, encrypted backups)');
|
|
48
|
+
console.log(' To reinstall: npm install -g beast-agent');
|
|
49
|
+
process.exit(ur.status || 0);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/* update mode: stop the running Beast (avoid file-lock EBUSY) → update npm (visible progress) → relaunch.
|
|
53
|
+
This command is run by the update button inside a VISIBLE cmd window;
|
|
54
|
+
it also works from a terminal — npm install output streams on screen. */
|
|
55
|
+
if (process.argv[2] === 'update') {
|
|
56
|
+
const isWin = process.platform === 'win32';
|
|
57
|
+
console.log('\u27F3 Updating beast-agent\u2026');
|
|
58
|
+
if (isWin) {
|
|
59
|
+
try {
|
|
60
|
+
spawnSync('powershell.exe', ['-NoProfile', '-Command',
|
|
61
|
+
"Get-Process electron -ErrorAction SilentlyContinue | Where-Object { $_.Path -like '*node_modules*beast-agent*' } | Stop-Process -Force"],
|
|
62
|
+
{ stdio: 'ignore' });
|
|
63
|
+
console.log('\u2022 stopped running Beast instances (if any)');
|
|
64
|
+
} catch {}
|
|
65
|
+
} else {
|
|
66
|
+
try { spawnSync('pkill', ['-f', 'node_modules/beast-agent'], { stdio: 'ignore' }); } catch {}
|
|
67
|
+
}
|
|
68
|
+
/* file locks (EBUSY) sometimes fail on the first attempt — 5 tries */
|
|
69
|
+
let ok = false;
|
|
70
|
+
for (let i = 1; i <= 5 && !ok; i++) {
|
|
71
|
+
const r = spawnSync('npm', ['install', '-g', 'beast-agent@latest'], { stdio: 'inherit', shell: isWin });
|
|
72
|
+
ok = r.status === 0;
|
|
73
|
+
if (!ok && i < 5) {
|
|
74
|
+
console.log(` \u2022 attempt ${i}/5 failed (possible file lock) \u2014 retrying in 3s\u2026`);
|
|
75
|
+
if (isWin) spawnSync('powershell.exe', ['-NoProfile', '-Command', 'Start-Sleep -Seconds 3'], { stdio: 'ignore' });
|
|
76
|
+
else spawnSync('sleep', ['3']);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (!ok) {
|
|
80
|
+
console.log('\n\u2717 update failed \u2014 manually: npm install -g beast-agent@latest');
|
|
81
|
+
if (isWin) spawnSync('cmd.exe', ['/c', 'pause'], { stdio: 'inherit', shell: false });
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
console.log('\n\u2713 beast-agent updated \u2014 launching the app\u2026');
|
|
85
|
+
launchDetached([]);
|
|
86
|
+
if (isWin) spawnSync('cmd.exe', ['/c', 'timeout /t 3'], { stdio: 'ignore' });
|
|
87
|
+
process.exit(0);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/* SearXNG: local search engine — install + start in the background (127.0.0.1:8888)
|
|
91
|
+
beast searxng → install if needed, then start
|
|
92
|
+
beast searxng status → status
|
|
93
|
+
beast searxng stop → stop */
|
|
94
|
+
if (process.argv[2] === 'searxng') {
|
|
95
|
+
require('../src/agent/searxng').cli(process.argv.slice(3)).catch((e) => {
|
|
96
|
+
console.error('\u2717 ' + String((e && e.message) || e));
|
|
97
|
+
process.exit(1);
|
|
98
|
+
});
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/* detached launcher + self-healing electron:
|
|
103
|
+
on fresh machines the electron binary download during npm postinstall
|
|
104
|
+
may have silently failed → it is repaired automatically here. */
|
|
105
|
+
function launchDetached(extraArgs) {
|
|
106
|
+
let electron = null;
|
|
107
|
+
try { electron = require('electron'); } catch {}
|
|
108
|
+
if (typeof electron !== 'string') {
|
|
109
|
+
let fix = null;
|
|
110
|
+
try { fix = require('../scripts/fix-electron'); } catch {}
|
|
111
|
+
if (fix) {
|
|
112
|
+
console.log('\u27F3 electron runtime files are missing \u2014 repairing automatically\u2026');
|
|
113
|
+
const r = fix.repair({ quiet: false });
|
|
114
|
+
if (r.ok && !r.skipped) console.log('\u2713 electron repaired');
|
|
115
|
+
if (r.ok) {
|
|
116
|
+
try { electron = require('electron'); } catch {}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
if (typeof electron !== 'string') {
|
|
121
|
+
console.log('\n\u2717 Electron runtime could not be installed.');
|
|
122
|
+
console.log(' Manual fix \u2014 run these 2 commands:');
|
|
123
|
+
console.log(' npm config set ignore-scripts false');
|
|
124
|
+
console.log(' npm install -g beast-agent');
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
const child = spawn(electron, [path.resolve(__dirname, '..'), ...extraArgs], {
|
|
128
|
+
stdio: 'ignore',
|
|
129
|
+
detached: true,
|
|
130
|
+
/* do NOT use windowsHide: Chromium starts the first window hidden (tray-only bug) */
|
|
131
|
+
});
|
|
132
|
+
child.unref();
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (!launchDetached(process.argv.slice(2))) process.exit(1);
|
|
137
|
+
process.exit(0);
|
package/package.json
CHANGED
package/scripts/release.js
CHANGED
|
@@ -1,137 +1,137 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/* Beast Agent —
|
|
4
|
-
|
|
5
|
-
npm run release →
|
|
6
|
-
npm run release -- minor → 0.21.0
|
|
7
|
-
npm run release -- 0.22.0 →
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
1) package.json
|
|
11
|
-
2) commit + tag + push (main + tag)
|
|
12
|
-
3) GitHub Release (
|
|
13
|
-
4) npm publish (NPM_TOKEN env var
|
|
14
|
-
5)
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
const fs = require('fs');
|
|
18
|
-
const path = require('path');
|
|
19
|
-
const { execSync } = require('child_process');
|
|
20
|
-
|
|
21
|
-
const ROOT = path.join(__dirname, '..');
|
|
22
|
-
const ONE_DRIVE_DIR = process.env.BEAST_BACKUP_DIR
|
|
23
|
-
|| 'C:\\Users\\batuh\\OneDrive\\Masaüstü\\Beast Agent';
|
|
24
|
-
|
|
25
|
-
const step = (msg) => console.log('\n\x1b[1m▶ ' + msg + '\x1b[0m');
|
|
26
|
-
const ok = (msg) => console.log(' \x1b[32m✓\x1b[0m ' + msg);
|
|
27
|
-
const warn = (msg) => console.log(' \x1b[33m!\x1b[0m ' + msg);
|
|
28
|
-
const fail = (msg) => { console.error(' \x1b[31m✗ ' + msg + '\x1b[0m'); process.exit(1); };
|
|
29
|
-
|
|
30
|
-
function run(cmd, opts = {}) {
|
|
31
|
-
/* stdio:'inherit'
|
|
32
|
-
return String(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
|
-
/* ----------
|
|
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('
|
|
49
|
-
const tag = 'v' + next;
|
|
50
|
-
|
|
51
|
-
console.log(`\x1b[1mBeast Agent release: ${current} → ${next}\x1b[0m`);
|
|
52
|
-
|
|
53
|
-
/* ---------- 1)
|
|
54
|
-
step('1/5
|
|
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/5 git: commit + tag + push');
|
|
61
|
-
try {
|
|
62
|
-
run('git add -A');
|
|
63
|
-
run(`git commit -m "v${next}"`);
|
|
64
|
-
} catch { warn('
|
|
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) GitHub Release (
|
|
71
|
-
step('3/5 GitHub Release ' + tag);
|
|
72
|
-
const gh = process.env.GH || 'gh';
|
|
73
|
-
let ghOk = false;
|
|
74
|
-
try {
|
|
75
|
-
const notes = [
|
|
76
|
-
`## Beast Agent ${tag}`,
|
|
77
|
-
'',
|
|
78
|
-
'-
|
|
79
|
-
'',
|
|
80
|
-
'
|
|
81
|
-
'
|
|
82
|
-
].join('\n');
|
|
83
|
-
const notesFile = path.join(ROOT, 'dist', 'release-notes.md');
|
|
84
|
-
fs.mkdirSync(path.dirname(notesFile), { recursive: true });
|
|
85
|
-
fs.writeFileSync(notesFile, notes);
|
|
86
|
-
try { run(`${gh} release delete ${tag} --yes --cleanup-tag`); } catch {}
|
|
87
|
-
run(`${gh} release create ${tag} --title "Beast Agent ${tag}" --notes-file "${notesFile}"`, { inherit: true });
|
|
88
|
-
ok('release
|
|
89
|
-
ghOk = true;
|
|
90
|
-
} catch (e) {
|
|
91
|
-
/* gh
|
|
92
|
-
warn('GitHub release
|
|
93
|
-
}
|
|
94
|
-
if (!ghOk) {
|
|
95
|
-
warn('GitHub Release
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/* ---------- 4) npm publish ---------- */
|
|
99
|
-
step('4/5 npm publish');
|
|
100
|
-
if (process.env.NPM_TOKEN) {
|
|
101
|
-
try {
|
|
102
|
-
run(`npm publish --//registry.npmjs.org/:_authToken=${process.env.NPM_TOKEN}`, { inherit: true });
|
|
103
|
-
ok('npm: beast-agent@' + next);
|
|
104
|
-
} catch (e) {
|
|
105
|
-
warn('npm publish
|
|
106
|
-
}
|
|
107
|
-
} else {
|
|
108
|
-
warn('NPM_TOKEN env
|
|
109
|
-
warn('
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/* ---------- 5) OneDrive
|
|
113
|
-
step('5/5 OneDrive
|
|
114
|
-
try {
|
|
115
|
-
const dest = path.join(ONE_DRIVE_DIR, 'beast-v' + next);
|
|
116
|
-
fs.mkdirSync(dest, { recursive: true });
|
|
117
|
-
/* robocopy 0-7
|
|
118
|
-
execSync 0
|
|
119
|
-
try {
|
|
120
|
-
execSync(
|
|
121
|
-
`robocopy "${ROOT}" "${dest}" /E /XD node_modules dist "beast agent web" .git /NFL /NDL /NJH`,
|
|
122
|
-
{ cwd: ROOT, stdio: 'pipe' }
|
|
123
|
-
);
|
|
124
|
-
} catch (e) {
|
|
125
|
-
const code = e && e.status;
|
|
126
|
-
if (!(typeof code === 'number' && code < 8)) throw e;
|
|
127
|
-
}
|
|
128
|
-
const info = path.join(dest, `
|
|
129
|
-
fs.writeFileSync(info, `BEAST AGENT v${next} — ${new Date().toLocaleString('
|
|
130
|
-
ok(dest);
|
|
131
|
-
} catch (e) {
|
|
132
|
-
warn('OneDrive
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
console.log(`\n\x1b[1m\x1b[32m✓ v${next}
|
|
136
|
-
console.log(' GitHub : https://github.com/algokodcom/beast-agent/releases/tag/' + tag);
|
|
137
|
-
console.log(' npm : https://www.npmjs.com/package/beast-agent');
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/* Beast Agent — Release / Sync System
|
|
4
|
+
Full release flow with a single command (NO EXE/INSTALLER — npm + source distribution):
|
|
5
|
+
npm run release → automatic patch bump (0.20.0 → 0.20.1)
|
|
6
|
+
npm run release -- minor → 0.21.0
|
|
7
|
+
npm run release -- 0.22.0 → specific version
|
|
8
|
+
|
|
9
|
+
Steps:
|
|
10
|
+
1) bump the package.json version
|
|
11
|
+
2) commit + tag + push (main + tag)
|
|
12
|
+
3) GitHub Release (source only — no exe/asset upload)
|
|
13
|
+
4) npm publish (only if NPM_TOKEN env var is set; otherwise skipped with a warning)
|
|
14
|
+
5) source copy into the OneDrive backup folder (beast-v< version >)
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const fs = require('fs');
|
|
18
|
+
const path = require('path');
|
|
19
|
+
const { execSync } = require('child_process');
|
|
20
|
+
|
|
21
|
+
const ROOT = path.join(__dirname, '..');
|
|
22
|
+
const ONE_DRIVE_DIR = process.env.BEAST_BACKUP_DIR
|
|
23
|
+
|| 'C:\\Users\\batuh\\OneDrive\\Masaüstü\\Beast Agent';
|
|
24
|
+
|
|
25
|
+
const step = (msg) => console.log('\n\x1b[1m▶ ' + msg + '\x1b[0m');
|
|
26
|
+
const ok = (msg) => console.log(' \x1b[32m✓\x1b[0m ' + msg);
|
|
27
|
+
const warn = (msg) => console.log(' \x1b[33m!\x1b[0m ' + msg);
|
|
28
|
+
const fail = (msg) => { console.error(' \x1b[31m✗ ' + msg + '\x1b[0m'); process.exit(1); };
|
|
29
|
+
|
|
30
|
+
function run(cmd, opts = {}) {
|
|
31
|
+
/* with stdio:'inherit' execSync returns null — avoid crashing on trim */
|
|
32
|
+
return String(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
|
+
/* ---------- arguments ---------- */
|
|
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('invalid version: ' + next);
|
|
49
|
+
const tag = 'v' + next;
|
|
50
|
+
|
|
51
|
+
console.log(`\x1b[1mBeast Agent release: ${current} → ${next}\x1b[0m`);
|
|
52
|
+
|
|
53
|
+
/* ---------- 1) version bump ---------- */
|
|
54
|
+
step('1/5 bump version: ' + 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/5 git: commit + tag + push');
|
|
61
|
+
try {
|
|
62
|
+
run('git add -A');
|
|
63
|
+
run(`git commit -m "v${next}"`);
|
|
64
|
+
} catch { warn('there were no changes to commit'); }
|
|
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) GitHub Release (source — no exe) ---------- */
|
|
71
|
+
step('3/5 GitHub Release ' + tag);
|
|
72
|
+
const gh = process.env.GH || 'gh';
|
|
73
|
+
let ghOk = false;
|
|
74
|
+
try {
|
|
75
|
+
const notes = [
|
|
76
|
+
`## Beast Agent ${tag}`,
|
|
77
|
+
'',
|
|
78
|
+
'- `npm i -g beast-agent@' + next + '`',
|
|
79
|
+
'',
|
|
80
|
+
'Distribution is done via npm — the exe/installer release has been removed.',
|
|
81
|
+
'Full changelog: commit history.',
|
|
82
|
+
].join('\n');
|
|
83
|
+
const notesFile = path.join(ROOT, 'dist', 'release-notes.md');
|
|
84
|
+
fs.mkdirSync(path.dirname(notesFile), { recursive: true });
|
|
85
|
+
fs.writeFileSync(notesFile, notes);
|
|
86
|
+
try { run(`${gh} release delete ${tag} --yes --cleanup-tag`); } catch {}
|
|
87
|
+
run(`${gh} release create ${tag} --title "Beast Agent ${tag}" --notes-file "${notesFile}"`, { inherit: true });
|
|
88
|
+
ok('release published: https://github.com/algokodcom/beast-agent/releases/tag/' + tag);
|
|
89
|
+
ghOk = true;
|
|
90
|
+
} catch (e) {
|
|
91
|
+
/* gh missing / not logged in → skip the release, continue with npm */
|
|
92
|
+
warn('GitHub release skipped (gh missing or error): ' + String(e).slice(0, 160));
|
|
93
|
+
}
|
|
94
|
+
if (!ghOk) {
|
|
95
|
+
warn('GitHub Release can be created manually: https://github.com/algokodcom/beast-agent/releases/new?tag=' + tag);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/* ---------- 4) npm publish ---------- */
|
|
99
|
+
step('4/5 npm publish');
|
|
100
|
+
if (process.env.NPM_TOKEN) {
|
|
101
|
+
try {
|
|
102
|
+
run(`npm publish --//registry.npmjs.org/:_authToken=${process.env.NPM_TOKEN}`, { inherit: true });
|
|
103
|
+
ok('npm: beast-agent@' + next);
|
|
104
|
+
} catch (e) {
|
|
105
|
+
warn('npm publish failed (the version may already exist): ' + String(e).slice(0, 200));
|
|
106
|
+
}
|
|
107
|
+
} else {
|
|
108
|
+
warn('NPM_TOKEN env is missing — npm step skipped.');
|
|
109
|
+
warn('Manually: set NPM_TOKEN=<token> && npm publish');
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/* ---------- 5) OneDrive source backup ---------- */
|
|
113
|
+
step('5/5 OneDrive backup (beast-v' + next + ')');
|
|
114
|
+
try {
|
|
115
|
+
const dest = path.join(ONE_DRIVE_DIR, 'beast-v' + next);
|
|
116
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
117
|
+
/* every robocopy exit code 0-7 is a success (1 = files copied);
|
|
118
|
+
execSync treats anything other than 0 as an error — swallow the code here, stop only on real failure (>7) */
|
|
119
|
+
try {
|
|
120
|
+
execSync(
|
|
121
|
+
`robocopy "${ROOT}" "${dest}" /E /XD node_modules dist "beast agent web" .git /NFL /NDL /NJH`,
|
|
122
|
+
{ cwd: ROOT, stdio: 'pipe' }
|
|
123
|
+
);
|
|
124
|
+
} catch (e) {
|
|
125
|
+
const code = e && e.status;
|
|
126
|
+
if (!(typeof code === 'number' && code < 8)) throw e;
|
|
127
|
+
}
|
|
128
|
+
const info = path.join(dest, `BACKUP-INFO-v${next}.txt`);
|
|
129
|
+
fs.writeFileSync(info, `BEAST AGENT v${next} — ${new Date().toLocaleString('en-US')}\nSource: ${ROOT}\nGitHub: https://github.com/algokodcom/beast-agent/releases/tag/${tag}\nnpm: https://www.npmjs.com/package/beast-agent\n`);
|
|
130
|
+
ok(dest);
|
|
131
|
+
} catch (e) {
|
|
132
|
+
warn('OneDrive backup skipped: ' + String(e).slice(0, 160));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
console.log(`\n\x1b[1m\x1b[32m✓ v${next} release complete.\x1b[0m`);
|
|
136
|
+
console.log(' GitHub : https://github.com/algokodcom/beast-agent/releases/tag/' + tag);
|
|
137
|
+
console.log(' npm : https://www.npmjs.com/package/beast-agent');
|
package/src/agent/engine.js
CHANGED
|
@@ -1473,7 +1473,7 @@ class Engine {
|
|
|
1473
1473
|
delete s.notes;
|
|
1474
1474
|
delete s.notesAt;
|
|
1475
1475
|
delete s.summary; // özet de sıfırlanır — taze başlangıç
|
|
1476
|
-
this.todos.set(sid, []);
|
|
1476
|
+
this.todos.set(sid, s.todos || []);
|
|
1477
1477
|
this.emit({ type: 'sessions' });
|
|
1478
1478
|
return true;
|
|
1479
1479
|
}
|
|
@@ -4140,7 +4140,7 @@ class Engine {
|
|
|
4140
4140
|
/* GÖREV LİSTESİ DİSİPLİNİ: ajan işi bitti sanıyor ama listede hâlâ
|
|
4141
4141
|
bekleyen/aktif madde varsa BİR KEZ hatırlat ve tura devam et —
|
|
4142
4142
|
liste ya tamamlanmalı ya kalan maddeler listeden düşürülmeli. */
|
|
4143
|
-
const todos =
|
|
4143
|
+
const todos = this.todos.has(sid) ? this.todos.get(sid) || [] : session.todos || [];
|
|
4144
4144
|
const pending = todos.filter((t) => t && t.status !== 'done');
|
|
4145
4145
|
if (pending.length && !nudged) {
|
|
4146
4146
|
nudged = true;
|
|
@@ -5370,6 +5370,8 @@ const skills = require('./skills');
|
|
|
5370
5370
|
const items = sanitizeTodoItems(args.items);
|
|
5371
5371
|
this._tagTodoIds(sessionId, items); /* her madde kalıcı ID taşır — geri alma buna bağlanır */
|
|
5372
5372
|
this.todos.set(sessionId, items);
|
|
5373
|
+
const tSession = this.cache.get(String(sessionId));
|
|
5374
|
+
if (tSession) tSession.todos = items;
|
|
5373
5375
|
try {
|
|
5374
5376
|
fs.appendFileSync(
|
|
5375
5377
|
this._file(sessionId),
|
|
@@ -6550,6 +6552,8 @@ Engine.prototype.undoTodo = function (sid, todoId) {
|
|
|
6550
6552
|
if (t && t.id === id && t.status === 'done') t.status = 'pending';
|
|
6551
6553
|
}
|
|
6552
6554
|
this.todos.set(key, todos);
|
|
6555
|
+
const uSession = this.cache.get(key);
|
|
6556
|
+
if (uSession) uSession.todos = todos;
|
|
6553
6557
|
try {
|
|
6554
6558
|
fs.appendFileSync(this._file(key), JSON.stringify({ t: 'todo', items: todos }) + '\n');
|
|
6555
6559
|
} catch {}
|
|
@@ -6584,6 +6588,8 @@ Engine.prototype.clearTodos = function (sid) {
|
|
|
6584
6588
|
}
|
|
6585
6589
|
const prev = this.todos.get(key) || [];
|
|
6586
6590
|
this.todos.set(key, []);
|
|
6591
|
+
const cSession = this.cache.get(key);
|
|
6592
|
+
if (cSession) cSession.todos = [];
|
|
6587
6593
|
try {
|
|
6588
6594
|
fs.appendFileSync(this._file(key), JSON.stringify({ t: 'todo', items: [] }) + '\n');
|
|
6589
6595
|
} catch {}
|
package/src/agent/searxng.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
/* SearXNG —
|
|
4
|
-
SearXNG
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
/* SearXNG — local metasearch integration (github.com/searxng/searxng)
|
|
4
|
+
SearXNG officially targets Linux/Docker; on Windows it crashes because of
|
|
5
|
+
`import pwd`. This module installs the real searxng (GitHub master) into
|
|
6
|
+
Beast's EMBEDDED Python + writes a pwd shim + runs it in the background on
|
|
7
|
+
127.0.0.1:8888. The search chain only uses the 'searxng' engine while it is UP.
|
|
8
8
|
|
|
9
|
-
CLI: beast searxng →
|
|
10
|
-
beast searxng status →
|
|
11
|
-
beast searxng stop →
|
|
9
|
+
CLI: beast searxng → install if needed + start
|
|
10
|
+
beast searxng status → status
|
|
11
|
+
beast searxng stop → stop */
|
|
12
12
|
|
|
13
13
|
const fs = require('fs');
|
|
14
14
|
const path = require('path');
|
|
@@ -20,7 +20,7 @@ const PKG_URL = 'https://github.com/searxng/searxng/archive/refs/heads/master.zi
|
|
|
20
20
|
const GETPIP_URL = 'https://bootstrap.pypa.io/get-pip.py';
|
|
21
21
|
const DEFAULT_URL = 'http://127.0.0.1:8888';
|
|
22
22
|
|
|
23
|
-
/* ----------
|
|
23
|
+
/* ---------- paths ---------- */
|
|
24
24
|
|
|
25
25
|
function beastAppDir() {
|
|
26
26
|
if (process.env.BEAST_DATA) return process.env.BEAST_DATA;
|
|
@@ -40,7 +40,7 @@ function runPyPath() { return path.join(dir(), 'run.py'); }
|
|
|
40
40
|
function pidPath() { return path.join(dir(), 'server.pid'); }
|
|
41
41
|
function pyExe() { return path.join(beastAppDir(), 'py', 'python.exe'); }
|
|
42
42
|
|
|
43
|
-
/* ----------
|
|
43
|
+
/* ---------- status probe (cached) ---------- */
|
|
44
44
|
|
|
45
45
|
let _probe = { at: 0, up: false, url: '' };
|
|
46
46
|
const PROBE_UP_TTL = 5 * 60 * 1000;
|
|
@@ -64,11 +64,11 @@ async function isUp(baseUrl = DEFAULT_URL, { force = false } = {}) {
|
|
|
64
64
|
return up;
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
/* ----------
|
|
67
|
+
/* ---------- search (chain engine) ---------- */
|
|
68
68
|
|
|
69
69
|
async function search(query, { maxResults = 8, signal, baseUrl } = {}) {
|
|
70
70
|
const base = String(baseUrl || process.env.BEAST_SEARXNG_URL || DEFAULT_URL).replace(/\/+$/, '');
|
|
71
|
-
if (!(await isUp(base))) return null; //
|
|
71
|
+
if (!(await isUp(base))) return null; // not up → the chain moves to the next engine
|
|
72
72
|
try {
|
|
73
73
|
const sig =
|
|
74
74
|
signal && typeof AbortSignal !== 'undefined' && AbortSignal.any
|
|
@@ -100,7 +100,7 @@ async function search(query, { maxResults = 8, signal, baseUrl } = {}) {
|
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
-
/* ----------
|
|
103
|
+
/* ---------- installation ---------- */
|
|
104
104
|
|
|
105
105
|
function sh(exe, args, timeoutMs = 600000) {
|
|
106
106
|
return new Promise((resolve, reject) => {
|
|
@@ -116,7 +116,7 @@ function sh(exe, args, timeoutMs = 600000) {
|
|
|
116
116
|
|
|
117
117
|
async function downloadTo(url, dest) {
|
|
118
118
|
const r = await fetch(url, { signal: AbortSignal.timeout(120000) });
|
|
119
|
-
if (!r.ok) throw new Error(`
|
|
119
|
+
if (!r.ok) throw new Error(`download failed: HTTP ${r.status}`);
|
|
120
120
|
fs.writeFileSync(dest, Buffer.from(await r.arrayBuffer()));
|
|
121
121
|
}
|
|
122
122
|
|
|
@@ -124,7 +124,7 @@ function sitePackagesDir(py) {
|
|
|
124
124
|
return sh(py, ['-c', 'import sysconfig;print(sysconfig.get_paths()["purelib"])']).then((s) => s.trim());
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
-
/*
|
|
127
|
+
/* if `import site` is not enabled in the embedded python's ._pth file, pip/packages are invisible */
|
|
128
128
|
function fixEmbeddedPth(py) {
|
|
129
129
|
try {
|
|
130
130
|
const dirPy = path.dirname(py);
|
|
@@ -145,14 +145,14 @@ async function ensurePip(py, onLog) {
|
|
|
145
145
|
await sh(py, ['-m', 'pip', '--version'], 60000);
|
|
146
146
|
return;
|
|
147
147
|
} catch {}
|
|
148
|
-
onLog('pip
|
|
148
|
+
onLog('installing pip…');
|
|
149
149
|
const gp = path.join(os.tmpdir(), 'beast-get-pip.py');
|
|
150
150
|
await downloadTo(GETPIP_URL, gp);
|
|
151
151
|
await sh(py, [gp, '--no-warn-script-location'], 300000);
|
|
152
152
|
fixEmbeddedPth(py);
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
-
/**
|
|
155
|
+
/** Full installation — what the `beast searxng` command does. */
|
|
156
156
|
async function install({ onLog } = {}) {
|
|
157
157
|
const log = (m) => { try { (onLog || ((s) => console.log(' • ' + s)))(m); } catch {} };
|
|
158
158
|
const tools = require('./tools');
|
|
@@ -161,22 +161,22 @@ async function install({ onLog } = {}) {
|
|
|
161
161
|
log('python: ' + py);
|
|
162
162
|
|
|
163
163
|
await ensurePip(py, log);
|
|
164
|
-
log('setuptools/wheel/tzdata
|
|
164
|
+
log('installing setuptools/wheel/tzdata…');
|
|
165
165
|
await sh(py, ['-m', 'pip', 'install', '--no-warn-script-location', 'setuptools', 'wheel', 'tzdata'], 600000);
|
|
166
166
|
|
|
167
|
-
log('searxng
|
|
167
|
+
log('downloading searxng dependencies (~40 MB)…');
|
|
168
168
|
const reqs = path.join(os.tmpdir(), 'beast-searxng-requirements.txt');
|
|
169
169
|
await downloadTo(REQS_URL, reqs);
|
|
170
170
|
await sh(py, ['-m', 'pip', 'install', '--no-warn-script-location', '-r', reqs], 900000);
|
|
171
171
|
|
|
172
|
-
log('searxng
|
|
172
|
+
log('installing the searxng engine…');
|
|
173
173
|
await sh(py, ['-m', 'pip', 'install', '--no-warn-script-location', '--no-build-isolation', PKG_URL], 900000);
|
|
174
174
|
|
|
175
|
-
/* pwd shim — searxng/valkeydb.py
|
|
175
|
+
/* pwd shim — searxng/valkeydb.py imports pwd for unix socket ownership */
|
|
176
176
|
const sp = (await sitePackagesDir(py)) || path.join(path.dirname(py), 'Lib', 'site-packages');
|
|
177
177
|
const shim = [
|
|
178
|
-
'"""Windows shim: SearXNG valkeydb.py
|
|
179
|
-
'
|
|
178
|
+
'"""Windows shim: SearXNG valkeydb.py only uses pwd for unix-socket ownership;',
|
|
179
|
+
'valkey is disabled on Windows, so a stub response is sufficient."""',
|
|
180
180
|
'import os',
|
|
181
181
|
'',
|
|
182
182
|
'class _Pw:',
|
|
@@ -203,7 +203,7 @@ async function install({ onLog } = {}) {
|
|
|
203
203
|
].join('\n');
|
|
204
204
|
fs.writeFileSync(path.join(sp, 'pwd.py'), shim, 'utf8');
|
|
205
205
|
|
|
206
|
-
/*
|
|
206
|
+
/* settings + runner */
|
|
207
207
|
const secret = 'beast-' + Math.random().toString(36).slice(2, 14);
|
|
208
208
|
fs.writeFileSync(
|
|
209
209
|
settingsPath(),
|
|
@@ -225,11 +225,11 @@ async function install({ onLog } = {}) {
|
|
|
225
225
|
);
|
|
226
226
|
fs.writeFileSync(runPyPath(), 'from searx.webapp import run; run()', 'utf8');
|
|
227
227
|
resetProbeCache();
|
|
228
|
-
log('
|
|
228
|
+
log('installation complete');
|
|
229
229
|
return { ok: true };
|
|
230
230
|
}
|
|
231
231
|
|
|
232
|
-
/* ----------
|
|
232
|
+
/* ---------- start / stop / status ---------- */
|
|
233
233
|
|
|
234
234
|
function readPid() {
|
|
235
235
|
try {
|
|
@@ -243,7 +243,7 @@ function readPid() {
|
|
|
243
243
|
async function start({ installIfMissing = true, onLog } = {}) {
|
|
244
244
|
if (await isUp(DEFAULT_URL, { force: true })) return { ok: true, already: true, url: DEFAULT_URL };
|
|
245
245
|
if (!fs.existsSync(settingsPath()) || !fs.existsSync(runPyPath())) {
|
|
246
|
-
if (!installIfMissing) return { ok: false, error: '
|
|
246
|
+
if (!installIfMissing) return { ok: false, error: 'not installed — first run: beast searxng' };
|
|
247
247
|
await install({ onLog });
|
|
248
248
|
}
|
|
249
249
|
const tools = require('./tools');
|
|
@@ -256,17 +256,17 @@ async function start({ installIfMissing = true, onLog } = {}) {
|
|
|
256
256
|
});
|
|
257
257
|
try { fs.writeFileSync(pidPath(), String(child.pid)); } catch {}
|
|
258
258
|
child.unref();
|
|
259
|
-
/*
|
|
259
|
+
/* wait for it to come up (the first boot compiles the engine list — a few seconds) */
|
|
260
260
|
for (let i = 0; i < 20; i++) {
|
|
261
261
|
await new Promise((r) => setTimeout(r, 1000));
|
|
262
262
|
if (await isUp(DEFAULT_URL, { force: true })) return { ok: true, url: DEFAULT_URL, pid: child.pid };
|
|
263
263
|
}
|
|
264
|
-
return { ok: false, error: 'searxng
|
|
264
|
+
return { ok: false, error: 'searxng failed to start (no response within 20s)' };
|
|
265
265
|
}
|
|
266
266
|
|
|
267
267
|
function stop() {
|
|
268
268
|
const pid = readPid();
|
|
269
|
-
if (!pid) return { ok: true, note: '
|
|
269
|
+
if (!pid) return { ok: true, note: 'not running' };
|
|
270
270
|
try {
|
|
271
271
|
execSync(`taskkill /PID ${pid} /T /F`, { stdio: 'ignore' });
|
|
272
272
|
} catch {}
|
|
@@ -288,26 +288,26 @@ async function status() {
|
|
|
288
288
|
|
|
289
289
|
async function cli(argv) {
|
|
290
290
|
const sub = String(argv[0] || 'start').toLowerCase();
|
|
291
|
-
console.log('\u27F3 SearXNG (
|
|
291
|
+
console.log('\u27F3 SearXNG (local search engine)');
|
|
292
292
|
if (sub === 'stop') {
|
|
293
293
|
const r = stop();
|
|
294
|
-
console.log(r.ok ? '\u2713
|
|
294
|
+
console.log(r.ok ? '\u2713 stopped' : '\u2717 could not stop');
|
|
295
295
|
process.exit(r.ok ? 0 : 1);
|
|
296
296
|
}
|
|
297
297
|
if (sub === 'status') {
|
|
298
298
|
const s = await status();
|
|
299
|
-
console.log(`
|
|
300
|
-
console.log(`
|
|
299
|
+
console.log(` installed: ${s.installed ? 'yes' : 'no'}`);
|
|
300
|
+
console.log(` running : ${s.up ? 'yes (' + s.url + ')' : 'no'}`);
|
|
301
301
|
process.exit(0);
|
|
302
302
|
}
|
|
303
303
|
/* start */
|
|
304
304
|
const r = await start({ installIfMissing: true, onLog: (m) => console.log(' \u2022 ' + m) });
|
|
305
305
|
if (r.ok) {
|
|
306
|
-
console.log(r.already ? '\u2713 SearXNG
|
|
307
|
-
console.log('\u2139
|
|
306
|
+
console.log(r.already ? '\u2713 SearXNG is already running — ' + r.url : '\u2713 SearXNG started — ' + r.url);
|
|
307
|
+
console.log('\u2139 ready to use in the search chain (Settings → Web Search order)');
|
|
308
308
|
process.exit(0);
|
|
309
309
|
}
|
|
310
|
-
console.error('\u2717 ' + (r.error || '
|
|
310
|
+
console.error('\u2717 ' + (r.error || 'failed to start'));
|
|
311
311
|
process.exit(1);
|
|
312
312
|
}
|
|
313
313
|
|
package/src/main.js
CHANGED
|
@@ -5104,7 +5104,12 @@ function resyncBrowserUi() {
|
|
|
5104
5104
|
setTimeout(() => { try { layoutBrowser(); resyncBrowserUi(); } catch {} }, 80);
|
|
5105
5105
|
setTimeout(() => { try { layoutBrowser(); resyncBrowserUi(); } catch {} }, 300);
|
|
5106
5106
|
});
|
|
5107
|
-
win.on('show', layoutBrowser);
|
|
5107
|
+
win.on('show', () => { layoutBrowser(); resyncBrowserUi(); });
|
|
5108
|
+
/* renderer her yeni yüklemede (açılış, reload, kurtarma) tarayıcı durumunu
|
|
5109
|
+
TAZE alır — açılışta kaçan 'browser' olayı dock'un ayrılmamasına yol açıyordu */
|
|
5110
|
+
win.webContents.on('did-finish-load', () => {
|
|
5111
|
+
try { resyncBrowserUi(); } catch {}
|
|
5112
|
+
});
|
|
5108
5113
|
// X'e basınca gizle — tepside yaşamaya devam, WhatsApp bağlantısı sürer
|
|
5109
5114
|
win.on('close', (e) => {
|
|
5110
5115
|
if (!app.isQuitting) {
|
|
@@ -7594,6 +7599,23 @@ ipcMain.handle('browser:toggle', () => {
|
|
|
7594
7599
|
/* göz ikonu: ajan tarayıcısını görünür/gizli yap — yalnızca gizleme özelliği
|
|
7595
7600
|
(Ayarlar → Web Arama) açıkken etkilidir; özellik kapalıysa hep görünür */
|
|
7596
7601
|
ipcMain.handle('browser:shown:get', () => ({ shown: !browserHeadlessPref(), enabled: browserHideEnabled() }));
|
|
7602
|
+
/* RENDERER AÇILIŞ SENKRONU: tarayıcı, renderer olayları dinlemeye başlamadan
|
|
7603
|
+
açılmış olabilir (açılışta oturum kurtarma/arka plan işi) — durum buradan
|
|
7604
|
+
PULL edilir; yoksa native view dock alanı ayrılmadan chat'in üzerinde kalır */
|
|
7605
|
+
ipcMain.handle('browser:state:get', () => {
|
|
7606
|
+
let url = '';
|
|
7607
|
+
try { url = browser.view && browser.open && !browser.view.webContents.isDestroyed() ? browser.view.webContents.getURL() : ''; } catch {}
|
|
7608
|
+
return {
|
|
7609
|
+
open: browser.open,
|
|
7610
|
+
visible: browser.visible,
|
|
7611
|
+
width: browserShownWidth(browserW()),
|
|
7612
|
+
phone: browser.phone,
|
|
7613
|
+
mobile: browser.mobile,
|
|
7614
|
+
phoneRect: browser.mobileRect,
|
|
7615
|
+
device: browser.deviceKey,
|
|
7616
|
+
url,
|
|
7617
|
+
};
|
|
7618
|
+
});
|
|
7597
7619
|
ipcMain.handle('browser:shown:set', (_e, v) => {
|
|
7598
7620
|
if (!browserHideEnabled()) return { shown: true, enabled: false };
|
|
7599
7621
|
settings.browserHeadless = !v;
|
package/src/preload.js
CHANGED
|
@@ -84,6 +84,7 @@ contextBridge.exposeInMainWorld('beast', {
|
|
|
84
84
|
browserShownGet: () => ipcRenderer.invoke('browser:shown:get'),
|
|
85
85
|
browserShownSet: (v) => ipcRenderer.invoke('browser:shown:set', v),
|
|
86
86
|
browserHideSet: (v) => ipcRenderer.invoke('browser:hide:set', v),
|
|
87
|
+
browserStateGet: () => ipcRenderer.invoke('browser:state:get'),
|
|
87
88
|
terminalToggle: () => ipcRenderer.invoke('terminal:toggle'),
|
|
88
89
|
terminalRun: (cmd, shell) => ipcRenderer.invoke('terminal:run', { cmd, shell }),
|
|
89
90
|
terminalStop: () => ipcRenderer.invoke('terminal:stop'),
|
package/src/renderer/renderer.js
CHANGED
|
@@ -5741,6 +5741,43 @@ function renderAgentsPane() {
|
|
|
5741
5741
|
|
|
5742
5742
|
/* ---------------- events from engine ---------------- */
|
|
5743
5743
|
|
|
5744
|
+
/* Tarayıcı panel durumu: canlı 'browser' olayı + açılış senkronu (browserStateGet)
|
|
5745
|
+
AYNI yolu kullanır — native view ile dock alanı (--bw) asla ayrışmasın */
|
|
5746
|
+
function applyBrowserState(ev) {
|
|
5747
|
+
/* visible=false → ajan tarayıcıyı GİZLİ kullanıyor: UI yer açmaz */
|
|
5748
|
+
const shown = !!ev.open && ev.visible !== false;
|
|
5749
|
+
const wasShown = document.body.classList.contains('browser-open');
|
|
5750
|
+
document.body.classList.toggle('browser-open', shown);
|
|
5751
|
+
if (shown && ev.width) document.body.style.setProperty('--bw', ev.width + 'px');
|
|
5752
|
+
document.body.classList.toggle('phone-mode', !!ev.phone);
|
|
5753
|
+
if (els.bbPhone) els.bbPhone.classList.toggle('on', !!ev.phone);
|
|
5754
|
+
els.browserBar.hidden = !shown;
|
|
5755
|
+
els.bbResize.hidden = !shown;
|
|
5756
|
+
/* terminal artık ALT dock — tarayıcıyla birlikte yaşar, kapatılmaz */
|
|
5757
|
+
/* #19 tarayıcı açılınca paralel ajan konsolu (sağ panel) yerini bırakır;
|
|
5758
|
+
kapanınca önceki durumuna döner — istenirse railBtn ile elle açılır.
|
|
5759
|
+
GİZLİ ajan gezinmeleri (shown=false, wasShown=false) rail'e DOKUNMAZ —
|
|
5760
|
+
aksi halde paralel ajan konsolu kendi kendine kapanırdı */
|
|
5761
|
+
if (shown) {
|
|
5762
|
+
railPrefBeforeBrowser = !document.body.classList.contains('rail-hidden');
|
|
5763
|
+
toggleRail(true);
|
|
5764
|
+
} else if (wasShown) {
|
|
5765
|
+
toggleRail(!railPrefBeforeBrowser);
|
|
5766
|
+
}
|
|
5767
|
+
/* IDE modunda tarayıcı açılır/kapanır/genişlik değişirse row yeniden bölünür —
|
|
5768
|
+
editör + Beast Code ORTAK kırpılıp ORTAK açılır (kullanıcı payı korunur) */
|
|
5769
|
+
if (ideModeOn()) ideSplitApplyFrac();
|
|
5770
|
+
if (sbModeOn()) sbSplitApplyFrac();
|
|
5771
|
+
if (shown) {
|
|
5772
|
+
els.browserBtn.classList.add('on');
|
|
5773
|
+
if (ev.url && document.activeElement !== els.bbUrl) {
|
|
5774
|
+
els.bbUrl.value = ev.url.startsWith('https://duckduckgo.com/?q=') ? decodeURIComponent(ev.url.split('q=')[1] || '') : ev.url;
|
|
5775
|
+
}
|
|
5776
|
+
} else {
|
|
5777
|
+
els.browserBtn.classList.remove('on');
|
|
5778
|
+
}
|
|
5779
|
+
}
|
|
5780
|
+
|
|
5744
5781
|
function onEvent(ev) {
|
|
5745
5782
|
if (ev.type === 'notify-click') {
|
|
5746
5783
|
/* toast tıklandı: ilgili panele atla */
|
|
@@ -5991,41 +6028,9 @@ function onEvent(ev) {
|
|
|
5991
6028
|
soldaki klasör paneli ELLE yenilemeden canlı tazelenir */
|
|
5992
6029
|
ideRefreshTree();
|
|
5993
6030
|
break;
|
|
5994
|
-
case 'browser':
|
|
5995
|
-
|
|
5996
|
-
const shown = !!ev.open && ev.visible !== false;
|
|
5997
|
-
const wasShown = document.body.classList.contains('browser-open');
|
|
5998
|
-
document.body.classList.toggle('browser-open', shown);
|
|
5999
|
-
if (shown && ev.width) document.body.style.setProperty('--bw', ev.width + 'px');
|
|
6000
|
-
document.body.classList.toggle('phone-mode', !!ev.phone);
|
|
6001
|
-
if (els.bbPhone) els.bbPhone.classList.toggle('on', !!ev.phone);
|
|
6002
|
-
els.browserBar.hidden = !shown;
|
|
6003
|
-
els.bbResize.hidden = !shown;
|
|
6004
|
-
/* terminal artık ALT dock — tarayıcıyla birlikte yaşar, kapatılmaz */
|
|
6005
|
-
/* #19 tarayıcı açılınca paralel ajan konsolu (sağ panel) yerini bırakır;
|
|
6006
|
-
kapanınca önceki durumuna döner — istenirse railBtn ile elle açılır.
|
|
6007
|
-
GİZLİ ajan gezinmeleri (shown=false, wasShown=false) rail'e DOKUNMAZ —
|
|
6008
|
-
aksi halde paralel ajan konsolu kendi kendine kapanırdı */
|
|
6009
|
-
if (shown) {
|
|
6010
|
-
railPrefBeforeBrowser = !document.body.classList.contains('rail-hidden');
|
|
6011
|
-
toggleRail(true);
|
|
6012
|
-
} else if (wasShown) {
|
|
6013
|
-
toggleRail(!railPrefBeforeBrowser);
|
|
6014
|
-
}
|
|
6015
|
-
/* IDE modunda tarayıcı açılır/kapanır/genişlik değişirse row yeniden bölünür —
|
|
6016
|
-
editör + Beast Code ORTAK kırpılıp ORTAK açılır (kullanıcı payı korunur) */
|
|
6017
|
-
if (ideModeOn()) ideSplitApplyFrac();
|
|
6018
|
-
if (sbModeOn()) sbSplitApplyFrac();
|
|
6019
|
-
if (shown) {
|
|
6020
|
-
els.browserBtn.classList.add('on');
|
|
6021
|
-
if (ev.url && document.activeElement !== els.bbUrl) {
|
|
6022
|
-
els.bbUrl.value = ev.url.startsWith('https://duckduckgo.com/?q=') ? decodeURIComponent(ev.url.split('q=')[1] || '') : ev.url;
|
|
6023
|
-
}
|
|
6024
|
-
} else {
|
|
6025
|
-
els.browserBtn.classList.remove('on');
|
|
6026
|
-
}
|
|
6031
|
+
case 'browser':
|
|
6032
|
+
applyBrowserState(ev);
|
|
6027
6033
|
break;
|
|
6028
|
-
}
|
|
6029
6034
|
case 'status':
|
|
6030
6035
|
/* kuyruktan tetiklenen yeni iş: kilitle kendiliğinden açılmışsa geri kapat */
|
|
6031
6036
|
if (ev.status !== 'idle' && !busy) setBusy(true);
|
|
@@ -7046,6 +7051,14 @@ async function init() {
|
|
|
7046
7051
|
beast.onEvent(onEvent);
|
|
7047
7052
|
toggleRail(true); /* Paralel Ajanlar paneli varsayılan KAPALI — railBtn ile açılır */
|
|
7048
7053
|
|
|
7054
|
+
/* AÇILIŞ SENKRONU: tarayıcı renderer hazır olmadan açılmışsa (oturum kurtarma/
|
|
7055
|
+
arka plan işi) 'browser' olayı kaybolurdu — durum PULL edilir ki dock alanı
|
|
7056
|
+
(--bw) ayrılsın ve native view chat'in üzerine binmesin */
|
|
7057
|
+
try {
|
|
7058
|
+
const bst = await beast.browserStateGet();
|
|
7059
|
+
if (bst) applyBrowserState(bst);
|
|
7060
|
+
} catch {}
|
|
7061
|
+
|
|
7049
7062
|
state = await beast.getState();
|
|
7050
7063
|
try {
|
|
7051
7064
|
const s = await beast.getSettings();
|